From f5c7061257df0640ea6e10e2b7f85de416c84cf9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 09:32:04 -0600 Subject: [PATCH 01/78] Move workspace serialization specs to workspace-spec --- spec/workspace-spec.coffee | 58 ++++++++++++++++++++ spec/workspace-view-spec.coffee | 95 --------------------------------- 2 files changed, 58 insertions(+), 95 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index ea4566bd0..836f17f13 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -17,6 +17,64 @@ describe "Workspace", -> atom.workspace = workspace = new Workspace waits(1) + describe "serialization", -> + simulateReload = -> + workspaceState = atom.workspace.serialize() + projectState = atom.project.serialize() + atom.workspace.destroy() + atom.project.destroy() + atom.project = atom.deserializers.deserialize(projectState) + atom.workspace = Workspace.deserialize(workspaceState) + + describe "when the workspace contains text editors", -> + it "constructs the view with the same panes", -> + pane1 = atom.workspace.getActivePane() + pane2 = pane1.splitRight(copyActiveItem: true) + pane3 = pane2.splitRight(copyActiveItem: true) + pane4 = null + + waitsForPromise -> + atom.workspace.open('b').then (editor) -> + pane2.activateItem(editor.copy()) + + waitsForPromise -> + atom.workspace.open('../sample.js').then (editor) -> + pane3.activateItem(editor) + + runs -> + pane3.activeItem.setCursorScreenPosition([2, 4]) + pane4 = pane2.splitDown() + + waitsForPromise -> + atom.workspace.open('../sample.txt').then (editor) -> + pane4.activateItem(editor) + + runs -> + pane4.getActiveItem().setCursorScreenPosition([0, 2]) + pane2.activate() + + simulateReload() + + expect(atom.workspace.getTextEditors().length).toBe 4 + [editor1, editor2, editor3, editor4] = atom.workspace.getTextEditors() + + expect(editor1.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b') + expect(editor2.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.txt') + expect(editor2.getCursorScreenPosition()).toEqual [0, 2] + expect(editor3.getPath()).toBe atom.project.getDirectories()[0]?.resolve('b') + expect(editor4.getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.js') + expect(editor4.getCursorScreenPosition()).toEqual [2, 4] + + expect(atom.workspace.getActiveTextEditor().getPath()).toBe editor3.getPath() + expect(document.title).toBe "#{path.basename(editor3.getPath())} - #{atom.project.getPaths()[0]} - Atom" + + describe "where there are no open panes or editors", -> + it "constructs the view with no open editors", -> + atom.workspace.getActivePane().destroy() + expect(atom.workspace.getTextEditors().length).toBe 0 + simulateReload() + expect(atom.workspace.getTextEditors().length).toBe 0 + describe "::open(uri, options)", -> openEvents = null diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index a160cdb39..b667b04db 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,101 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "@deserialize()", -> - viewState = null - - simulateReload = -> - workspaceState = atom.workspace.serialize() - projectState = atom.project.serialize() - atom.workspaceView.remove() - atom.project = atom.deserializers.deserialize(projectState) - atom.workspace = Workspace.deserialize(workspaceState) - atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView - atom.workspaceView.attachToDom() - - describe "when the serialized WorkspaceView has an unsaved buffer", -> - it "constructs the view with the same panes", -> - atom.workspaceView.attachToDom() - - waitsForPromise -> - atom.workspace.open() - - runs -> - editorView1 = atom.workspaceView.getActiveView() - buffer = editorView1.getEditor().getBuffer() - editorView1.getPaneView().getModel().splitRight(copyActiveItem: true) - expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1] - - simulateReload() - - expect(atom.workspaceView.getEditorViews().length).toBe 2 - expect(atom.workspaceView.getActivePaneView()).toBe atom.workspaceView.getPaneViews()[1] - expect(document.title).toBe "untitled - #{atom.project.getPaths()[0]} - Atom" - - describe "when there are open editors", -> - it "constructs the view with the same panes", -> - atom.workspaceView.attachToDom() - pane1 = atom.workspaceView.getActivePaneView() - pane2 = pane1.splitRight() - pane3 = pane2.splitRight() - pane4 = null - - waitsForPromise -> - atom.workspace.open('b').then (editor) -> - pane2.activateItem(editor.copy()) - - waitsForPromise -> - atom.workspace.open('../sample.js').then (editor) -> - pane3.activateItem(editor) - - runs -> - pane3.activeItem.setCursorScreenPosition([2, 4]) - pane4 = pane2.splitDown() - - waitsForPromise -> - atom.workspace.open('../sample.txt').then (editor) -> - pane4.activateItem(editor) - - runs -> - pane4.activeItem.setCursorScreenPosition([0, 2]) - pane2.focus() - - simulateReload() - - expect(atom.workspaceView.getEditorViews().length).toBe 4 - editorView1 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane atom-text-editor:eq(0)').view() - editorView3 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane atom-text-editor:eq(1)').view() - editorView2 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane atom-text-editor:eq(0)').view() - editorView4 = atom.workspaceView.panes.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane atom-text-editor:eq(1)').view() - - expect(editorView1.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('a') - expect(editorView2.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('b') - expect(editorView3.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.js') - expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4] - expect(editorView4.getEditor().getPath()).toBe atom.project.getDirectories()[0]?.resolve('../sample.txt') - expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2] - - # ensure adjust pane dimensions is called - expect(editorView1.width()).toBeGreaterThan 0 - expect(editorView2.width()).toBeGreaterThan 0 - expect(editorView3.width()).toBeGreaterThan 0 - expect(editorView4.width()).toBeGreaterThan 0 - - # ensure correct editorView is focused again - expect(editorView2).toHaveFocus() - expect(editorView1).not.toHaveFocus() - expect(editorView3).not.toHaveFocus() - expect(editorView4).not.toHaveFocus() - - expect(document.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPaths()[0]} - Atom" - - describe "where there are no open editors", -> - it "constructs the view with no open editors", -> - atom.workspaceView.getActivePaneView().remove() - expect(atom.workspaceView.getEditorViews().length).toBe 0 - simulateReload() - expect(atom.workspaceView.getEditorViews().length).toBe 0 - describe "focus", -> beforeEach -> atom.workspaceView.attachToDom() From e6c7ce215b4427db411f33cd751a043293429eb8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 09:55:21 -0600 Subject: [PATCH 02/78] Move keymap wiring specs from workspace-view-spec to window-spec --- spec/window-spec.coffee | 14 ++++++++++++++ spec/workspace-view-spec.coffee | 11 ----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/spec/window-spec.coffee b/spec/window-spec.coffee index 94e605a36..d56d087b1 100644 --- a/spec/window-spec.coffee +++ b/spec/window-spec.coffee @@ -1,4 +1,5 @@ {$, $$} = require '../src/space-pen-extensions' +KeymapManager = require 'atom-keymap' path = require 'path' fs = require 'fs-plus' temp = require 'temp' @@ -304,3 +305,16 @@ describe "Window", -> runs -> expect(atom.project.getPaths()[0]).toBe pathToOpen + + describe "when keydown events occur on the document", -> + it "dispatches the event via the KeymapManager and CommandRegistry", -> + dispatchedCommands = [] + atom.commands.onWillDispatch (command) -> dispatchedCommands.push(command) + atom.commands.add '*', 'foo-command': -> + atom.keymaps.add 'source-name', '*': {'x': 'foo-command'} + + event = KeymapManager.buildKeydownEvent('x', target: document.createElement('div')) + document.dispatchEvent(event) + + expect(dispatchedCommands.length).toBe 1 + expect(dispatchedCommands[0].type).toBe 'foo-command' diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index b667b04db..14d1e76e8 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -36,17 +36,6 @@ describe "WorkspaceView", -> atom.workspaceView.focus() expect(activePane).toHaveFocus() - describe "keymap wiring", -> - describe "when a keydown event is triggered in the WorkspaceView", -> - it "triggers matching keybindings for that event", -> - commandHandler = jasmine.createSpy('commandHandler') - atom.workspaceView.on('foo-command', commandHandler) - atom.keymaps.add('name', '*': {'x': 'foo-command'}) - event = keydownEvent 'x', target: atom.workspaceView[0] - - atom.workspaceView.trigger(event) - expect(commandHandler).toHaveBeenCalled() - describe "window:toggle-invisibles event", -> it "shows/hides invisibles in all open and future editors", -> atom.workspaceView.height(200) From 0ba313a6f297b3e61e37c49043d926ca8ed3010f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 09:59:53 -0600 Subject: [PATCH 03/78] Move workspace focus specs to workspace-element-spec --- spec/workspace-element-spec.coffee | 9 +++++++++ spec/workspace-view-spec.coffee | 11 ----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 1771b2363..eedf7b33a 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -8,6 +8,15 @@ describe "WorkspaceElement", -> beforeEach -> workspaceElement = atom.views.getView(atom.workspace) + describe "when the workspace element is focused", -> + it "transfers focus to the active pane", -> + jasmine.attachToDOM(workspaceElement) + activePaneElement = atom.views.getView(atom.workspace.getActivePane()) + document.body.focus() + expect(document.activeElement).not.toBe(activePaneElement) + workspaceElement.focus() + expect(document.activeElement).toBe(activePaneElement) + describe "the 'window:run-package-specs' command", -> it "runs the package specs for the active item's project path, or the first project path", -> spyOn(ipc, 'send') diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 14d1e76e8..0c6981566 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,17 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "focus", -> - beforeEach -> - atom.workspaceView.attachToDom() - - it "hands off focus to the active pane", -> - activePane = atom.workspaceView.getActivePaneView() - $('body').focus() - expect(activePane).not.toHaveFocus() - atom.workspaceView.focus() - expect(activePane).toHaveFocus() - describe "window:toggle-invisibles event", -> it "shows/hides invisibles in all open and future editors", -> atom.workspaceView.height(200) From d44f13a51e831c534ad514db8c00da06f93c1b6f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:02:13 -0600 Subject: [PATCH 04/78] Test window:toggle-invisibles in workspace-element-spec, more simply --- spec/workspace-element-spec.coffee | 9 +++++++++ spec/workspace-view-spec.coffee | 29 ----------------------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index eedf7b33a..24a220e1d 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -17,6 +17,15 @@ describe "WorkspaceElement", -> workspaceElement.focus() expect(document.activeElement).toBe(activePaneElement) + + describe "the 'window:toggle-invisibles' command", -> + it "shows/hides invisibles in all open and future editors", -> + expect(atom.config.get('editor.showInvisibles')).toBe false + atom.commands.dispatch(workspaceElement, 'window:toggle-invisibles') + expect(atom.config.get('editor.showInvisibles')).toBe true + atom.commands.dispatch(workspaceElement, 'window:toggle-invisibles') + expect(atom.config.get('editor.showInvisibles')).toBe false + describe "the 'window:run-package-specs' command", -> it "runs the package specs for the active item's project path, or the first project path", -> spyOn(ipc, 'send') diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 0c6981566..3284742ce 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,35 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "window:toggle-invisibles event", -> - it "shows/hides invisibles in all open and future editors", -> - atom.workspaceView.height(200) - atom.workspaceView.attachToDom() - rightEditorView = atom.workspaceView.getActiveView() - rightEditorView.getEditor().setText("\t \n") - rightEditorView.getPaneView().getModel().splitLeft(copyActiveItem: true) - leftEditorView = atom.workspaceView.getActiveView() - expect(rightEditorView.find(".line:first").text()).toBe " " - expect(leftEditorView.find(".line:first").text()).toBe " " - - {space, tab, eol} = atom.config.get('editor.invisibles') - withInvisiblesShowing = "#{tab} #{space}#{space}#{eol}" - - atom.workspaceView.trigger "window:toggle-invisibles" - expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing - expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing - - leftEditorView.getPaneView().getModel().splitDown(copyActiveItem: true) - lowerLeftEditorView = atom.workspaceView.getActiveView() - expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing - - atom.workspaceView.trigger "window:toggle-invisibles" - expect(rightEditorView.find(".line:first").text()).toBe " " - expect(leftEditorView.find(".line:first").text()).toBe " " - - rightEditorView.getPaneView().getModel().splitDown(copyActiveItem: true) - lowerRightEditorView = atom.workspaceView.getActiveView() - expect(lowerRightEditorView.find(".line:first").text()).toBe " " describe ".eachEditorView(callback)", -> beforeEach -> From 7fc04501c90386b4978308c9bf44a0ef7533f99f Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:04:21 -0600 Subject: [PATCH 05/78] Remove specs for deprecated WorkspaceView::eachEditorView method --- spec/workspace-view-spec.coffee | 49 --------------------------------- 1 file changed, 49 deletions(-) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 3284742ce..aa05efad9 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,55 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - - describe ".eachEditorView(callback)", -> - beforeEach -> - atom.workspaceView.attachToDom() - - it "invokes the callback for existing editor", -> - count = 0 - callbackEditor = null - callback = (editor) -> - callbackEditor = editor - count++ - atom.workspaceView.eachEditorView(callback) - expect(count).toBe 1 - expect(callbackEditor).toBe atom.workspaceView.getActiveView() - - it "invokes the callback for new editor", -> - count = 0 - callbackEditor = null - callback = (editor) -> - callbackEditor = editor - count++ - - atom.workspaceView.eachEditorView(callback) - count = 0 - callbackEditor = null - atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) - expect(count).toBe 1 - expect(callbackEditor).toBe atom.workspaceView.getActiveView() - - it "does not invoke the callback for mini editors", -> - editorViewCreatedHandler = jasmine.createSpy('editorViewCreatedHandler') - atom.workspaceView.eachEditorView(editorViewCreatedHandler) - editorViewCreatedHandler.reset() - miniEditor = new TextEditorView(mini: true) - atom.workspaceView.append(miniEditor) - expect(editorViewCreatedHandler).not.toHaveBeenCalled() - - it "returns a subscription that can be disabled", -> - count = 0 - callback = (editor) -> count++ - - subscription = atom.workspaceView.eachEditorView(callback) - expect(count).toBe 1 - atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) - expect(count).toBe 2 - subscription.off() - atom.workspaceView.getActiveView().getPaneView().getModel().splitRight(copyActiveItem: true) - expect(count).toBe 2 - describe "core:close", -> it "closes the active pane item until all that remains is a single empty pane", -> atom.config.set('core.destroyEmptyPanes', true) From 117d1507ab0eabb4b897e57a7e80f8e357e89f83 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:17:24 -0600 Subject: [PATCH 06/78] =?UTF-8?q?Move=20=E2=80=98core:close=E2=80=99=20spe?= =?UTF-8?q?cs=20in=20workspace-view-spec=20to=20workspace-spec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/workspace-spec.coffee | 29 +++++++++++++++++++++++++++++ spec/workspace-view-spec.coffee | 20 -------------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 836f17f13..57fba6401 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -1325,3 +1325,32 @@ describe "Workspace", -> save = -> atom.workspace.saveActivePaneItem() expect(save).toThrow() + + describe "::destroyActivePaneItemOrEmptyPane", -> + beforeEach -> + waitsForPromise -> atom.workspace.open() + + it "closes the active pane item until all that remains is a single empty pane", -> + atom.config.set('core.destroyEmptyPanes', false) + + pane1 = atom.workspace.getActivePane() + pane2 = pane1.splitRight(copyActiveItem: true) + + expect(atom.workspace.getPanes().length).toBe 2 + expect(pane2.getItems().length).toBe 1 + atom.workspace.destroyActivePaneItemOrEmptyPane() + + expect(atom.workspace.getPanes().length).toBe 2 + expect(pane2.getItems().length).toBe 0 + + atom.workspace.destroyActivePaneItemOrEmptyPane() + + expect(atom.workspace.getPanes().length).toBe 1 + expect(pane1.getItems().length).toBe 1 + + atom.workspace.destroyActivePaneItemOrEmptyPane() + expect(atom.workspace.getPanes().length).toBe 1 + expect(pane1.getItems().length).toBe 0 + + atom.workspace.destroyActivePaneItemOrEmptyPane() + expect(atom.workspace.getPanes().length).toBe 1 diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index aa05efad9..e61468e83 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,26 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "core:close", -> - it "closes the active pane item until all that remains is a single empty pane", -> - atom.config.set('core.destroyEmptyPanes', true) - - paneView1 = atom.workspaceView.getActivePaneView() - editorView = atom.workspaceView.getActiveView() - editorView.getPaneView().getModel().splitRight(copyActiveItem: true) - paneView2 = atom.workspaceView.getActivePaneView() - - expect(paneView1).not.toBe paneView2 - expect(atom.workspaceView.getPaneViews()).toHaveLength 2 - atom.workspaceView.trigger('core:close') - - expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 1 - expect(atom.workspaceView.getPaneViews()).toHaveLength 1 - atom.workspaceView.trigger('core:close') - - expect(atom.workspaceView.getActivePaneView().getItems()).toHaveLength 0 - expect(atom.workspaceView.getPaneViews()).toHaveLength 1 - describe "the scrollbar visibility class", -> it "has a class based on the style of the scrollbar", -> style = 'legacy' From d3b6454037bd4cc87adae75e703c98550b19f087 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:31:14 -0600 Subject: [PATCH 07/78] Move scrollbar style observation specs to workspace-element-spec --- spec/workspace-element-spec.coffee | 20 +++++++++++++++----- spec/workspace-view-spec.coffee | 13 ------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 24a220e1d..5f16b6835 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -3,13 +3,9 @@ path = require 'path' temp = require('temp').track() describe "WorkspaceElement", -> - workspaceElement = null - - beforeEach -> - workspaceElement = atom.views.getView(atom.workspace) - describe "when the workspace element is focused", -> it "transfers focus to the active pane", -> + workspaceElement = atom.views.getView(atom.workspace) jasmine.attachToDOM(workspaceElement) activePaneElement = atom.views.getView(atom.workspace.getActivePane()) document.body.focus() @@ -17,9 +13,22 @@ describe "WorkspaceElement", -> workspaceElement.focus() expect(document.activeElement).toBe(activePaneElement) + describe "the scrollbar visibility class", -> + it "has a class based on the style of the scrollbar", -> + observeCallback = null + scrollbarStyle = require 'scrollbar-style' + spyOn(scrollbarStyle, 'observePreferredScrollbarStyle').andCallFake (cb) -> observeCallback = cb + workspaceElement = atom.views.getView(atom.workspace) + + observeCallback('legacy') + expect(workspaceElement.className).toMatch 'scrollbars-visible-always' + + observeCallback('overlay') + expect(workspaceElement).toHaveClass 'scrollbars-visible-when-scrolling' describe "the 'window:toggle-invisibles' command", -> it "shows/hides invisibles in all open and future editors", -> + workspaceElement = atom.views.getView(atom.workspace) expect(atom.config.get('editor.showInvisibles')).toBe false atom.commands.dispatch(workspaceElement, 'window:toggle-invisibles') expect(atom.config.get('editor.showInvisibles')).toBe true @@ -28,6 +37,7 @@ describe "WorkspaceElement", -> describe "the 'window:run-package-specs' command", -> it "runs the package specs for the active item's project path, or the first project path", -> + workspaceElement = atom.views.getView(atom.workspace) spyOn(ipc, 'send') # No project paths. Don't try to run specs. diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index e61468e83..252454fda 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,19 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "the scrollbar visibility class", -> - it "has a class based on the style of the scrollbar", -> - style = 'legacy' - scrollbarStyle = require 'scrollbar-style' - spyOn(scrollbarStyle, 'getPreferredScrollbarStyle').andCallFake -> style - - atom.workspaceView.element.observeScrollbarStyle() - expect(atom.workspaceView).toHaveClass 'scrollbars-visible-always' - - style = 'overlay' - atom.workspaceView.element.observeScrollbarStyle() - expect(atom.workspaceView).toHaveClass 'scrollbars-visible-when-scrolling' - describe "editor font styling", -> [editorNode, editor] = [] From ac6a5846b7b78a050a3582775b975ae251ac9034 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:39:20 -0600 Subject: [PATCH 08/78] Move font styling specs to workspace-element-spec --- spec/workspace-element-spec.coffee | 32 ++++++++++++++++++++++++++++++ spec/workspace-view-spec.coffee | 28 -------------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 5f16b6835..6f66ad62d 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -26,6 +26,38 @@ describe "WorkspaceElement", -> observeCallback('overlay') expect(workspaceElement).toHaveClass 'scrollbars-visible-when-scrolling' + describe "editor font styling", -> + [editor, editorElement] = [] + + beforeEach -> + waitsForPromise -> atom.workspace.open('sample.js') + + runs -> + workspaceElement = atom.views.getView(atom.workspace) + jasmine.attachToDOM(workspaceElement) + editor = atom.workspace.getActiveTextEditor() + editorElement = atom.views.getView(editor) + + it "updates the font-size based on the 'editor.fontSize' config value", -> + initialCharWidth = editor.getDefaultCharWidth() + expect(getComputedStyle(editorElement).fontSize).toBe atom.config.get('editor.fontSize') + 'px' + atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5) + expect(getComputedStyle(editorElement).fontSize).toBe atom.config.get('editor.fontSize') + 'px' + expect(editor.getDefaultCharWidth()).toBeGreaterThan initialCharWidth + + it "updates the font-family based on the 'editor.fontFamily' config value", -> + initialCharWidth = editor.getDefaultCharWidth() + expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + atom.config.set('editor.fontFamily', 'sans-serif') + expect(getComputedStyle(editorElement).fontFamily).toBe atom.config.get('editor.fontFamily') + expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth + + it "updates the line-height based on the 'editor.lineHeight' config value", -> + initialLineHeight = editor.getLineHeightInPixels() + atom.config.set('editor.lineHeight', '30px') + expect(getComputedStyle(editorElement).lineHeight).toBe atom.config.get('editor.lineHeight') + expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight + describe "the 'window:toggle-invisibles' command", -> it "shows/hides invisibles in all open and future editors", -> workspaceElement = atom.views.getView(atom.workspace) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index 252454fda..d1c9913df 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -25,34 +25,6 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - describe "editor font styling", -> - [editorNode, editor] = [] - - beforeEach -> - atom.workspaceView.attachToDom() - editorNode = atom.workspaceView.find('atom-text-editor')[0] - editor = atom.workspaceView.find('atom-text-editor').view().getEditor() - - it "updates the font-size based on the 'editor.fontSize' config value", -> - initialCharWidth = editor.getDefaultCharWidth() - expect(getComputedStyle(editorNode).fontSize).toBe atom.config.get('editor.fontSize') + 'px' - atom.config.set('editor.fontSize', atom.config.get('editor.fontSize') + 5) - expect(getComputedStyle(editorNode).fontSize).toBe atom.config.get('editor.fontSize') + 'px' - expect(editor.getDefaultCharWidth()).toBeGreaterThan initialCharWidth - - it "updates the font-family based on the 'editor.fontFamily' config value", -> - initialCharWidth = editor.getDefaultCharWidth() - expect(getComputedStyle(editorNode).fontFamily).toBe atom.config.get('editor.fontFamily') - atom.config.set('editor.fontFamily', 'sans-serif') - expect(getComputedStyle(editorNode).fontFamily).toBe atom.config.get('editor.fontFamily') - expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth - - it "updates the line-height based on the 'editor.lineHeight' config value", -> - initialLineHeight = editor.getLineHeightInPixels() - atom.config.set('editor.lineHeight', '30px') - expect(getComputedStyle(editorNode).lineHeight).toBe atom.config.get('editor.lineHeight') - expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight - describe 'panel containers', -> workspaceElement = null beforeEach -> From 4d231b2a2449f3ebf7a61d52310e8a1b41015b7c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:40:12 -0600 Subject: [PATCH 09/78] Move panel containers specs to workspace-element-spec --- spec/workspace-element-spec.coffee | 17 +++++++++++++++++ spec/workspace-view-spec.coffee | 19 ------------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/spec/workspace-element-spec.coffee b/spec/workspace-element-spec.coffee index 6f66ad62d..09c54d5ea 100644 --- a/spec/workspace-element-spec.coffee +++ b/spec/workspace-element-spec.coffee @@ -58,6 +58,23 @@ describe "WorkspaceElement", -> expect(getComputedStyle(editorElement).lineHeight).toBe atom.config.get('editor.lineHeight') expect(editor.getLineHeightInPixels()).not.toBe initialLineHeight + describe 'panel containers', -> + it 'inserts panel container elements in the correct places in the DOM', -> + workspaceElement = atom.views.getView(atom.workspace) + + leftContainer = workspaceElement.querySelector('atom-panel-container.left') + rightContainer = workspaceElement.querySelector('atom-panel-container.right') + expect(leftContainer.nextSibling).toBe workspaceElement.verticalAxis + expect(rightContainer.previousSibling).toBe workspaceElement.verticalAxis + + topContainer = workspaceElement.querySelector('atom-panel-container.top') + bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom') + expect(topContainer.nextSibling).toBe workspaceElement.paneContainer + expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer + + modalContainer = workspaceElement.querySelector('atom-panel-container.modal') + expect(modalContainer.parentNode).toBe workspaceElement + describe "the 'window:toggle-invisibles' command", -> it "shows/hides invisibles in all open and future editors", -> workspaceElement = atom.views.getView(atom.workspace) diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index d1c9913df..03c0548f6 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -24,22 +24,3 @@ describe "WorkspaceView", -> afterEach -> jasmine.restoreDeprecationsSnapshot() - - describe 'panel containers', -> - workspaceElement = null - beforeEach -> - workspaceElement = atom.views.getView(atom.workspace) - - it 'inserts panel container elements in the correct places in the DOM', -> - leftContainer = workspaceElement.querySelector('atom-panel-container.left') - rightContainer = workspaceElement.querySelector('atom-panel-container.right') - expect(leftContainer.nextSibling).toBe workspaceElement.verticalAxis - expect(rightContainer.previousSibling).toBe workspaceElement.verticalAxis - - topContainer = workspaceElement.querySelector('atom-panel-container.top') - bottomContainer = workspaceElement.querySelector('atom-panel-container.bottom') - expect(topContainer.nextSibling).toBe workspaceElement.paneContainer - expect(bottomContainer.previousSibling).toBe workspaceElement.paneContainer - - modalContainer = workspaceElement.querySelector('atom-panel-container.modal') - expect(modalContainer.parentNode).toBe workspaceElement From 131e2a29b4ab235dca88473352e7f21be786705c Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:40:24 -0600 Subject: [PATCH 10/78] :gun: workspace-view-spec --- spec/workspace-view-spec.coffee | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 spec/workspace-view-spec.coffee diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee deleted file mode 100644 index 03c0548f6..000000000 --- a/spec/workspace-view-spec.coffee +++ /dev/null @@ -1,26 +0,0 @@ -{$, $$, View} = require '../src/space-pen-extensions' -Q = require 'q' -path = require 'path' -temp = require 'temp' -TextEditorView = require '../src/text-editor-view' -PaneView = require '../src/pane-view' -Workspace = require '../src/workspace' - -describe "WorkspaceView", -> - pathToOpen = null - - beforeEach -> - jasmine.snapshotDeprecations() - - atom.project.setPaths([atom.project.getDirectories()[0]?.resolve('dir')]) - pathToOpen = atom.project.getDirectories()[0]?.resolve('a') - atom.workspace = new Workspace - atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView - atom.workspaceView.enableKeymap() - atom.workspaceView.focus() - - waitsForPromise -> - atom.workspace.open(pathToOpen) - - afterEach -> - jasmine.restoreDeprecationsSnapshot() From 6fec11780ba49124247418db9f3f1c4baec3acaa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 10:50:56 -0600 Subject: [PATCH 11/78] :gun: WorkspaceView shim --- benchmark/benchmark-suite.coffee | 4 +- exports/atom.coffee | 7 - spec/spec-helper.coffee | 3 +- src/atom.coffee | 31 +-- src/select-list-view.coffee | 2 +- src/workspace-element.coffee | 7 - src/workspace-view.coffee | 340 ------------------------------- 7 files changed, 9 insertions(+), 385 deletions(-) delete mode 100644 src/workspace-view.coffee diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 7b06d9758..daee36e9f 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -1,3 +1,5 @@ +# + require './benchmark-helper' {$} = require '../src/space-pen-extensions' _ = require 'underscore-plus' @@ -8,7 +10,7 @@ describe "editorView.", -> editorView = null beforeEach -> - atom.workspaceViewParentSelector = '#jasmine-content' + atom.workspaceParentSelectorctor = '#jasmine-content' atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView atom.workspaceView.attachToDom() diff --git a/exports/atom.coffee b/exports/atom.coffee index 5a3d9673e..36bff1967 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -36,13 +36,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE """ require '../src/workspace' - Object.defineProperty module.exports, 'WorkspaceView', get: -> - deprecate """ - Requiring `WorkspaceView` from `atom` is no longer supported. - Use `atom.views.getView(atom.workspace)` instead. - """ - require '../src/workspace-view' - Object.defineProperty module.exports, '$', get: -> deprecate """ Requiring `$` from `atom` is no longer supported. diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index b96d21452..ffb83cd5a 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -102,7 +102,7 @@ beforeEach -> atom.styles.restoreSnapshot(styleElementsToRestore) atom.views.clearDocumentRequests() - atom.workspaceViewParentSelector = '#jasmine-content' + atom.workspaceParentSelectorctor = '#jasmine-content' window.resetTimeouts() spyOn(_._, "now").andCallFake -> window.now @@ -171,7 +171,6 @@ afterEach -> atom.workspace?.destroy() atom.workspace = null - atom.__workspaceView = null delete atom.state.workspace atom.project?.destroy() diff --git a/src/atom.coffee b/src/atom.coffee index 05228b6ca..b5574f774 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -35,28 +35,12 @@ class Atom extends Model atom.deserializeTimings.atom = Date.now() - startTime if includeDeprecatedAPIs - workspaceViewDeprecationMessage = """ - atom.workspaceView is no longer available. - In most cases you will not need the view. See the Workspace docs for - alternatives: https://atom.io/docs/api/latest/Workspace. - If you do need the view, please use `atom.views.getView(atom.workspace)`, - which returns an HTMLElement. - """ - serviceHubDeprecationMessage = """ atom.services is no longer available. To register service providers and consumers, use the `providedServices` and `consumedServices` fields in your package's package.json. """ - Object.defineProperty atom, 'workspaceView', - get: -> - deprecate(workspaceViewDeprecationMessage) - atom.__workspaceView - set: (newValue) -> - deprecate(workspaceViewDeprecationMessage) - atom.__workspaceView = newValue - Object.defineProperty atom, 'services', get: -> deprecate(serviceHubDeprecationMessage) @@ -123,7 +107,7 @@ class Atom extends Model @getCurrentWindow: -> remote.getCurrentWindow() - workspaceViewParentSelector: 'body' + workspaceParentSelectorctor: 'body' lastUncaughtError: null ### @@ -680,7 +664,6 @@ class Atom extends Model # Essential: Visually and audibly trigger a beep. beep: -> shell.beep() if @config.get('core.audioBeep') - @__workspaceView?.trigger 'beep' @emitter.emit 'did-beep' # Essential: A flexible way to open a dialog akin to an alert dialog. @@ -761,24 +744,18 @@ class Atom extends Model @project ?= @deserializers.deserialize(@state.project) ? new Project() @deserializeTimings.project = Date.now() - startTime - deserializeWorkspaceView: -> + deserializeWorkspace: -> Workspace = require './workspace' - if includeDeprecatedAPIs - WorkspaceView = require './workspace-view' - startTime = Date.now() @workspace = Workspace.deserialize(@state.workspace) ? new Workspace workspaceElement = @views.getView(@workspace) - if includeDeprecatedAPIs - @__workspaceView = workspaceElement.__spacePenView - @deserializeTimings.workspace = Date.now() - startTime @keymaps.defaultTarget = workspaceElement - document.querySelector(@workspaceViewParentSelector).appendChild(workspaceElement) + document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement) deserializePackageStates: -> @packages.packageStates = @state.packageStates ? {} @@ -787,7 +764,7 @@ class Atom extends Model deserializeEditorWindow: -> @deserializePackageStates() @deserializeProject() - @deserializeWorkspaceView() + @deserializeWorkspace() loadConfig: -> @config.setSchema null, {type: 'object', properties: _.clone(require('./config-schema'))} diff --git a/src/select-list-view.coffee b/src/select-list-view.coffee index 046be1a3d..e3966af01 100644 --- a/src/select-list-view.coffee +++ b/src/select-list-view.coffee @@ -290,7 +290,7 @@ class SelectListView extends View if @previouslyFocusedElement?.isOnDom() @previouslyFocusedElement.focus() else - atom.workspaceView.focus() + atom.views.getView(atom.workspace).focus() cancelled: -> @filterEditorView.getEditor().setText('') diff --git a/src/workspace-element.coffee b/src/workspace-element.coffee index e07d9c90f..69bdcb276 100644 --- a/src/workspace-element.coffee +++ b/src/workspace-element.coffee @@ -4,7 +4,6 @@ path = require 'path' Grim = require 'grim' scrollbarStyle = require 'scrollbar-style' {callAttachHooks} = require 'space-pen' -WorkspaceView = null module.exports = class WorkspaceElement extends HTMLElement @@ -15,7 +14,6 @@ class WorkspaceElement extends HTMLElement @initializeContent() @observeScrollbarStyle() @observeTextEditorFontConfig() - @createSpacePenShim() if Grim.includeDeprecatedAPIs attachedCallback: -> callAttachHooks(this) if Grim.includeDeprecatedAPIs @@ -64,10 +62,6 @@ class WorkspaceElement extends HTMLElement """ atom.styles.addStyleSheet(styleSheetSource, sourcePath: 'global-text-editor-styles') - createSpacePenShim: -> - WorkspaceView ?= require './workspace-view' - @__spacePenView = new WorkspaceView(this) - initialize: (@model) -> @paneContainer = atom.views.getView(@model.paneContainer) @verticalAxis.appendChild(@paneContainer) @@ -88,7 +82,6 @@ class WorkspaceElement extends HTMLElement @appendChild(@panelContainers.modal) - @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs this getModel: -> @model diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee deleted file mode 100644 index 6fe9f6848..000000000 --- a/src/workspace-view.coffee +++ /dev/null @@ -1,340 +0,0 @@ -ipc = require 'ipc' -path = require 'path' -Q = require 'q' -_ = require 'underscore-plus' -Delegator = require 'delegato' -{deprecate, logDeprecationWarnings} = require 'grim' -{$, $$, View} = require './space-pen-extensions' -fs = require 'fs-plus' -Workspace = require './workspace' -PaneView = require './pane-view' -PaneContainerView = require './pane-container-view' -TextEditor = require './text-editor' - -# Deprecated: The top-level view for the entire window. An instance of this class is -# available via the `atom.workspaceView` global. -# -# It is backed by a model object, an instance of {Workspace}, which is available -# via the `atom.workspace` global or {::getModel}. You should prefer to interact -# with the model object when possible, but it won't always be possible with the -# current API. -# -# ## Adding Perimeter Panels -# -# Use the following methods if possible to attach panels to the perimeter of the -# workspace rather than manipulating the DOM directly to better insulate you to -# changes in the workspace markup: -# -# * {::prependToTop} -# * {::appendToTop} -# * {::prependToBottom} -# * {::appendToBottom} -# * {::prependToLeft} -# * {::appendToLeft} -# * {::prependToRight} -# * {::appendToRight} -# -# ## Requiring in package specs -# -# If you need a `WorkspaceView` instance to test your package, require it via -# the built-in `atom` module. -# -# ```coffee -# {WorkspaceView} = require 'atom' -# ``` -# -# You can assign it to the `atom.workspaceView` global in the spec or just use -# it as a local, depending on what you're trying to accomplish. Building the -# `WorkspaceView` is currently expensive, so you should try build a {Workspace} -# instead if possible. -module.exports = -class WorkspaceView extends View - Delegator.includeInto(this) - - @delegatesProperty 'fullScreen', 'destroyedItemURIs', toProperty: 'model' - @delegatesMethods 'open', 'openSync', - 'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem', - 'destroyActivePane', 'increaseFontSize', 'decreaseFontSize', toProperty: 'model' - - constructor: (@element) -> - unless @element? - return atom.views.getView(atom.workspace).__spacePenView - super - @deprecateViewEvents() - @attachedEditorViews = new WeakSet - - setModel: (@model) -> - @horizontal = @find('atom-workspace-axis.horizontal') - @vertical = @find('atom-workspace-axis.vertical') - @panes = @find('atom-pane-container').view() - @subscribe @model.onDidOpen => @trigger 'uri-opened' - - beforeRemove: -> - @model?.destroy() - - ### - Section: Accessing the Workspace Model - ### - - # Essential: Get the underlying model object. - # - # Returns a {Workspace}. - getModel: -> @model - - ### - Section: Accessing Views - ### - - # Essential: Register a function to be called for every current and future - # editor view in the workspace (only includes {TextEditorView}s that are pane - # items). - # - # * `callback` A {Function} with an {TextEditorView} as its only argument. - # * `editorView` {TextEditorView} - # - # Returns a subscription object with an `.off` method that you can call to - # unregister the callback. - eachEditorView: (callback) -> - for editorView in @getEditorViews() - @attachedEditorViews.add(editorView) - callback(editorView) - - attachedCallback = (e, editorView) => - unless @attachedEditorViews.has(editorView) - @attachedEditorViews.add(editorView) - callback(editorView) unless editorView.mini - - @on('editor:attached', attachedCallback) - - off: => @off('editor:attached', attachedCallback) - - # Essential: Register a function to be called for every current and future - # pane view in the workspace. - # - # * `callback` A {Function} with a {PaneView} as its only argument. - # * `paneView` {PaneView} - # - # Returns a subscription object with an `.off` method that you can call to - # unregister the callback. - eachPaneView: (callback) -> - @panes.eachPaneView(callback) - - # Essential: Get all existing pane views. - # - # Prefer {Workspace::getPanes} if you don't need access to the view objects. - # Also consider using {::eachPaneView} if you want to register a callback for - # all current and *future* pane views. - # - # Returns an Array of all open {PaneView}s. - getPaneViews: -> - @panes.getPaneViews() - - # Essential: Get the active pane view. - # - # Prefer {Workspace::getActivePane} if you don't actually need access to the - # view. - # - # Returns a {PaneView}. - getActivePaneView: -> - @panes.getActivePaneView() - - # Essential: Get the view associated with the active pane item. - # - # Returns a view. - getActiveView: -> - @panes.getActiveView() - - ### - Section: Adding elements to the workspace - ### - - prependToTop: (element) -> - deprecate 'Please use Workspace::addTopPanel() instead' - @vertical.prepend(element) - - appendToTop: (element) -> - deprecate 'Please use Workspace::addTopPanel() instead' - @panes.before(element) - - prependToBottom: (element) -> - deprecate 'Please use Workspace::addBottomPanel() instead' - @panes.after(element) - - appendToBottom: (element) -> - deprecate 'Please use Workspace::addBottomPanel() instead' - @vertical.append(element) - - prependToLeft: (element) -> - deprecate 'Please use Workspace::addLeftPanel() instead' - @horizontal.prepend(element) - - appendToLeft: (element) -> - deprecate 'Please use Workspace::addLeftPanel() instead' - @vertical.before(element) - - prependToRight: (element) -> - deprecate 'Please use Workspace::addRightPanel() instead' - @vertical.after(element) - - appendToRight: (element) -> - deprecate 'Please use Workspace::addRightPanel() instead' - @horizontal.append(element) - - ### - Section: Focusing pane views - ### - - # Focus the previous pane by id. - focusPreviousPaneView: -> @model.activatePreviousPane() - - # Focus the next pane by id. - focusNextPaneView: -> @model.activateNextPane() - - # Focus the pane directly above the active pane. - focusPaneViewAbove: -> @panes.focusPaneViewAbove() - - # Focus the pane directly below the active pane. - focusPaneViewBelow: -> @panes.focusPaneViewBelow() - - # Focus the pane directly to the left of the active pane. - focusPaneViewOnLeft: -> @panes.focusPaneViewOnLeft() - - # Focus the pane directly to the right of the active pane. - focusPaneViewOnRight: -> @panes.focusPaneViewOnRight() - - ### - Section: Private - ### - - # Prompts to save all unsaved items - confirmClose: -> - @model.confirmClose() - - # Get all editor views. - # - # You should prefer {Workspace::getEditors} unless you absolutely need access - # to the view objects. Also consider using {::eachEditorView}, which will call - # a callback for all current and *future* editor views. - # - # Returns an {Array} of {TextEditorView}s. - getEditorViews: -> - for editorElement in @panes.element.querySelectorAll('atom-pane > .item-views > atom-text-editor') - $(editorElement).view() - - ### - Section: Deprecated - ### - - deprecateViewEvents: -> - originalWorkspaceViewOn = @on - - @on = (eventName) => - switch eventName - when 'beep' - deprecate('Use Atom::onDidBeep instead') - when 'cursor:moved' - deprecate('Use TextEditor::onDidChangeCursorPosition instead') - when 'editor:attached' - deprecate('Use Workspace::onDidAddTextEditor instead') - when 'editor:detached' - deprecate('Use TextEditor::onDidDestroy instead') - when 'editor:will-be-removed' - deprecate('Use TextEditor::onDidDestroy instead') - when 'pane:active-item-changed' - deprecate('Use Pane::onDidChangeActiveItem instead') - when 'pane:active-item-modified-status-changed' - deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeModified on the active item instead') - when 'pane:active-item-title-changed' - deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeTitle on the active item instead') - when 'pane:attached' - deprecate('Use Workspace::onDidAddPane instead') - when 'pane:became-active' - deprecate('Use Pane::onDidActivate instead') - when 'pane:became-inactive' - deprecate('Use Pane::onDidChangeActive instead') - when 'pane:item-added' - deprecate('Use Pane::onDidAddItem instead') - when 'pane:item-moved' - deprecate('Use Pane::onDidMoveItem instead') - when 'pane:item-removed' - deprecate('Use Pane::onDidRemoveItem instead') - when 'pane:removed' - deprecate('Use Pane::onDidDestroy instead') - when 'pane-container:active-pane-item-changed' - deprecate('Use Workspace::onDidChangeActivePaneItem instead') - when 'selection:changed' - deprecate('Use TextEditor::onDidChangeSelectionRange instead') - when 'uri-opened' - deprecate('Use Workspace::onDidOpen instead') - originalWorkspaceViewOn.apply(this, arguments) - - TextEditorView = require './text-editor-view' - originalEditorViewOn = TextEditorView::on - TextEditorView::on = (eventName) -> - switch eventName - when 'cursor:moved' - deprecate('Use TextEditor::onDidChangeCursorPosition instead') - when 'editor:attached' - deprecate('Use TextEditor::onDidAddTextEditor instead') - when 'editor:detached' - deprecate('Use TextEditor::onDidDestroy instead') - when 'editor:will-be-removed' - deprecate('Use TextEditor::onDidDestroy instead') - when 'selection:changed' - deprecate('Use TextEditor::onDidChangeSelectionRange instead') - originalEditorViewOn.apply(this, arguments) - - originalPaneViewOn = PaneView::on - PaneView::on = (eventName) -> - switch eventName - when 'cursor:moved' - deprecate('Use TextEditor::onDidChangeCursorPosition instead') - when 'editor:attached' - deprecate('Use TextEditor::onDidAddTextEditor instead') - when 'editor:detached' - deprecate('Use TextEditor::onDidDestroy instead') - when 'editor:will-be-removed' - deprecate('Use TextEditor::onDidDestroy instead') - when 'pane:active-item-changed' - deprecate('Use Pane::onDidChangeActiveItem instead') - when 'pane:active-item-modified-status-changed' - deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeModified on the active item instead') - when 'pane:active-item-title-changed' - deprecate('Use Pane::onDidChangeActiveItem and call onDidChangeTitle on the active item instead') - when 'pane:attached' - deprecate('Use Workspace::onDidAddPane instead') - when 'pane:became-active' - deprecate('Use Pane::onDidActivate instead') - when 'pane:became-inactive' - deprecate('Use Pane::onDidChangeActive instead') - when 'pane:item-added' - deprecate('Use Pane::onDidAddItem instead') - when 'pane:item-moved' - deprecate('Use Pane::onDidMoveItem instead') - when 'pane:item-removed' - deprecate('Use Pane::onDidRemoveItem instead') - when 'pane:removed' - deprecate('Use Pane::onDidDestroy instead') - when 'selection:changed' - deprecate('Use TextEditor::onDidChangeSelectionRange instead') - originalPaneViewOn.apply(this, arguments) - - # Deprecated - eachPane: (callback) -> - deprecate("Use WorkspaceView::eachPaneView instead") - @eachPaneView(callback) - - # Deprecated - getPanes: -> - deprecate("Use WorkspaceView::getPaneViews instead") - @getPaneViews() - - # Deprecated - getActivePane: -> - deprecate("Use WorkspaceView::getActivePaneView instead") - @getActivePaneView() - - # Deprecated: Call {Workspace::getActivePaneItem} instead. - getActivePaneItem: -> - deprecate("Use Workspace::getActivePaneItem instead") - @model.getActivePaneItem() From dfda3adb961eaa9d8f0d53cc3f3477da014911b2 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 11:22:08 -0600 Subject: [PATCH 12/78] Move view-level specs for active pane item changing to pane-element-spec --- spec/pane-element-spec.coffee | 104 ++++++++++++++++++++++++++++++++++ spec/pane-view-spec.coffee | 99 -------------------------------- 2 files changed, 104 insertions(+), 99 deletions(-) create mode 100644 spec/pane-element-spec.coffee diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee new file mode 100644 index 000000000..da284652a --- /dev/null +++ b/spec/pane-element-spec.coffee @@ -0,0 +1,104 @@ +PaneContainer = require '../src/pane-container' + +describe "PaneElement", -> + describe "when the active item changes", -> + it "hides all item elements except the active one", -> + container = new PaneContainer + pane = container.getRoot() + item1 = document.createElement('div') + item2 = document.createElement('div') + item3 = document.createElement('div') + pane.addItem(item1) + pane.addItem(item2) + pane.addItem(item3) + paneElement = atom.views.getView(pane) + + expect(pane.getActiveItem()).toBe item1 + expect(item1.parentElement).toBeDefined() + expect(item1.style.display).toBe '' + expect(item2.parentElement).toBeNull() + expect(item3.parentElement).toBeNull() + + pane.activateItem(item2) + expect(item2.parentElement).toBeDefined() + expect(item1.style.display).toBe 'none' + expect(item2.style.display).toBe '' + expect(item3.parentElement).toBeNull() + + pane.activateItem(item3) + expect(item3.parentElement).toBeDefined() + expect(item1.style.display).toBe 'none' + expect(item2.style.display).toBe 'none' + expect(item3.style.display).toBe '' + + it "transfers focus to the new item if the previous item was focused", -> + container = new PaneContainer + pane = container.getRoot() + item1 = document.createElement('div') + item1.tabIndex = -1 + item2 = document.createElement('div') + item2.tabIndex = -1 + pane.addItem(item1) + pane.addItem(item2) + paneElement = atom.views.getView(pane) + jasmine.attachToDOM(paneElement) + paneElement.focus() + + expect(document.activeElement).toBe item1 + pane.activateItem(item2) + expect(document.activeElement).toBe item2 + + describe "if the active item is a model object", -> + it "retrieves the associated view from atom.views and appends it", -> + class TestModel + + atom.views.addViewProvider TestModel, (model) -> + view = document.createElement('div') + view.model = model + view + + item1 = new TestModel + item2 = new TestModel + + container = new PaneContainer + pane = container.getRoot() + pane.addItem(item1) + pane.addItem(item2) + paneElement = atom.views.getView(pane) + + expect(paneElement.itemViews.children[0].model).toBe item1 + expect(paneElement.itemViews.children[0].style.display).toBe '' + pane.activateItem(item2) + expect(paneElement.itemViews.children[1].model).toBe item2 + expect(paneElement.itemViews.children[0].style.display).toBe 'none' + expect(paneElement.itemViews.children[1].style.display).toBe '' + + describe "when the new active implements .getPath()", -> + it "adds the file path and file name as a data attribute on the pane", -> + container = new PaneContainer + pane = container.getRoot() + item1 = document.createElement('div') + item1.getPath = -> '/foo/bar.txt' + item2 = document.createElement('div') + pane.addItem(item1) + pane.addItem(item2) + paneElement = atom.views.getView(pane) + + expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt' + expect(paneElement.dataset.activeItemName).toBe 'bar.txt' + + pane.activateItem(item2) + + expect(paneElement.dataset.activeItemPath).toBeUndefined() + expect(paneElement.dataset.activeItemName).toBeUndefined() + + pane.activateItem(item1) + expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt' + expect(paneElement.dataset.activeItemName).toBe 'bar.txt' + + pane.destroyItems() + expect(paneElement.dataset.activeItemPath).toBeUndefined() + expect(paneElement.dataset.activeItemName).toBeUndefined() + + + diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 19fa247e0..6d123761d 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,105 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when the active pane item changes", -> - it "hides all item views except the active one", -> - expect(pane.getActiveItem()).toBe view1 - expect(view1.css('display')).not.toBe 'none' - - pane.activateItem(view2) - expect(view1.css('display')).toBe 'none' - expect(view2.css('display')).not.toBe 'none' - - it "triggers 'pane:active-item-changed'", -> - itemChangedHandler = jasmine.createSpy("itemChangedHandler") - container.on 'pane:active-item-changed', itemChangedHandler - - expect(pane.getActiveItem()).toBe view1 - paneModel.activateItem(view2) - paneModel.activateItem(view2) - - expect(itemChangedHandler.callCount).toBe 1 - expect(itemChangedHandler.argsForCall[0][1]).toBe view2 - itemChangedHandler.reset() - - paneModel.activateItem(editor1) - expect(itemChangedHandler).toHaveBeenCalled() - expect(itemChangedHandler.argsForCall[0][1]).toBe editor1 - itemChangedHandler.reset() - - it "transfers focus to the new active view if the previous view was focused", -> - container.attachToDom() - pane.focus() - expect(pane.activeView).not.toBe view2 - expect(pane.activeView).toMatchSelector ':focus' - paneModel.activateItem(view2) - expect(view2).toMatchSelector ':focus' - - describe "when the new activeItem is a model", -> - it "shows the item's view or creates and shows a new view for the item if none exists", -> - initialViewCount = pane.itemViews.find('.test-view').length - - model1 = - id: 'test-model-1' - text: 'Test Model 1' - serialize: -> {@id, @text} - getViewClass: -> TestView - - model2 = - id: 'test-model-2' - text: 'Test Model 2' - serialize: -> {@id, @text} - getViewClass: -> TestView - - paneModel.activateItem(model1) - paneModel.activateItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - paneModel.activatePreviousItem() - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - paneModel.destroyItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1 - - paneModel.destroyItem(model1) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount - - describe "when the new activeItem is a view", -> - it "appends it to the itemViews div if it hasn't already been appended and shows it", -> - expect(pane.itemViews.find('#view-2')).not.toExist() - paneModel.activateItem(view2) - expect(pane.itemViews.find('#view-2')).toExist() - paneModel.activateItem(view1) - paneModel.activateItem(view2) - expect(pane.itemViews.find('#view-2').length).toBe 1 - - describe "when the new activeItem implements ::getPath", -> - beforeEach -> - paneModel.activateItem(editor1) - - it "adds the file path as a data attribute to the pane", -> - expect(pane).toHaveAttr('data-active-item-path') - - it "adds the file name as a data attribute to the pane", -> - expect(pane).toHaveAttr('data-active-item-name') - - describe "when the activeItem is destroyed", -> - it "removes the data attributes", -> - pane.destroyItems() - expect(pane).not.toHaveAttr('data-active-item-path') - expect(pane).not.toHaveAttr('data-active-item-name') - - describe "when the new activeItem does not implement ::getPath", -> - beforeEach -> - paneModel.activateItem(editor1) - paneModel.activateItem(document.createElement('div')) - - it "does not add the file path as a data attribute to the pane", -> - expect(pane).not.toHaveAttr('data-active-item-path') - - it "does not add the file name as data attribute to the pane", -> - expect(pane).not.toHaveAttr('data-active-item-name') - describe "when an item is destroyed", -> it "triggers the 'pane:item-removed' event with the item and its former index", -> itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") From 60670b9c3c9db0bbc73cf42afe558c03e1de1887 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 11:40:43 -0600 Subject: [PATCH 13/78] Move pane item removal specs to pane-element-spec --- spec/pane-element-spec.coffee | 57 ++++++++++++++++++++++++++--------- spec/pane-view-spec.coffee | 21 ------------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee index da284652a..078ef16ea 100644 --- a/spec/pane-element-spec.coffee +++ b/spec/pane-element-spec.coffee @@ -1,17 +1,21 @@ PaneContainer = require '../src/pane-container' describe "PaneElement", -> + [paneElement, container, pane] = [] + + beforeEach -> + container = new PaneContainer + pane = container.getRoot() + paneElement = atom.views.getView(pane) + describe "when the active item changes", -> it "hides all item elements except the active one", -> - container = new PaneContainer - pane = container.getRoot() item1 = document.createElement('div') item2 = document.createElement('div') item3 = document.createElement('div') pane.addItem(item1) pane.addItem(item2) pane.addItem(item3) - paneElement = atom.views.getView(pane) expect(pane.getActiveItem()).toBe item1 expect(item1.parentElement).toBeDefined() @@ -32,15 +36,12 @@ describe "PaneElement", -> expect(item3.style.display).toBe '' it "transfers focus to the new item if the previous item was focused", -> - container = new PaneContainer - pane = container.getRoot() item1 = document.createElement('div') item1.tabIndex = -1 item2 = document.createElement('div') item2.tabIndex = -1 pane.addItem(item1) pane.addItem(item2) - paneElement = atom.views.getView(pane) jasmine.attachToDOM(paneElement) paneElement.focus() @@ -49,7 +50,7 @@ describe "PaneElement", -> expect(document.activeElement).toBe item2 describe "if the active item is a model object", -> - it "retrieves the associated view from atom.views and appends it", -> + it "retrieves the associated view from atom.views and appends it to the itemViews div", -> class TestModel atom.views.addViewProvider TestModel, (model) -> @@ -59,12 +60,8 @@ describe "PaneElement", -> item1 = new TestModel item2 = new TestModel - - container = new PaneContainer - pane = container.getRoot() pane.addItem(item1) pane.addItem(item2) - paneElement = atom.views.getView(pane) expect(paneElement.itemViews.children[0].model).toBe item1 expect(paneElement.itemViews.children[0].style.display).toBe '' @@ -75,14 +72,11 @@ describe "PaneElement", -> describe "when the new active implements .getPath()", -> it "adds the file path and file name as a data attribute on the pane", -> - container = new PaneContainer - pane = container.getRoot() item1 = document.createElement('div') item1.getPath = -> '/foo/bar.txt' item2 = document.createElement('div') pane.addItem(item1) pane.addItem(item2) - paneElement = atom.views.getView(pane) expect(paneElement.dataset.activeItemPath).toBe '/foo/bar.txt' expect(paneElement.dataset.activeItemName).toBe 'bar.txt' @@ -100,5 +94,40 @@ describe "PaneElement", -> expect(paneElement.dataset.activeItemPath).toBeUndefined() expect(paneElement.dataset.activeItemName).toBeUndefined() + describe "when an item is removed from the pane", -> + describe "when the destroyed item is an element", -> + it "removes the item from the itemViews div", -> + item1 = document.createElement('div') + item2 = document.createElement('div') + pane.addItem(item1) + pane.addItem(item2) + paneElement = atom.views.getView(pane) + expect(item1.parentElement).toBe paneElement.itemViews + pane.destroyItem(item1) + expect(item1.parentElement).toBeNull() + expect(item2.parentElement).toBe paneElement.itemViews + pane.destroyItem(item2) + expect(item2.parentElement).toBeNull() + describe "when the destroyed item is a model", -> + it "removes the model's associated view", -> + class TestModel + + atom.views.addViewProvider TestModel, (model) -> + view = document.createElement('div') + model.element = view + view.model = model + view + + item1 = new TestModel + item2 = new TestModel + pane.addItem(item1) + pane.addItem(item2) + + expect(item1.element.parentElement).toBe paneElement.itemViews + pane.destroyItem(item1) + expect(item1.element.parentElement).toBeNull() + expect(item2.element.parentElement).toBe paneElement.itemViews + pane.destroyItem(item2) + expect(item2.element.parentElement).toBeNull() diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 6d123761d..ee8d0a189 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,27 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when an item is destroyed", -> - it "triggers the 'pane:item-removed' event with the item and its former index", -> - itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") - pane.on 'pane:item-removed', itemRemovedHandler - paneModel.destroyItem(editor1) - expect(itemRemovedHandler).toHaveBeenCalled() - expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1] - - describe "when the destroyed item is a view", -> - it "removes the item from the 'item-views' div", -> - expect(view1.parent()).toMatchSelector pane.itemViews - paneModel.destroyItem(view1) - expect(view1.parent()).not.toMatchSelector pane.itemViews - - describe "when the destroyed item is a model", -> - it "removes the associated view", -> - paneModel.activateItem(editor1) - expect(pane.itemViews.find('atom-text-editor').length).toBe 1 - pane.destroyItem(editor1) - expect(pane.itemViews.find('atom-text-editor').length).toBe 0 - describe "when an item is moved within the same pane", -> it "emits a 'pane:item-moved' event with the item and the new index", -> pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") From 4f3db6a271e0edf06bd721b3eec6b68f1e6f22bc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 11:42:29 -0600 Subject: [PATCH 14/78] Remove some tests for outdated pane behaviors --- spec/pane-view-spec.coffee | 112 ------------------------------------- 1 file changed, 112 deletions(-) diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index ee8d0a189..268a979e3 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,91 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when an item is moved within the same pane", -> - it "emits a 'pane:item-moved' event with the item and the new index", -> - pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") - paneModel.moveItem(view1, 2) - expect(itemMovedHandler).toHaveBeenCalled() - expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2] - - describe "when an item is moved to another pane", -> - it "detaches the item's view rather than removing it", -> - container.attachToDom() - expect(view1.is(':visible')).toBe true - paneModel2 = paneModel.splitRight() - view1.data('preservative', 1234) - paneModel.moveItemToPane(view1, paneModel2, 1) - expect(view1.data('preservative')).toBe 1234 - paneModel2.activateItemAtIndex(1) - expect(view1.data('preservative')).toBe 1234 - expect(view1.is(':visible')).toBe true - - describe "when the title of the active item changes", -> - describe 'when there is no onDidChangeTitle method (deprecated)', -> - beforeEach -> - jasmine.snapshotDeprecations() - - view1.onDidChangeTitle = null - view2.onDidChangeTitle = null - - pane.activateItem(view2) - pane.activateItem(view1) - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "emits pane:active-item-title-changed", -> - activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") - pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler - - expect(pane.getActiveItem()).toBe view1 - - view2.trigger 'title-changed' - expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() - - view1.trigger 'title-changed' - expect(activeItemTitleChangedHandler).toHaveBeenCalled() - activeItemTitleChangedHandler.reset() - - pane.activateItem(view2) - view2.trigger 'title-changed' - expect(activeItemTitleChangedHandler).toHaveBeenCalled() - - describe 'when there is a onDidChangeTitle method', -> - it "emits pane:active-item-title-changed", -> - activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") - pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler - - expect(pane.getActiveItem()).toBe view1 - view2.changeTitle() - expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() - - view1.changeTitle() - expect(activeItemTitleChangedHandler).toHaveBeenCalled() - activeItemTitleChangedHandler.reset() - - pane.activateItem(view2) - view2.changeTitle() - expect(activeItemTitleChangedHandler).toHaveBeenCalled() - - describe "when an unmodifed buffer's path is deleted", -> - it "removes the pane item", -> - editor = null - jasmine.unspy(window, 'setTimeout') - filePath = path.join(temp.mkdirSync(), 'file.txt') - fs.writeFileSync(filePath, '') - - waitsForPromise -> - atom.workspace.open(filePath).then (o) -> editor = o - - runs -> - pane.activateItem(editor) - expect(pane.items).toHaveLength(5) - fs.removeSync(filePath) - - waitsFor -> - pane.items.length is 4 - describe "when a pane is destroyed", -> [pane2, pane2Model] = [] @@ -138,13 +53,6 @@ describe "PaneView", -> pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another pane2 = atom.views.getView(pane2Model).__spacePenView - it "triggers a 'pane:removed' event with the pane", -> - removedHandler = jasmine.createSpy("removedHandler") - container.on 'pane:removed', removedHandler - paneModel.destroy() - expect(removedHandler).toHaveBeenCalled() - expect(removedHandler.argsForCall[0][1]).toBe pane - describe "if the destroyed pane has focus", -> [paneToLeft, paneToRight] = [] @@ -156,14 +64,6 @@ describe "PaneView", -> pane2Model.destroy() expect(pane.hasFocus()).toBe true - describe "::getNextPane()", -> - it "returns the next pane if one exists, wrapping around from the last pane to the first", -> - pane.activateItem(editor1) - expect(pane.getNextPane()).toBeUndefined - pane2 = pane.splitRight(pane.copyActiveItem()) - expect(pane.getNextPane()).toBe pane2 - expect(pane2.getNextPane()).toBe pane - describe "when the pane's active status changes", -> [pane2, pane2Model] = [] @@ -179,18 +79,6 @@ describe "PaneView", -> pane2Model.activate() expect(pane).not.toHaveClass('active') - it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", -> - pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler") - pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler") - paneModel.activate() - - expect(becameActiveHandler.callCount).toBe 1 - expect(becameInactiveHandler.callCount).toBe 0 - - pane2Model.activate() - expect(becameActiveHandler.callCount).toBe 1 - expect(becameInactiveHandler.callCount).toBe 1 - describe "when the pane is focused", -> beforeEach -> container.attachToDom() From fec21165d0c2725b6b6f1e20e7fca29dfb2678f4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 13:29:53 -0600 Subject: [PATCH 15/78] Move pane focus transfer test to pane-container-element-spec --- spec/pane-container-element-spec.coffee | 24 ++++++++++++++++++------ spec/pane-view-spec.coffee | 18 ------------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index c4ce8da7e..f0c356a5c 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -4,16 +4,13 @@ PaneAxis = require '../src/pane-axis' describe "PaneContainerElement", -> describe "when panes are added or removed", -> - [paneAxisElement, paneAxis] = [] + it "inserts or removes resize elements", -> + childTagNames = -> + child.nodeName.toLowerCase() for child in paneAxisElement.children - beforeEach -> paneAxis = new PaneAxis paneAxisElement = new PaneAxisElement().initialize(paneAxis) - childTagNames = -> - child.nodeName.toLowerCase() for child in paneAxisElement.children - - it "inserts or removes resize elements", -> expect(childTagNames()).toEqual [] paneAxis.addChild(new PaneAxis) @@ -44,6 +41,21 @@ describe "PaneContainerElement", -> 'atom-pane-axis' ] + it "transfers focus to the next pane if a focused pane is removed", -> + container = new PaneContainer + containerElement = atom.views.getView(container) + leftPane = container.getActivePane() + leftPaneElement = atom.views.getView(leftPane) + rightPane = leftPane.splitRight() + rightPaneElement = atom.views.getView(rightPane) + + jasmine.attachToDOM(containerElement) + rightPaneElement.focus() + expect(document.activeElement).toBe rightPaneElement + + rightPane.destroy() + expect(document.activeElement).toBe leftPaneElement + describe "when the resize element is dragged ", -> [container, containerElement] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 268a979e3..b78e2d9de 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,24 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when a pane is destroyed", -> - [pane2, pane2Model] = [] - - beforeEach -> - pane2Model = paneModel.splitRight() # Can't destroy the last pane, so we add another - pane2 = atom.views.getView(pane2Model).__spacePenView - - describe "if the destroyed pane has focus", -> - [paneToLeft, paneToRight] = [] - - it "focuses the next pane", -> - container.attachToDom() - pane2.activate() - expect(pane.hasFocus()).toBe false - expect(pane2.hasFocus()).toBe true - pane2Model.destroy() - expect(pane.hasFocus()).toBe true - describe "when the pane's active status changes", -> [pane2, pane2Model] = [] From b053e95aa7c6d4d300af2275ca2a5164051a19ee Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 13:32:42 -0600 Subject: [PATCH 16/78] Move tests of adding/removing pane active class to pane-element-spec --- spec/pane-element-spec.coffee | 11 +++++++++++ spec/pane-view-spec.coffee | 15 --------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee index 078ef16ea..edda722b3 100644 --- a/spec/pane-element-spec.coffee +++ b/spec/pane-element-spec.coffee @@ -8,6 +8,17 @@ describe "PaneElement", -> pane = container.getRoot() paneElement = atom.views.getView(pane) + describe "when the pane's active status changes", -> + it "adds or removes the .active class as appropriate", -> + pane2 = pane.splitRight() + expect(pane2.isActive()).toBe true + + expect(paneElement.className).not.toMatch /active/ + pane.activate() + expect(paneElement.className).toMatch /active/ + pane2.activate() + expect(paneElement.className).not.toMatch /active/ + describe "when the active item changes", -> it "hides all item elements except the active one", -> item1 = document.createElement('div') diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index b78e2d9de..5a7bc7472 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,21 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when the pane's active status changes", -> - [pane2, pane2Model] = [] - - beforeEach -> - pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()]) - pane2 = atom.views.getView(pane2Model).__spacePenView - expect(pane2Model.isActive()).toBe true - - it "adds or removes the .active class as appropriate", -> - expect(pane).not.toHaveClass('active') - paneModel.activate() - expect(pane).toHaveClass('active') - pane2Model.activate() - expect(pane).not.toHaveClass('active') - describe "when the pane is focused", -> beforeEach -> container.attachToDom() From e1b8680e1ccac04c6638c63780989b990ec5efb7 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 13:55:31 -0600 Subject: [PATCH 17/78] Move pane focus handling tests to pane-element-spec --- spec/pane-element-spec.coffee | 20 ++++++++++++++++++++ spec/pane-view-spec.coffee | 16 ---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee index edda722b3..28d3b6ce8 100644 --- a/spec/pane-element-spec.coffee +++ b/spec/pane-element-spec.coffee @@ -142,3 +142,23 @@ describe "PaneElement", -> expect(item2.element.parentElement).toBe paneElement.itemViews pane.destroyItem(item2) expect(item2.element.parentElement).toBeNull() + + describe "when the pane element is focused", -> + it "transfers focus to the active view", -> + item = document.createElement('div') + item.tabIndex = -1 + pane.activateItem(item) + jasmine.attachToDOM(paneElement) + + expect(document.activeElement).toBe document.body + paneElement.focus() + expect(document.activeElement).toBe item + + it "makes the pane active", -> + pane.splitRight() + expect(pane.isActive()).toBe false + + jasmine.attachToDOM(paneElement) + paneElement.focus() + + expect(pane.isActive()).toBe true diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 5a7bc7472..ce7b7ed15 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,22 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when the pane is focused", -> - beforeEach -> - container.attachToDom() - - it "transfers focus to the active view", -> - focusHandler = jasmine.createSpy("focusHandler") - pane.getActiveItem().on 'focus', focusHandler - pane.focus() - expect(focusHandler).toHaveBeenCalled() - - it "makes the pane active", -> - paneModel.splitRight(items: [pane.copyActiveItem()]) - expect(paneModel.isActive()).toBe false - pane.focus() - expect(paneModel.isActive()).toBe true - describe "when a pane is split", -> it "builds the appropriateatom-pane-axis.horizontal and pane-column views", -> pane1 = pane From f0fe2d8463d99020845d99406dc946605c81e30a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 13:56:05 -0600 Subject: [PATCH 18/78] Move spliting/unsplitting panes DOM specs to pane-container-element-spec --- spec/pane-container-element-spec.coffee | 24 ++++++++++++++++++++++++ spec/pane-view-spec.coffee | 18 ------------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index f0c356a5c..408e9f2db 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -56,6 +56,30 @@ describe "PaneContainerElement", -> rightPane.destroy() expect(document.activeElement).toBe leftPaneElement + describe "when a pane is split", -> + it "builds appropriately-oriented atom-pane-axis elements", -> + container = new PaneContainer + containerElement = atom.views.getView(container) + + pane1 = container.getRoot() + pane2 = pane1.splitRight() + pane3 = pane2.splitDown() + + horizontalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane') + expect(horizontalPanes.length).toBe 1 + expect(horizontalPanes[0]).toBe atom.views.getView(pane1) + + verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane') + expect(verticalPanes.length).toBe 2 + expect(verticalPanes[0]).toBe atom.views.getView(pane2) + expect(verticalPanes[1]).toBe atom.views.getView(pane3) + + pane1.destroy() + verticalPanes = containerElement.querySelectorAll('atom-pane-container > atom-pane-axis.vertical > atom-pane') + expect(verticalPanes.length).toBe 2 + expect(verticalPanes[0]).toBe atom.views.getView(pane2) + expect(verticalPanes[1]).toBe atom.views.getView(pane3) + describe "when the resize element is dragged ", -> [container, containerElement] = [] diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index ce7b7ed15..383a0ffb6 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,24 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "when a pane is split", -> - it "builds the appropriateatom-pane-axis.horizontal and pane-column views", -> - pane1 = pane - pane1Model = pane.getModel() - pane.activateItem(editor1) - - pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()]) - pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()]) - pane2 = pane2Model._view - pane2 = atom.views.getView(pane2Model).__spacePenView - pane3 = atom.views.getView(pane3Model).__spacePenView - - expect(container.find('> atom-pane-axis.horizontal > atom-pane').toArray()).toEqual [pane1[0]] - expect(container.find('> atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]] - - pane1Model.destroy() - expect(container.find('> atom-pane-axis.vertical > atom-pane').toArray()).toEqual [pane2[0], pane3[0]] - describe "serialization", -> it "focuses the pane after attach only if had focus when serialized", -> container.attachToDom() From b405ad2f9b60974e0f7b734bea0f5e32c2ed664a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 13:59:59 -0600 Subject: [PATCH 19/78] Move test of pane focus on attachment to pane-element-spec --- spec/pane-element-spec.coffee | 6 ++++++ spec/pane-view-spec.coffee | 16 ---------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee index 28d3b6ce8..7742a7735 100644 --- a/spec/pane-element-spec.coffee +++ b/spec/pane-element-spec.coffee @@ -162,3 +162,9 @@ describe "PaneElement", -> paneElement.focus() expect(pane.isActive()).toBe true + + describe "when the pane element is attached", -> + it "focuses the pane element if isFocused() returns true on its model", -> + pane.focus() + jasmine.attachToDOM(paneElement) + expect(document.activeElement).toBe paneElement diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 383a0ffb6..636428281 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -46,22 +46,6 @@ describe "PaneView", -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - describe "serialization", -> - it "focuses the pane after attach only if had focus when serialized", -> - container.attachToDom() - pane.focus() - - container2 = atom.views.getView(container.model.testSerialization()).__spacePenView - pane2 = container2.getRoot() - container2.attachToDom() - expect(pane2).toMatchSelector(':has(:focus)') - - $(document.activeElement).blur() - container3 = atom.views.getView(container.model.testSerialization()).__spacePenView - pane3 = container3.getRoot() - container3.attachToDom() - expect(pane3).not.toMatchSelector(':has(:focus)') - describe "drag and drop", -> buildDragEvent = (type, files) -> dataTransfer = From 65564055029023c37c1b96e90db05353ec02a286 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:03:32 -0600 Subject: [PATCH 20/78] Move pane drag and drop specs to pane-element-spec --- spec/pane-element-spec.coffee | 27 +++++++++++++++++++++++++++ spec/pane-view-spec.coffee | 27 --------------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/spec/pane-element-spec.coffee b/spec/pane-element-spec.coffee index 7742a7735..40712ce90 100644 --- a/spec/pane-element-spec.coffee +++ b/spec/pane-element-spec.coffee @@ -168,3 +168,30 @@ describe "PaneElement", -> pane.focus() jasmine.attachToDOM(paneElement) expect(document.activeElement).toBe paneElement + + describe "drag and drop", -> + buildDragEvent = (type, files) -> + dataTransfer = + files: files + data: {} + setData: (key, value) -> @data[key] = value + getData: (key) -> @data[key] + + event = new CustomEvent("drop") + event.dataTransfer = dataTransfer + event + + describe "when a file is dragged to the pane", -> + it "opens it", -> + spyOn(atom, "open") + event = buildDragEvent("drop", [{path: "/fake1"}, {path: "/fake2"}]) + paneElement.dispatchEvent(event) + expect(atom.open.callCount).toBe 1 + expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2'] + + describe "when a non-file is dragged to the pane", -> + it "does nothing", -> + spyOn(atom, "open") + event = buildDragEvent("drop", []) + paneElement.dispatchEvent(event) + expect(atom.open).not.toHaveBeenCalled() diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 636428281..aea146046 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -45,30 +45,3 @@ describe "PaneView", -> afterEach -> deserializerDisposable.dispose() jasmine.restoreDeprecationsSnapshot() - - describe "drag and drop", -> - buildDragEvent = (type, files) -> - dataTransfer = - files: files - data: {} - setData: (key, value) -> @data[key] = value - getData: (key) -> @data[key] - - event = new CustomEvent("drop") - event.dataTransfer = dataTransfer - event - - describe "when a file is dragged to window", -> - it "opens it", -> - spyOn(atom, "open") - event = buildDragEvent("drop", [ {path: "/fake1"}, {path: "/fake2"} ]) - pane[0].dispatchEvent(event) - expect(atom.open.callCount).toBe 1 - expect(atom.open.argsForCall[0][0]).toEqual pathsToOpen: ['/fake1', '/fake2'] - - describe "when a non-file is dragged to window", -> - it "does nothing", -> - spyOn(atom, "open") - event = buildDragEvent("drop", []) - pane[0].dispatchEvent(event) - expect(atom.open).not.toHaveBeenCalled() From 34f16e2da5a1073b79361dac18c98057e88e867a Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:03:43 -0600 Subject: [PATCH 21/78] Remove pane-view-spec --- spec/pane-view-spec.coffee | 47 -------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 spec/pane-view-spec.coffee diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee deleted file mode 100644 index aea146046..000000000 --- a/spec/pane-view-spec.coffee +++ /dev/null @@ -1,47 +0,0 @@ -PaneContainer = require '../src/pane-container' -PaneView = require '../src/pane-view' -fs = require 'fs-plus' -{Emitter, Disposable} = require 'event-kit' -{$, View} = require '../src/space-pen-extensions' -path = require 'path' -temp = require 'temp' - -describe "PaneView", -> - [container, containerModel, view1, view2, editor1, editor2, pane, paneModel, deserializerDisposable] = [] - - class TestView extends View - @deserialize: ({id, text}) -> new TestView({id, text}) - @content: ({id, text}) -> @div class: 'test-view', id: id, tabindex: -1, text - initialize: ({@id, @text}) -> - @emitter = new Emitter - serialize: -> {deserializer: 'TestView', @id, @text} - getURI: -> @id - isEqual: (other) -> other? and @id is other.id and @text is other.text - changeTitle: -> - @emitter.emit 'did-change-title', 'title' - onDidChangeTitle: (callback) -> - @emitter.on 'did-change-title', callback - onDidChangeModified: -> new Disposable(->) - - beforeEach -> - jasmine.snapshotDeprecations() - - deserializerDisposable = atom.deserializers.add(TestView) - container = atom.views.getView(new PaneContainer).__spacePenView - containerModel = container.model - view1 = new TestView(id: 'view-1', text: 'View 1') - view2 = new TestView(id: 'view-2', text: 'View 2') - waitsForPromise -> - atom.workspace.open('sample.js').then (o) -> editor1 = o - - waitsForPromise -> - atom.workspace.open('sample.txt').then (o) -> editor2 = o - - runs -> - pane = container.getRoot() - paneModel = pane.getModel() - paneModel.addItems([view1, editor1, view2, editor2]) - - afterEach -> - deserializerDisposable.dispose() - jasmine.restoreDeprecationsSnapshot() From 8d0207e82b677307e4c375d8c041ecde63486cb3 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:17:45 -0600 Subject: [PATCH 22/78] Move pane container serialization specs to model layer --- spec/pane-container-spec.coffee | 26 +++++++++++++++++++++++++ spec/pane-container-view-spec.coffee | 29 ---------------------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 09681a07a..0ca82fa4c 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -40,6 +40,32 @@ describe "PaneContainer", -> containerB = atom.deserializers.deserialize(state) expect(containerB.getActivePane()).toBe containerB.getPanes()[0] + describe "if there are empty panes after deserialization", -> + beforeEach -> + pane3A.getItems()[0].serialize = -> deserializer: 'Bogus' + + describe "if the 'core.destroyEmptyPanes' config option is false (the default)", -> + it "leaves the empty panes intact", -> + state = containerA.serialize() + containerB = atom.deserializers.deserialize(state) + [leftPane, column] = containerB.getRoot().getChildren() + [topPane, bottomPane] = column.getChildren() + + expect(leftPane.getItems().length).toBe 1 + expect(topPane.getItems().length).toBe 1 + expect(bottomPane.getItems().length).toBe 0 + + describe "if the 'core.destroyEmptyPanes' config option is true", -> + it "removes empty panes on deserialization", -> + atom.config.set('core.destroyEmptyPanes', true) + + state = containerA.serialize() + containerB = atom.deserializers.deserialize(state) + [leftPane, rightPane] = containerB.getRoot().getChildren() + + expect(leftPane.getItems().length).toBe 1 + expect(rightPane.getItems().length).toBe 1 + it "does not allow the root pane to be destroyed", -> container = new PaneContainer container.getRoot().destroy() diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 1b9cf9813..560c793ee 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -74,35 +74,6 @@ describe "PaneContainerView", -> for item in pane.getItems() expect(item.saved).toBeTruthy() - describe "serialization", -> - it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", -> - newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView - expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist() - expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)')).toExist() - expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(3)')).toExist() - - newContainer.height(200).width(300).attachToDom() - expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)').width()).toBe 150 - expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > :contains(2)').height()).toBe 100 - - describe "if there are empty panes after deserialization", -> - beforeEach -> - # only deserialize pane 1's view successfully - TestView.deserialize = ({name}) -> new TestView(name) if name is '1' - - describe "if the 'core.destroyEmptyPanes' config option is false (the default)", -> - it "leaves the empty panes intact", -> - newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView - expect(newContainer.find('atom-pane-axis.horizontal > :contains(1)')).toExist() - expect(newContainer.find('atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane').length).toBe 2 - - describe "if the 'core.destroyEmptyPanes' config option is true", -> - it "removes empty panes on deserialization", -> - atom.config.set('core.destroyEmptyPanes', true) - newContainer = atom.views.getView(container.model.testSerialization()).__spacePenView - expect(newContainer.find('atom-pane-axis.horizontal, atom-pane-axis.vertical')).not.toExist() - expect(newContainer.find('> :contains(1)')).toExist() - describe "pane-container:active-pane-item-changed", -> [pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = [] beforeEach -> From 2b75244240b2115e949f5ebd67dc2605fd72da05 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:18:07 -0600 Subject: [PATCH 23/78] Move pane container saveAll specs to model layer --- spec/pane-container-spec.coffee | 16 ++++++++++++++++ spec/pane-container-view-spec.coffee | 10 ---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index 0ca82fa4c..b32868d8e 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -249,3 +249,19 @@ describe "PaneContainer", -> ['will', {item: item2, pane: pane2, index: 0}] ['did', {item: item2, pane: pane2, index: 0}] ] + + describe "::saveAll()", -> + it "saves all open pane items", -> + container = new PaneContainer + pane1 = container.getRoot() + pane2 = pane1.splitRight() + + pane1.addItem(item1 = {getURI: (-> ''), save: -> @saved = true}) + pane1.addItem(item2 = {getURI: (-> ''), save: -> @saved = true}) + pane2.addItem(item3 = {getURI: (-> ''), save: -> @saved = true}) + + container.saveAll() + + expect(item1.saved).toBe true + expect(item2.saved).toBe true + expect(item3.saved).toBe true diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 560c793ee..15cb9ac0a 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -64,16 +64,6 @@ describe "PaneContainerView", -> pane4.splitDown() expect(panes).toEqual [] - describe ".saveAll()", -> - it "saves all open pane items", -> - pane1.activateItem(new TestView('4')) - - container.saveAll() - - for pane in container.getPaneViews() - for item in pane.getItems() - expect(item.saved).toBeTruthy() - describe "pane-container:active-pane-item-changed", -> [pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = [] beforeEach -> From e01057b1208fb3271bc01053d5a0d89bed6143a1 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:20:03 -0600 Subject: [PATCH 24/78] Remove tests of deprecated pane container behavior --- spec/pane-container-view-spec.coffee | 177 --------------------------- 1 file changed, 177 deletions(-) diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index 15cb9ac0a..c0c2b71a6 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -31,183 +31,6 @@ describe "PaneContainerView", -> afterEach -> deserializerDisposable.dispose() - describe ".getActivePaneView()", -> - it "returns the most-recently focused pane", -> - focusStealer = $$ -> @div tabindex: -1, "focus stealer" - focusStealer.attachToDom() - container.attachToDom() - - pane2.focus() - expect(container.getFocusedPane()).toBe pane2 - expect(container.getActivePaneView()).toBe pane2 - - focusStealer.focus() - expect(container.getFocusedPane()).toBeUndefined() - expect(container.getActivePaneView()).toBe pane2 - - pane3.focus() - expect(container.getFocusedPane()).toBe pane3 - expect(container.getActivePaneView()).toBe pane3 - - describe ".eachPaneView(callback)", -> - it "runs the callback with all current and future panes until the subscription is cancelled", -> - panes = [] - subscription = container.eachPaneView (pane) -> panes.push(pane) - expect(panes).toEqual [pane1, pane2, pane3] - - panes = [] - pane4 = pane3.splitRight(pane3.copyActiveItem()) - expect(panes).toEqual [pane4] - - panes = [] - subscription.off() - pane4.splitDown() - expect(panes).toEqual [] - - describe "pane-container:active-pane-item-changed", -> - [pane1, item1a, item1b, item2a, item2b, item3a, container, activeItemChangedHandler] = [] - beforeEach -> - item1a = new TestView('1a') - item1b = new TestView('1b') - item2a = new TestView('2a') - item2b = new TestView('2b') - item3a = new TestView('3a') - - container = atom.views.getView(new PaneContainer).__spacePenView - pane1 = container.getRoot() - pane1.activateItem(item1a) - container.attachToDom() - - activeItemChangedHandler = jasmine.createSpy("activeItemChangedHandler") - container.on 'pane-container:active-pane-item-changed', activeItemChangedHandler - - describe "when there is one pane", -> - it "is triggered when a new pane item is added", -> - pane1.activateItem(item1b) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1b - - it "is not triggered when the active pane item is shown again", -> - pane1.activateItem(item1a) - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - it "is triggered when switching to an existing pane item", -> - pane1.activateItem(item1b) - activeItemChangedHandler.reset() - - pane1.activateItem(item1a) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a - - it "is triggered when the active pane item is destroyed", -> - pane1.activateItem(item1b) - activeItemChangedHandler.reset() - - pane1.destroyItem(item1b) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a - - it "is not triggered when an inactive pane item is destroyed", -> - pane1.activateItem(item1b) - activeItemChangedHandler.reset() - - pane1.destroyItem(item1a) - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - it "is triggered when all pane items are destroyed", -> - pane1.destroyItem(item1a) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toBe undefined - - describe "when there are two panes", -> - [pane2] = [] - - beforeEach -> - pane2 = pane1.splitLeft(item2a) - activeItemChangedHandler.reset() - - it "is triggered when a new pane item is added to the active pane", -> - pane2.activateItem(item2b) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2b - - it "is not triggered when a new pane item is added to an inactive pane", -> - pane1.activateItem(item1b) - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - it "is triggered when the active pane's active item is destroyed", -> - pane2.activateItem(item2b) - activeItemChangedHandler.reset() - - pane2.destroyItem(item2b) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item2a - - it "is not triggered when an inactive pane's active item is destroyed", -> - pane1.activateItem(item1b) - activeItemChangedHandler.reset() - - pane1.destroyItem(item1b) - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - it "is triggered when the active pane is destroyed", -> - pane2.remove() - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a - - it "is not triggered when an inactive pane is destroyed", -> - pane1.remove() - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - it "is triggered when the active pane is changed", -> - pane1.activate() - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item1a - - describe "when there are multiple panes", -> - beforeEach -> - pane2 = pane1.splitRight(item2a) - activeItemChangedHandler.reset() - - it "is triggered when a new pane is added", -> - pane2.splitDown(item3a) - expect(activeItemChangedHandler.callCount).toBe 1 - expect(activeItemChangedHandler.argsForCall[0][1]).toEqual item3a - - it "is not triggered when an inactive pane is destroyed", -> - pane3 = pane2.splitDown(item3a) - activeItemChangedHandler.reset() - - pane1.remove() - pane2.remove() - expect(activeItemChangedHandler).not.toHaveBeenCalled() - - describe ".focusNextPaneView()", -> - it "focuses the pane following the focused pane or the first pane if no pane has focus", -> - container.attachToDom() - container.focusNextPaneView() - expect(pane1.activeItem).toMatchSelector ':focus' - container.focusNextPaneView() - expect(pane2.activeItem).toMatchSelector ':focus' - container.focusNextPaneView() - expect(pane3.activeItem).toMatchSelector ':focus' - container.focusNextPaneView() - expect(pane1.activeItem).toMatchSelector ':focus' - - describe ".focusPreviousPaneView()", -> - it "focuses the pane preceding the focused pane or the last pane if no pane has focus", -> - container.attachToDom() - container.getPaneViews()[0].focus() # activate first pane - - container.focusPreviousPaneView() - expect(pane3.activeItem).toMatchSelector ':focus' - container.focusPreviousPaneView() - expect(pane2.activeItem).toMatchSelector ':focus' - container.focusPreviousPaneView() - expect(pane1.activeItem).toMatchSelector ':focus' - container.focusPreviousPaneView() - expect(pane3.activeItem).toMatchSelector ':focus' - describe "changing focus directionally between panes", -> [pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = [] From cb3ea3937707c63348014f899f118b0d35b05a98 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:28:53 -0600 Subject: [PATCH 25/78] Move direction focus specs to pane-container-element-spec --- spec/pane-container-element-spec.coffee | 93 +++++++++++++++++++++++++ spec/pane-container-view-spec.coffee | 86 ----------------------- 2 files changed, 93 insertions(+), 86 deletions(-) diff --git a/spec/pane-container-element-spec.coffee b/spec/pane-container-element-spec.coffee index 408e9f2db..890baec38 100644 --- a/spec/pane-container-element-spec.coffee +++ b/spec/pane-container-element-spec.coffee @@ -230,3 +230,96 @@ describe "PaneContainerElement", -> atom.commands.dispatch(atom.views.getView(rightPane), 'pane:decrease-size') expect(leftPane.getFlexScale()).toBe 1/1.1 expect(rightPane.getFlexScale()).toBe 1/1.1 + + describe "changing focus directionally between panes", -> + [containerElement, pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = [] + + beforeEach -> + # Set up a grid of 9 panes, in the following arrangement, where the + # numbers correspond to the variable names below. + # + # ------- + # |1|2|3| + # ------- + # |4|5|6| + # ------- + # |7|8|9| + # ------- + + buildElement = (id) -> + element = document.createElement('div') + element.textContent = id + element.tabIndex = -1 + element + + container = new PaneContainer + pane1 = container.getRoot() + pane1.activateItem(buildElement('1')) + pane4 = pane1.splitDown(items: [buildElement('4')]) + pane7 = pane4.splitDown(items: [buildElement('7')]) + + pane2 = pane1.splitRight(items: [buildElement('2')]) + pane3 = pane2.splitRight(items: [buildElement('3')]) + + pane5 = pane4.splitRight(items: [buildElement('5')]) + pane6 = pane5.splitRight(items: [buildElement('6')]) + + pane8 = pane7.splitRight(items: [buildElement('8')]) + pane9 = pane8.splitRight(items: [buildElement('9')]) + + containerElement = atom.views.getView(container) + containerElement.style.height = '400px' + containerElement.style.width = '400px' + jasmine.attachToDOM(containerElement) + + describe "::focusPaneViewAbove()", -> + describe "when there are multiple rows above the focused pane", -> + it "focuses up to the adjacent row", -> + pane8.activate() + containerElement.focusPaneViewAbove() + expect(document.activeElement).toBe pane5.getActiveItem() + + describe "when there are no rows above the focused pane", -> + it "keeps the current pane focused", -> + pane2.activate() + containerElement.focusPaneViewAbove() + expect(document.activeElement).toBe pane2.getActiveItem() + + describe "::focusPaneViewBelow()", -> + describe "when there are multiple rows below the focused pane", -> + it "focuses down to the adjacent row", -> + pane2.activate() + containerElement.focusPaneViewBelow() + expect(document.activeElement).toBe pane5.getActiveItem() + + describe "when there are no rows below the focused pane", -> + it "keeps the current pane focused", -> + pane8.activate() + containerElement.focusPaneViewBelow() + expect(document.activeElement).toBe pane8.getActiveItem() + + describe "::focusPaneViewOnLeft()", -> + describe "when there are multiple columns to the left of the focused pane", -> + it "focuses left to the adjacent column", -> + pane6.activate() + containerElement.focusPaneViewOnLeft() + expect(document.activeElement).toBe pane5.getActiveItem() + + describe "when there are no columns to the left of the focused pane", -> + it "keeps the current pane focused", -> + pane4.activate() + containerElement.focusPaneViewOnLeft() + expect(document.activeElement).toBe pane4.getActiveItem() + + describe "::focusPaneViewOnRight()", -> + describe "when there are multiple columns to the right of the focused pane", -> + it "focuses right to the adjacent column", -> + pane4.activate() + containerElement.focusPaneViewOnRight() + expect(document.activeElement).toBe pane5.getActiveItem() + + describe "when there are no columns to the right of the focused pane", -> + it "keeps the current pane focused", -> + pane6.activate() + containerElement.focusPaneViewOnRight() + expect(document.activeElement).toBe pane6.getActiveItem() diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index c0c2b71a6..3efdd0c25 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -30,89 +30,3 @@ describe "PaneContainerView", -> afterEach -> deserializerDisposable.dispose() - - describe "changing focus directionally between panes", -> - [pane1, pane2, pane3, pane4, pane5, pane6, pane7, pane8, pane9] = [] - - beforeEach -> - # Set up a grid of 9 panes, in the following arrangement, where the - # numbers correspond to the variable names below. - # - # ------- - # |1|2|3| - # ------- - # |4|5|6| - # ------- - # |7|8|9| - # ------- - - container = atom.views.getView(new PaneContainer).__spacePenView - pane1 = container.getRoot() - pane1.activateItem(new TestView('1')) - pane4 = pane1.splitDown(new TestView('4')) - pane7 = pane4.splitDown(new TestView('7')) - - pane2 = pane1.splitRight(new TestView('2')) - pane3 = pane2.splitRight(new TestView('3')) - - pane5 = pane4.splitRight(new TestView('5')) - pane6 = pane5.splitRight(new TestView('6')) - - pane8 = pane7.splitRight(new TestView('8')) - pane9 = pane8.splitRight(new TestView('9')) - - container.height(400) - container.width(400) - container.attachToDom() - - describe ".focusPaneViewAbove()", -> - describe "when there are multiple rows above the focused pane", -> - it "focuses up to the adjacent row", -> - pane8.focus() - container.focusPaneViewAbove() - expect(pane5.activeItem).toMatchSelector ':focus' - - describe "when there are no rows above the focused pane", -> - it "keeps the current pane focused", -> - pane2.focus() - container.focusPaneViewAbove() - expect(pane2.activeItem).toMatchSelector ':focus' - - describe ".focusPaneViewBelow()", -> - describe "when there are multiple rows below the focused pane", -> - it "focuses down to the adjacent row", -> - pane2.focus() - container.focusPaneViewBelow() - expect(pane5.activeItem).toMatchSelector ':focus' - - describe "when there are no rows below the focused pane", -> - it "keeps the current pane focused", -> - pane8.focus() - container.focusPaneViewBelow() - expect(pane8.activeItem).toMatchSelector ':focus' - - describe ".focusPaneViewOnLeft()", -> - describe "when there are multiple columns to the left of the focused pane", -> - it "focuses left to the adjacent column", -> - pane6.focus() - container.focusPaneViewOnLeft() - expect(pane5.activeItem).toMatchSelector ':focus' - - describe "when there are no columns to the left of the focused pane", -> - it "keeps the current pane focused", -> - pane4.focus() - container.focusPaneViewOnLeft() - expect(pane4.activeItem).toMatchSelector ':focus' - - describe ".focusPaneViewOnRight()", -> - describe "when there are multiple columns to the right of the focused pane", -> - it "focuses right to the adjacent column", -> - pane4.focus() - container.focusPaneViewOnRight() - expect(pane5.activeItem).toMatchSelector ':focus' - - describe "when there are no columns to the right of the focused pane", -> - it "keeps the current pane focused", -> - pane6.focus() - container.focusPaneViewOnRight() - expect(pane6.activeItem).toMatchSelector ':focus' From 6858833931d6664a034a0df116d3c9cdf7b865ac Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:29:09 -0600 Subject: [PATCH 26/78] Remove pane-container-view-spec --- spec/pane-container-view-spec.coffee | 32 ---------------------------- 1 file changed, 32 deletions(-) delete mode 100644 spec/pane-container-view-spec.coffee diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee deleted file mode 100644 index 3efdd0c25..000000000 --- a/spec/pane-container-view-spec.coffee +++ /dev/null @@ -1,32 +0,0 @@ -path = require 'path' -temp = require 'temp' -PaneContainer = require '../src/pane-container' -PaneContainerView = require '../src/pane-container-view' -PaneView = require '../src/pane-view' -{Disposable} = require 'event-kit' -{$, View, $$} = require '../src/space-pen-extensions' - -describe "PaneContainerView", -> - [TestView, container, pane1, pane2, pane3, deserializerDisposable] = [] - - beforeEach -> - class TestView extends View - deserializerDisposable = atom.deserializers.add(this) - @deserialize: ({name}) -> new TestView(name) - @content: -> @div tabindex: -1 - initialize: (@name) -> @text(@name) - serialize: -> {deserializer: 'TestView', @name} - getURI: -> path.join(temp.dir, @name) - save: -> @saved = true - isEqual: (other) -> @name is other?.name - onDidChangeTitle: -> new Disposable(->) - onDidChangeModified: -> new Disposable(->) - - container = atom.views.getView(atom.workspace.paneContainer).__spacePenView - pane1 = container.getRoot() - pane1.activateItem(new TestView('1')) - pane2 = pane1.splitRight(new TestView('2')) - pane3 = pane2.splitDown(new TestView('3')) - - afterEach -> - deserializerDisposable.dispose() From 4c73395608d99cc477f7f99c0f24cdd6b674a656 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:43:11 -0600 Subject: [PATCH 27/78] Remove PaneContainerView and PaneView SpacePen shims --- src/pane-container-element.coffee | 6 -- src/pane-container-view.coffee | 89 ---------------- src/pane-element.coffee | 8 -- src/pane-view.coffee | 167 ------------------------------ 4 files changed, 270 deletions(-) delete mode 100644 src/pane-container-view.coffee delete mode 100644 src/pane-view.coffee diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee index 94b008255..23763e055 100644 --- a/src/pane-container-element.coffee +++ b/src/pane-container-element.coffee @@ -1,7 +1,6 @@ {CompositeDisposable} = require 'event-kit' Grim = require 'grim' {callAttachHooks} = require './space-pen-extensions' -PaneContainerView = null _ = require 'underscore-plus' module.exports = @@ -10,13 +9,8 @@ class PaneContainerElement extends HTMLElement @subscriptions = new CompositeDisposable @classList.add 'panes' - if Grim.includeDeprecatedAPIs - PaneContainerView ?= require './pane-container-view' - @__spacePenView = new PaneContainerView(this) - initialize: (@model) -> @subscriptions.add @model.observeRoot(@rootChanged.bind(this)) - @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs this rootChanged: (root) -> diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee deleted file mode 100644 index d92e99299..000000000 --- a/src/pane-container-view.coffee +++ /dev/null @@ -1,89 +0,0 @@ -{deprecate} = require 'grim' -Delegator = require 'delegato' -{CompositeDisposable} = require 'event-kit' -{$, View, callAttachHooks} = require './space-pen-extensions' -PaneView = require './pane-view' -PaneContainer = require './pane-container' - -# Manages the list of panes within a {WorkspaceView} -module.exports = -class PaneContainerView extends View - Delegator.includeInto(this) - - @delegatesMethod 'saveAll', toProperty: 'model' - - @content: -> - @div class: 'panes' - - constructor: (@element) -> - super - @subscriptions = new CompositeDisposable - - setModel: (@model) -> - @subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged) - - getRoot: -> - view = atom.views.getView(@model.getRoot()) - view.__spacePenView ? view - - onActivePaneItemChanged: (activeItem) => - @trigger 'pane-container:active-pane-item-changed', [activeItem] - - confirmClose: -> - @model.confirmClose() - - getPaneViews: -> - @find('atom-pane').views() - - indexOfPane: (paneView) -> - @getPaneViews().indexOf(paneView.view()) - - paneAtIndex: (index) -> - @getPaneViews()[index] - - eachPaneView: (callback) -> - callback(paneView) for paneView in @getPaneViews() - paneViewAttached = (e) -> callback($(e.target).view()) - @on 'pane:attached', paneViewAttached - off: => @off 'pane:attached', paneViewAttached - - getFocusedPane: -> - @find('atom-pane:has(:focus)').view() - - getActivePane: -> - deprecate("Use PaneContainerView::getActivePaneView instead.") - @getActivePaneView() - - getActivePaneView: -> - atom.views.getView(@model.getActivePane()).__spacePenView - - getActivePaneItem: -> - @model.getActivePaneItem() - - getActiveView: -> - @getActivePaneView()?.activeView - - paneForUri: (uri) -> - atom.views.getView(@model.paneForURI(uri)).__spacePenView - - focusNextPaneView: -> - @model.activateNextPane() - - focusPreviousPaneView: -> - @model.activatePreviousPane() - - focusPaneViewAbove: -> - @element.focusPaneViewAbove() - - focusPaneViewBelow: -> - @element.focusPaneViewBelow() - - focusPaneViewOnLeft: -> - @element.focusPaneViewOnLeft() - - focusPaneViewOnRight: -> - @element.focusPaneViewOnRight() - - getPanes: -> - deprecate("Use PaneContainerView::getPaneViews() instead") - @getPaneViews() diff --git a/src/pane-element.coffee b/src/pane-element.coffee index 0d53269fd..7bafa26bb 100644 --- a/src/pane-element.coffee +++ b/src/pane-element.coffee @@ -2,7 +2,6 @@ path = require 'path' {CompositeDisposable} = require 'event-kit' Grim = require 'grim' {$, callAttachHooks, callRemoveHooks} = require './space-pen-extensions' -PaneView = null class PaneElement extends HTMLElement attached: false @@ -14,7 +13,6 @@ class PaneElement extends HTMLElement @initializeContent() @subscribeToDOMEvents() - @createSpacePenShim() if Grim.includeDeprecatedAPIs attachedCallback: -> @attached = true @@ -55,10 +53,6 @@ class PaneElement extends HTMLElement @addEventListener 'dragover', handleDragOver @addEventListener 'drop', handleDrop - createSpacePenShim: -> - PaneView ?= require './pane-view' - @__spacePenView = new PaneView(this) - initialize: (@model) -> @subscriptions.add @model.onDidActivate(@activated.bind(this)) @subscriptions.add @model.observeActive(@activeStatusChanged.bind(this)) @@ -66,8 +60,6 @@ class PaneElement extends HTMLElement @subscriptions.add @model.onDidRemoveItem(@itemRemoved.bind(this)) @subscriptions.add @model.onDidDestroy(@paneDestroyed.bind(this)) @subscriptions.add @model.observeFlexScale(@flexScaleChanged.bind(this)) - - @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs this getModel: -> @model diff --git a/src/pane-view.coffee b/src/pane-view.coffee deleted file mode 100644 index 775514ca2..000000000 --- a/src/pane-view.coffee +++ /dev/null @@ -1,167 +0,0 @@ -{$, View} = require './space-pen-extensions' -Delegator = require 'delegato' -{deprecate} = require 'grim' -{CompositeDisposable} = require 'event-kit' -PropertyAccessors = require 'property-accessors' - -Pane = require './pane' - -# A container which can contains multiple items to be switched between. -# -# Items can be almost anything however most commonly they're {TextEditorView}s. -# -# Most packages won't need to use this class, unless you're interested in -# building a package that deals with switching between panes or items. -module.exports = -class PaneView extends View - Delegator.includeInto(this) - PropertyAccessors.includeInto(this) - - @delegatesProperties 'items', 'activeItem', toProperty: 'model' - @delegatesMethods 'getItems', 'activateNextItem', 'activatePreviousItem', 'getActiveItemIndex', - 'activateItemAtIndex', 'activateItem', 'addItem', 'itemAtIndex', 'moveItem', 'moveItemToPane', - 'destroyItem', 'destroyItems', 'destroyActiveItem', 'destroyInactiveItems', - 'saveActiveItem', 'saveActiveItemAs', 'saveItem', 'saveItemAs', 'saveItems', - 'itemForUri', 'activateItemForUri', 'promptToSaveItem', 'copyActiveItem', 'isActive', - 'activate', 'getActiveItem', toProperty: 'model' - - previousActiveItem: null - attached: false - - constructor: (@element) -> - @itemViews = $(element.itemViews) - super - - setModel: (@model) -> - @subscriptions = new CompositeDisposable - @subscriptions.add @model.observeActiveItem(@onActiveItemChanged) - @subscriptions.add @model.onDidAddItem(@onItemAdded) - @subscriptions.add @model.onDidRemoveItem(@onItemRemoved) - @subscriptions.add @model.onDidMoveItem(@onItemMoved) - @subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed) - @subscriptions.add @model.observeActive(@onActiveStatusChanged) - @subscriptions.add @model.onDidDestroy(@onPaneDestroyed) - - afterAttach: -> - @container ?= @closest('atom-pane-container').view() - @trigger('pane:attached', [this]) unless @attached - @attached = true - - onPaneDestroyed: => - @container?.trigger 'pane:removed', [this] - @subscriptions.dispose() - - remove: -> - @model.destroy() unless @model.isDestroyed() - - # Essential: Returns the {Pane} model underlying this pane view - getModel: -> @model - - # Deprecated: Use ::destroyItem - removeItem: (item) -> - deprecate("Use PaneView::destroyItem instead") - @destroyItem(item) - - # Deprecated: Use ::activateItem - showItem: (item) -> - deprecate("Use PaneView::activateItem instead") - @activateItem(item) - - # Deprecated: Use ::activateItemForUri - showItemForUri: (item) -> - deprecate("Use PaneView::activateItemForUri instead") - @activateItemForUri(item) - - # Deprecated: Use ::activateItemAtIndex - showItemAtIndex: (index) -> - deprecate("Use PaneView::activateItemAtIndex instead") - @activateItemAtIndex(index) - - # Deprecated: Use ::activateNextItem - showNextItem: -> - deprecate("Use PaneView::activateNextItem instead") - @activateNextItem() - - # Deprecated: Use ::activatePreviousItem - showPreviousItem: -> - deprecate("Use PaneView::activatePreviousItem instead") - @activatePreviousItem() - - onActiveStatusChanged: (active) => - if active - @trigger 'pane:became-active' - else - @trigger 'pane:became-inactive' - - # Public: Returns the next pane, ordered by creation. - getNextPane: -> - panes = @container?.getPaneViews() - return unless panes.length > 1 - nextIndex = (panes.indexOf(this) + 1) % panes.length - panes[nextIndex] - - getActivePaneItem: -> - @activeItem - - onActiveItemChanged: (item) => - @activeItemDisposables.dispose() if @activeItemDisposables? - @activeItemDisposables = new CompositeDisposable() - - if @previousActiveItem?.off? - @previousActiveItem.off 'title-changed', @activeItemTitleChanged - @previousActiveItem.off 'modified-status-changed', @activeItemModifiedChanged - @previousActiveItem = item - - return unless item? - - if item.onDidChangeTitle? - disposable = item.onDidChangeTitle(@activeItemTitleChanged) - @activeItemDisposables.add(disposable) if disposable?.dispose? - else if item.on? - disposable = item.on('title-changed', @activeItemTitleChanged) - @activeItemDisposables.add(disposable) if disposable?.dispose? - - if item.onDidChangeModified? - disposable = item.onDidChangeModified(@activeItemModifiedChanged) - @activeItemDisposables.add(disposable) if disposable?.dispose? - else if item.on? - item.on('modified-status-changed', @activeItemModifiedChanged) - @activeItemDisposables.add(disposable) if disposable?.dispose? - - @trigger 'pane:active-item-changed', [item] - - onItemAdded: ({item, index}) => - @trigger 'pane:item-added', [item, index] - - onItemRemoved: ({item, index, destroyed}) => - @trigger 'pane:item-removed', [item, index] - - onItemMoved: ({item, newIndex}) => - @trigger 'pane:item-moved', [item, newIndex] - - onBeforeItemDestroyed: ({item}) => - @unsubscribe(item) if typeof item.off is 'function' - @trigger 'pane:before-item-destroyed', [item] - - activeItemTitleChanged: => - @trigger 'pane:active-item-title-changed' - - activeItemModifiedChanged: => - @trigger 'pane:active-item-modified-status-changed' - - @::accessor 'activeView', -> - element = atom.views.getView(@activeItem) - $(element).view() ? element - - splitLeft: (items...) -> atom.views.getView(@model.splitLeft({items})).__spacePenView - - splitRight: (items...) -> atom.views.getView(@model.splitRight({items})).__spacePenView - - splitUp: (items...) -> atom.views.getView(@model.splitUp({items})).__spacePenView - - splitDown: (items...) -> atom.views.getView(@model.splitDown({items})).__spacePenView - - getContainer: -> @closest('atom-pane-container').view() - - focus: -> - @element.focus() From 3414b904a0da71d9dff3495d48a502b26c335776 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:49:17 -0600 Subject: [PATCH 28/78] Remove SelectListView --- exports/atom.coffee | 10 - spec/select-list-view-spec.coffee | 212 -------------------- src/select-list-view.coffee | 312 ------------------------------ 3 files changed, 534 deletions(-) delete mode 100644 spec/select-list-view-spec.coffee delete mode 100644 src/select-list-view.coffee diff --git a/exports/atom.coffee b/exports/atom.coffee index 36bff1967..aa65770b6 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -103,16 +103,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE """ require '../src/scroll-view' - Object.defineProperty module.exports, 'SelectListView', get: -> - deprecate """ - Requiring `SelectListView` from `atom` is no longer supported. - Please require `SelectListView` from `atom-space-pen-view` instead: - `{SelectListView} = require 'atom-space-pen-views'` - Note that the API has changed slightly! Please read the docs at https://github.com/atom/atom-space-pen-views - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - require '../src/select-list-view' - if includeDeprecatedAPIs Object.defineProperty module.exports, 'Git', get: -> deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`" diff --git a/spec/select-list-view-spec.coffee b/spec/select-list-view-spec.coffee deleted file mode 100644 index e22b24f88..000000000 --- a/spec/select-list-view-spec.coffee +++ /dev/null @@ -1,212 +0,0 @@ -SelectListView = require '../src/select-list-view' -{$, $$} = require '../src/space-pen-extensions' - -describe "SelectListView", -> - [selectList, items, list, filterEditorView] = [] - - beforeEach -> - items = [ - ["A", "Alpha"], ["B", "Bravo"], ["C", "Charlie"], - ["D", "Delta"], ["E", "Echo"], ["F", "Foxtrot"] - ] - - selectList = new SelectListView - selectList.setMaxItems(4) - selectList.getFilterKey = -> 1 - selectList.viewForItem = (item) -> - $$ -> @li item[1], class: item[0] - - selectList.confirmed = jasmine.createSpy('confirmed hook') - selectList.cancelled = jasmine.createSpy('cancelled hook') - - selectList.setItems(items) - {list, filterEditorView} = selectList - - describe "when an array is assigned", -> - it "populates the list with up to maxItems items, based on the liForElement function", -> - expect(list.find('li').length).toBe selectList.maxItems - expect(list.find('li:eq(0)')).toHaveText 'Alpha' - expect(list.find('li:eq(0)')).toHaveClass 'A' - - describe "viewForItem(item)", -> - it "allows raw DOM elements to be returned", -> - selectList.viewForItem = (item) -> - li = document.createElement('li') - li.classList.add(item[0]) - li.innerText = item[1] - li - - selectList.setItems(items) - - expect(list.find('li').length).toBe selectList.maxItems - expect(list.find('li:eq(0)')).toHaveText 'Alpha' - expect(list.find('li:eq(0)')).toHaveClass 'A' - expect(selectList.getSelectedItem()).toBe items[0] - - it "allows raw HTML to be returned", -> - selectList.viewForItem = (item) -> - "
  • #{item}
  • " - - selectList.setItems(['Bermuda', 'Bahama']) - - expect(list.find('li:eq(0)')).toHaveText 'Bermuda' - expect(selectList.getSelectedItem()).toBe 'Bermuda' - - describe "when the text of the mini editor changes", -> - beforeEach -> - selectList.attachToDom() - - it "filters the elements in the list based on the scoreElement function and selects the first item", -> - filterEditorView.getEditor().insertText('la') - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li').length).toBe 2 - expect(list.find('li:contains(Alpha)')).toExist() - expect(list.find('li:contains(Delta)')).toExist() - expect(list.find('li:first')).toHaveClass 'selected' - expect(selectList.error).not.toBeVisible() - - it "displays an error if there are no matches, removes error when there are matches", -> - filterEditorView.getEditor().insertText('nothing will match this') - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li').length).toBe 0 - expect(selectList.error).not.toBeHidden() - - filterEditorView.getEditor().setText('la') - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li').length).toBe 2 - expect(selectList.error).not.toBeVisible() - - it "displays no elements until the array has been set on the list", -> - selectList.items = null - selectList.list.empty() - filterEditorView.getEditor().insertText('la') - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li').length).toBe 0 - expect(selectList.error).toBeHidden() - selectList.setItems(items) - expect(list.find('li').length).toBe 2 - - describe "when core:move-up / core:move-down are triggered on the filterEditorView", -> - it "selects the previous / next item in the list, or wraps around to the other side", -> - expect(list.find('li:first')).toHaveClass 'selected' - - filterEditorView.trigger 'core:move-up' - - expect(list.find('li:first')).not.toHaveClass 'selected' - expect(list.find('li:last')).toHaveClass 'selected' - - filterEditorView.trigger 'core:move-down' - - expect(list.find('li:first')).toHaveClass 'selected' - expect(list.find('li:last')).not.toHaveClass 'selected' - - filterEditorView.trigger 'core:move-down' - - expect(list.find('li:eq(0)')).not.toHaveClass 'selected' - expect(list.find('li:eq(1)')).toHaveClass 'selected' - - filterEditorView.trigger 'core:move-down' - - expect(list.find('li:eq(1)')).not.toHaveClass 'selected' - expect(list.find('li:eq(2)')).toHaveClass 'selected' - - filterEditorView.trigger 'core:move-up' - - expect(list.find('li:eq(2)')).not.toHaveClass 'selected' - expect(list.find('li:eq(1)')).toHaveClass 'selected' - - it "scrolls to keep the selected item in view", -> - selectList.attachToDom() - itemHeight = list.find('li').outerHeight() - list.height(itemHeight * 2) - - filterEditorView.trigger 'core:move-down' - filterEditorView.trigger 'core:move-down' - expect(list.scrollBottom()).toBe itemHeight * 3 - - filterEditorView.trigger 'core:move-down' - expect(list.scrollBottom()).toBe itemHeight * 4 - - filterEditorView.trigger 'core:move-up' - filterEditorView.trigger 'core:move-up' - expect(list.scrollTop()).toBe itemHeight - - describe "the core:confirm event", -> - describe "when there is an item selected (because the list in not empty)", -> - it "triggers the selected hook with the selected array element", -> - filterEditorView.trigger 'core:move-down' - filterEditorView.trigger 'core:move-down' - filterEditorView.trigger 'core:confirm' - expect(selectList.confirmed).toHaveBeenCalledWith(items[2]) - - describe "when there is no item selected (because the list is empty)", -> - beforeEach -> - selectList.attachToDom() - - it "does not trigger the confirmed hook", -> - filterEditorView.getEditor().insertText("i will never match anything") - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li')).not.toExist() - filterEditorView.trigger 'core:confirm' - expect(selectList.confirmed).not.toHaveBeenCalled() - - it "does trigger the cancelled hook", -> - filterEditorView.getEditor().insertText("i will never match anything") - window.advanceClock(selectList.inputThrottle) - - expect(list.find('li')).not.toExist() - filterEditorView.trigger 'core:confirm' - expect(selectList.cancelled).toHaveBeenCalled() - - describe "when a list item is clicked", -> - it "selects the item on mousedown and confirms it on mouseup", -> - item = list.find('li:eq(1)') - - item.mousedown() - expect(item).toHaveClass 'selected' - item.mouseup() - - expect(selectList.confirmed).toHaveBeenCalledWith(items[1]) - - describe "the core:cancel event", -> - it "triggers the cancelled hook and detaches and empties the select list", -> - spyOn(selectList, 'detach') - filterEditorView.trigger 'core:cancel' - expect(selectList.cancelled).toHaveBeenCalled() - expect(selectList.detach).toHaveBeenCalled() - expect(selectList.list).toBeEmpty() - - describe "when the mini editor loses focus", -> - it "triggers the cancelled hook and detaches the select list", -> - spyOn(selectList, 'detach') - filterEditorView.trigger 'blur' - expect(selectList.cancelled).toHaveBeenCalled() - expect(selectList.detach).toHaveBeenCalled() - - describe "the core:move-to-top event", -> - it "scrolls to the top, selects the first element, and does not bubble the event", -> - selectList.attachToDom() - moveToTopHandler = jasmine.createSpy("moveToTopHandler") - selectList.parent().on 'core:move-to-top', moveToTopHandler - - selectList.trigger 'core:move-down' - expect(list.find('li:eq(1)')).toHaveClass 'selected' - selectList.trigger 'core:move-to-top' - expect(list.find('li:first')).toHaveClass 'selected' - expect(moveToTopHandler).not.toHaveBeenCalled() - - describe "the core:move-to-bottom event", -> - it "scrolls to the bottom, selects the last element, and does not bubble the event", -> - selectList.attachToDom() - moveToBottomHandler = jasmine.createSpy("moveToBottomHandler") - selectList.parent().on 'core:move-to-bottom', moveToBottomHandler - - expect(list.find('li:first')).toHaveClass 'selected' - selectList.trigger 'core:move-to-bottom' - expect(list.find('li:last')).toHaveClass 'selected' - expect(moveToBottomHandler).not.toHaveBeenCalled() diff --git a/src/select-list-view.coffee b/src/select-list-view.coffee deleted file mode 100644 index e3966af01..000000000 --- a/src/select-list-view.coffee +++ /dev/null @@ -1,312 +0,0 @@ -{$, View} = require './space-pen-extensions' -TextEditorView = require './text-editor-view' -fuzzyFilter = require('fuzzaldrin').filter - -# Deprecated: Provides a view that renders a list of items with an editor that -# filters the items. Used by many packages such as the fuzzy-finder, -# command-palette, symbols-view and autocomplete. -# -# Subclasses must implement the following methods: -# -# * {::viewForItem} -# * {::confirmed} -# -# ## Requiring in packages -# -# ```coffee -# {SelectListView} = require 'atom' -# -# class MySelectListView extends SelectListView -# initialize: -> -# super -# @addClass('overlay from-top') -# @setItems(['Hello', 'World']) -# atom.workspaceView.append(this) -# @focusFilterEditor() -# -# viewForItem: (item) -> -# "
  • #{item}
  • " -# -# confirmed: (item) -> -# console.log("#{item} was selected") -# ``` -module.exports = -class SelectListView extends View - @content: -> - @div class: 'select-list', => - @subview 'filterEditorView', new TextEditorView(mini: true) - @div class: 'error-message', outlet: 'error' - @div class: 'loading', outlet: 'loadingArea', => - @span class: 'loading-message', outlet: 'loading' - @span class: 'badge', outlet: 'loadingBadge' - @ol class: 'list-group', outlet: 'list' - - maxItems: Infinity - scheduleTimeout: null - inputThrottle: 50 - cancelling: false - - ### - Section: Construction - ### - - # Essential: Initialize the select list view. - # - # This method can be overridden by subclasses but `super` should always - # be called. - initialize: -> - @filterEditorView.getEditor().getBuffer().onDidChange => - @schedulePopulateList() - @filterEditorView.on 'blur', => - @cancel() unless @cancelling - - # This prevents the focusout event from firing on the filter editor view - # when the list is scrolled by clicking the scrollbar and dragging. - @list.on 'mousedown', ({target}) => - false if target is @list[0] - - @on 'core:move-up', => - @selectPreviousItemView() - @on 'core:move-down', => - @selectNextItemView() - @on 'core:move-to-top', => - @selectItemView(@list.find('li:first')) - @list.scrollToTop() - false - @on 'core:move-to-bottom', => - @selectItemView(@list.find('li:last')) - @list.scrollToBottom() - false - - @on 'core:confirm', => @confirmSelection() - @on 'core:cancel', => @cancel() - - @list.on 'mousedown', 'li', (e) => - @selectItemView($(e.target).closest('li')) - e.preventDefault() - - @list.on 'mouseup', 'li', (e) => - @confirmSelection() if $(e.target).closest('li').hasClass('selected') - e.preventDefault() - - ### - Section: Methods that must be overridden - ### - - # Essential: Create a view for the given model item. - # - # This method must be overridden by subclasses. - # - # This is called when the item is about to appended to the list view. - # - # * `item` The model item being rendered. This will always be one of the items - # previously passed to {::setItems}. - # - # Returns a String of HTML, DOM element, jQuery object, or View. - viewForItem: (item) -> - throw new Error("Subclass must implement a viewForItem(item) method") - - # Essential: Callback function for when an item is selected. - # - # This method must be overridden by subclasses. - # - # * `item` The selected model item. This will always be one of the items - # previously passed to {::setItems}. - # - # Returns a DOM element, jQuery object, or {View}. - confirmed: (item) -> - throw new Error("Subclass must implement a confirmed(item) method") - - ### - Section: Managing the list of items - ### - - # Essential: Set the array of items to display in the list. - # - # This should be model items not actual views. {::viewForItem} will be - # called to render the item when it is being appended to the list view. - # - # * `items` The {Array} of model items to display in the list (default: []). - setItems: (@items=[]) -> - @populateList() - @setLoading() - - # Essential: Get the model item that is currently selected in the list view. - # - # Returns a model item. - getSelectedItem: -> - @getSelectedItemView().data('select-list-item') - - # Extended: Get the property name to use when filtering items. - # - # This method may be overridden by classes to allow fuzzy filtering based - # on a specific property of the item objects. - # - # For example if the objects you pass to {::setItems} are of the type - # `{"id": 3, "name": "Atom"}` then you would return `"name"` from this method - # to fuzzy filter by that property when text is entered into this view's - # editor. - # - # Returns the property name to fuzzy filter by. - getFilterKey: -> - - # Extended: Get the filter query to use when fuzzy filtering the visible - # elements. - # - # By default this method returns the text in the mini editor but it can be - # overridden by subclasses if needed. - # - # Returns a {String} to use when fuzzy filtering the elements to display. - getFilterQuery: -> - @filterEditorView.getEditor().getText() - - # Extended: Set the maximum numbers of items to display in the list. - # - # * `maxItems` The maximum {Number} of items to display. - setMaxItems: (@maxItems) -> - - # Extended: Populate the list view with the model items previously set by - # calling {::setItems}. - # - # Subclasses may override this method but should always call `super`. - populateList: -> - return unless @items? - - filterQuery = @getFilterQuery() - if filterQuery.length - filteredItems = fuzzyFilter(@items, filterQuery, key: @getFilterKey()) - else - filteredItems = @items - - @list.empty() - if filteredItems.length - @setError(null) - - for i in [0...Math.min(filteredItems.length, @maxItems)] - item = filteredItems[i] - itemView = $(@viewForItem(item)) - itemView.data('select-list-item', item) - @list.append(itemView) - - @selectItemView(@list.find('li:first')) - else - @setError(@getEmptyMessage(@items.length, filteredItems.length)) - - ### - Section: Messages to the user - ### - - # Essential: Set the error message to display. - # - # * `message` The {String} error message (default: ''). - setError: (message='') -> - if message.length is 0 - @error.text('').hide() - else - @setLoading() - @error.text(message).show() - - # Essential: Set the loading message to display. - # - # * `message` The {String} loading message (default: ''). - setLoading: (message='') -> - if message.length is 0 - @loading.text("") - @loadingBadge.text("") - @loadingArea.hide() - else - @setError() - @loading.text(message) - @loadingArea.show() - - # Extended: Get the message to display when there are no items. - # - # Subclasses may override this method to customize the message. - # - # * `itemCount` The {Number} of items in the array specified to {::setItems} - # * `filteredItemCount` The {Number} of items that pass the fuzzy filter test. - # - # Returns a {String} message (default: 'No matches found'). - getEmptyMessage: (itemCount, filteredItemCount) -> 'No matches found' - - ### - Section: View Actions - ### - - # Essential: Cancel and close this select list view. - # - # This restores focus to the previously focused element if - # {::storeFocusedElement} was called prior to this view being attached. - cancel: -> - @list.empty() - @cancelling = true - filterEditorViewFocused = @filterEditorView.isFocused - @cancelled() - @detach() - @restoreFocus() if filterEditorViewFocused - @cancelling = false - clearTimeout(@scheduleTimeout) - - # Extended: Focus the fuzzy filter editor view. - focusFilterEditor: -> - @filterEditorView.focus() - - # Extended: Store the currently focused element. This element will be given - # back focus when {::cancel} is called. - storeFocusedElement: -> - @previouslyFocusedElement = $(document.activeElement) - - ### - Section: Private - ### - - selectPreviousItemView: -> - view = @getSelectedItemView().prev() - view = @list.find('li:last') unless view.length - @selectItemView(view) - - selectNextItemView: -> - view = @getSelectedItemView().next() - view = @list.find('li:first') unless view.length - @selectItemView(view) - - selectItemView: (view) -> - return unless view.length - @list.find('.selected').removeClass('selected') - view.addClass('selected') - @scrollToItemView(view) - - scrollToItemView: (view) -> - scrollTop = @list.scrollTop() - desiredTop = view.position().top + scrollTop - desiredBottom = desiredTop + view.outerHeight() - - if desiredTop < scrollTop - @list.scrollTop(desiredTop) - else if desiredBottom > @list.scrollBottom() - @list.scrollBottom(desiredBottom) - - restoreFocus: -> - if @previouslyFocusedElement?.isOnDom() - @previouslyFocusedElement.focus() - else - atom.views.getView(atom.workspace).focus() - - cancelled: -> - @filterEditorView.getEditor().setText('') - - getSelectedItemView: -> - @list.find('li.selected') - - confirmSelection: -> - item = @getSelectedItem() - if item? - @confirmed(item) - else - @cancel() - - schedulePopulateList: -> - clearTimeout(@scheduleTimeout) - populateCallback = => - @populateList() if @isOnDom() - @scheduleTimeout = setTimeout(populateCallback, @inputThrottle) From 03d13753ee81de459faf096fc0deb12da1464161 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 14:52:34 -0600 Subject: [PATCH 29/78] Remove ScrollView --- exports/atom.coffee | 10 ---------- src/scroll-view.coffee | 38 -------------------------------------- 2 files changed, 48 deletions(-) delete mode 100644 src/scroll-view.coffee diff --git a/exports/atom.coffee b/exports/atom.coffee index aa65770b6..c47fb8a79 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -93,16 +93,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE """ require '../src/text-editor-view' - Object.defineProperty module.exports, 'ScrollView', get: -> - deprecate """ - Requiring `ScrollView` from `atom` is no longer supported. - Please require `ScrollView` from `atom-space-pen-view` instead: - `{ScrollView} = require 'atom-space-pen-views'` - Note that the API has changed slightly! Please read the docs at https://github.com/atom/atom-space-pen-views - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - require '../src/scroll-view' - if includeDeprecatedAPIs Object.defineProperty module.exports, 'Git', get: -> deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`" diff --git a/src/scroll-view.coffee b/src/scroll-view.coffee deleted file mode 100644 index 86743be4c..000000000 --- a/src/scroll-view.coffee +++ /dev/null @@ -1,38 +0,0 @@ -{View} = require './space-pen-extensions' - -# Deprecated: Represents a view that scrolls. -# -# Handles several core events to update scroll position: -# -# * `core:move-up` Scrolls the view up -# * `core:move-down` Scrolls the view down -# * `core:page-up` Scrolls the view up by the height of the page -# * `core:page-down` Scrolls the view down by the height of the page -# * `core:move-to-top` Scrolls the editor to the top -# * `core:move-to-bottom` Scroll the editor to the bottom -# -# Subclasses must call `super` if overriding the `initialize` method. -# -# ## Examples -# -# ```coffee -# {ScrollView} = require 'atom' -# -# class MyView extends ScrollView -# @content: -> -# @div() -# -# initialize: -> -# super -# @text('super long content that will scroll') -# ``` -# -module.exports = -class ScrollView extends View - initialize: -> - @on 'core:move-up', => @scrollUp() - @on 'core:move-down', => @scrollDown() - @on 'core:page-up', => @pageUp() - @on 'core:page-down', => @pageDown() - @on 'core:move-to-top', => @scrollToTop() - @on 'core:move-to-bottom', => @scrollToBottom() From c7ecad5576252d8671b2df09d444c7efe2bd8e2d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 15:36:42 -0600 Subject: [PATCH 30/78] Remove EditorView --- exports/atom.coffee | 18 -- spec/package-manager-spec.coffee | 12 +- spec/spec-helper.coffee | 1 - spec/text-editor-component-spec.coffee | 112 +++++---- src/text-editor-component.coffee | 4 - src/text-editor-element.coffee | 7 - src/text-editor-view.coffee | 324 ------------------------- src/text-editor.coffee | 10 +- 8 files changed, 68 insertions(+), 420 deletions(-) delete mode 100644 src/text-editor-view.coffee diff --git a/exports/atom.coffee b/exports/atom.coffee index c47fb8a79..8e7e639c9 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -75,24 +75,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE """ View - Object.defineProperty module.exports, 'EditorView', get: -> - deprecate """ - Requiring `EditorView` from `atom` is no longer supported. - Please require `TextEditorView` from `atom-space-pen-view` instead: - `{TextEditorView} = require 'atom-space-pen-views'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - require '../src/text-editor-view' - - Object.defineProperty module.exports, 'TextEditorView', get: -> - deprecate """ - Requiring `TextEditorView` from `atom` is no longer supported. - Please require `TextEditorView` from `atom-space-pen-view` instead: - `{TextEditorView} = require 'atom-space-pen-views'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - require '../src/text-editor-view' - if includeDeprecatedAPIs Object.defineProperty module.exports, 'Git', get: -> deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`" diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 1604209d5..cbe65a650 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -220,22 +220,16 @@ describe "PackageManager", -> atom.workspace.open() runs -> - editorView = atom.views.getView(atom.workspace.getActiveTextEditor()).__spacePenView - legacyCommandListener = jasmine.createSpy("legacyCommandListener") - editorView.command 'activation-command', legacyCommandListener + editorElement = atom.views.getView(atom.workspace.getActiveTextEditor()) editorCommandListener = jasmine.createSpy("editorCommandListener") atom.commands.add 'atom-text-editor', 'activation-command', editorCommandListener - atom.commands.dispatch(editorView[0], 'activation-command') + atom.commands.dispatch(editorElement, 'activation-command') expect(mainModule.activate.callCount).toBe 1 - expect(mainModule.legacyActivationCommandCallCount).toBe 1 expect(mainModule.activationCommandCallCount).toBe 1 - expect(legacyCommandListener.callCount).toBe 1 expect(editorCommandListener.callCount).toBe 1 expect(workspaceCommandListener.callCount).toBe 1 - atom.commands.dispatch(editorView[0], 'activation-command') - expect(mainModule.legacyActivationCommandCallCount).toBe 2 + atom.commands.dispatch(editorElement, 'activation-command') expect(mainModule.activationCommandCallCount).toBe 2 - expect(legacyCommandListener.callCount).toBe 2 expect(editorCommandListener.callCount).toBe 2 expect(workspaceCommandListener.callCount).toBe 2 expect(mainModule.activate.callCount).toBe 1 diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index ffb83cd5a..afe24057d 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -19,7 +19,6 @@ Project = require '../src/project' Workspace = require '../src/workspace' ServiceHub = require 'service-hub' TextEditor = require '../src/text-editor' -TextEditorView = require '../src/text-editor-view' TextEditorElement = require '../src/text-editor-element' TokenizedBuffer = require '../src/tokenized-buffer' TextEditorComponent = require '../src/text-editor-component' diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index e0230fbb8..921e77a23 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -1,12 +1,11 @@ _ = require 'underscore-plus' {extend, flatten, toArray, last} = _ -TextEditorView = require '../src/text-editor-view' -TextEditorComponent = require '../src/text-editor-component' +TextEditorElement = require '../src/text-editor-element' nbsp = String.fromCharCode(160) describe "TextEditorComponent", -> - [contentNode, editor, wrapperView, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = [] + [contentNode, editor, wrapperNode, component, componentNode, verticalScrollbarNode, horizontalScrollbarNode] = [] [lineHeightInPixels, charWidth, nextAnimationFrame, noAnimationFrame, tileSize, tileHeightInPixels] = [] beforeEach -> @@ -34,12 +33,13 @@ describe "TextEditorComponent", -> contentNode = document.querySelector('#jasmine-content') contentNode.style.width = '1000px' - wrapperView = new TextEditorView(editor, {tileSize}) - wrapperView.attachToDom() - wrapperNode = wrapperView.element + wrapperNode = new TextEditorElement() + wrapperNode.tileSize = tileSize + wrapperNode.initialize(editor) wrapperNode.setUpdatedSynchronously(false) + jasmine.attachToDOM(wrapperNode) - {component} = wrapperView + {component} = wrapperNode component.setFontFamily('monospace') component.setLineHeight(1.3) component.setFontSize(20) @@ -2384,11 +2384,11 @@ describe "TextEditorComponent", -> inputNode.focus() nextAnimationFrame() expect(componentNode.classList.contains('is-focused')).toBe true - expect(wrapperView.hasClass('is-focused')).toBe true + expect(wrapperNode.classList.contains('is-focused')).toBe true inputNode.blur() nextAnimationFrame() expect(componentNode.classList.contains('is-focused')).toBe false - expect(wrapperView.hasClass('is-focused')).toBe false + expect(wrapperNode.classList.contains('is-focused')).toBe false describe "selection handling", -> cursor = null @@ -2896,17 +2896,18 @@ describe "TextEditorComponent", -> describe "hiding and showing the editor", -> describe "when the editor is hidden when it is mounted", -> it "defers measurement and rendering until the editor becomes visible", -> - wrapperView.remove() + wrapperNode.remove() hiddenParent = document.createElement('div') hiddenParent.style.display = 'none' contentNode.appendChild(hiddenParent) - wrapperView = new TextEditorView(editor, {tileSize}) - wrapperNode = wrapperView.element - wrapperView.appendTo(hiddenParent) + wrapperNode = new TextEditorElement() + wrapperNode.tileSize = tileSize + wrapperNode.initialize(editor) + hiddenParent.appendChild(wrapperNode) - {component} = wrapperView + {component} = wrapperNode componentNode = component.getDomNode() expect(componentNode.querySelectorAll('.line').length).toBe 0 @@ -2917,18 +2918,25 @@ describe "TextEditorComponent", -> describe "when the lineHeight changes while the editor is hidden", -> it "does not attempt to measure the lineHeightInPixels until the editor becomes visible again", -> - wrapperView.hide() + initialLineHeightInPixels = null + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + initialLineHeightInPixels = editor.getLineHeightInPixels() component.setLineHeight(2) expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels describe "when the fontSize changes while the editor is hidden", -> it "does not attempt to measure the lineHeightInPixels or defaultCharWidth until the editor becomes visible again", -> - wrapperView.hide() + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + initialLineHeightInPixels = editor.getLineHeightInPixels() initialCharWidth = editor.getDefaultCharWidth() @@ -2936,17 +2944,22 @@ describe "TextEditorComponent", -> expect(editor.getLineHeightInPixels()).toBe initialLineHeightInPixels expect(editor.getDefaultCharWidth()).toBe initialCharWidth - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + expect(editor.getLineHeightInPixels()).not.toBe initialLineHeightInPixels expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth it "does not re-measure character widths until the editor is shown again", -> - wrapperView.hide() + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() component.setFontSize(22) editor.getBuffer().insert([0, 0], 'a') # regression test against atom/atom#3318 - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + editor.setCursorBufferPosition([0, Infinity]) nextAnimationFrame() @@ -2956,22 +2969,29 @@ describe "TextEditorComponent", -> describe "when the fontFamily changes while the editor is hidden", -> it "does not attempt to measure the defaultCharWidth until the editor becomes visible again", -> - wrapperView.hide() + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + initialLineHeightInPixels = editor.getLineHeightInPixels() initialCharWidth = editor.getDefaultCharWidth() component.setFontFamily('serif') expect(editor.getDefaultCharWidth()).toBe initialCharWidth - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + expect(editor.getDefaultCharWidth()).not.toBe initialCharWidth it "does not re-measure character widths until the editor is shown again", -> - wrapperView.hide() + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() component.setFontFamily('serif') - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + editor.setCursorBufferPosition([0, Infinity]) nextAnimationFrame() @@ -2986,14 +3006,18 @@ describe "TextEditorComponent", -> it "does not re-measure character widths until the editor is shown again", -> atom.config.set('editor.fontFamily', 'sans-serif') - wrapperView.hide() + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + atom.themes.applyStylesheet 'test', """ .function.js { font-weight: bold; } """ - wrapperView.show() + wrapperNode.style.display = '' + component.checkForVisibilityChange() + editor.setCursorBufferPosition([0, Infinity]) nextAnimationFrame() @@ -3004,11 +3028,17 @@ describe "TextEditorComponent", -> describe "when lines are changed while the editor is hidden", -> it "does not measure new characters until the editor is shown again", -> editor.setText('') - wrapperView.hide() + + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + editor.setText('var z = 1') editor.setCursorBufferPosition([0, Infinity]) nextAnimationFrame() - wrapperView.show() + + wrapperNode.style.display = 'none' + component.checkForVisibilityChange() + expect(componentNode.querySelector('.cursor').style['-webkit-transform']).toBe "translate(#{9 * charWidth}px, 0px)" describe "soft wrapping", -> @@ -3138,26 +3168,6 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(componentNode.querySelector('.placeholder-text')).toBeNull() - describe "legacy editor compatibility", -> - it "triggers the screen-lines-changed event before the editor:display-updated event", -> - editor.setSoftWrapped(true) - - callingOrder = [] - editor.onDidChange -> callingOrder.push 'screen-lines-changed' - wrapperView.on 'editor:display-updated', -> callingOrder.push 'editor:display-updated' - editor.insertText("HELLO! HELLO!\n HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! HELLO! ") - nextAnimationFrame() - - expect(callingOrder).toEqual ['screen-lines-changed', 'editor:display-updated', 'editor:display-updated'] - - it "works with the ::setEditorHeightInLines and ::setEditorWidthInChars helpers", -> - setEditorHeightInLines(wrapperView, 7) - nextAnimationFrame() - expect(componentNode.offsetHeight).toBe lineHeightInPixels * 7 - - setEditorWidthInChars(wrapperView, 10) - expect(componentNode.querySelector('.scroll-view').offsetWidth).toBe charWidth * 10 - describe "grammar data attributes", -> it "adds and updates the grammar data attribute based on the current grammar", -> expect(wrapperNode.dataset.grammar).toBe 'source js' @@ -3172,10 +3182,10 @@ describe "TextEditorComponent", -> describe "detaching and reattaching the editor (regression)", -> it "does not throw an exception", -> - wrapperView.detach() - wrapperView.attachToDom() + wrapperNode.remove() + jasmine.attachToDOM(wrapperNode) - wrapperView.trigger('core:move-right') + atom.commands.dispatch(wrapperNode, 'core:move-right') expect(editor.getCursorBufferPosition()).toEqual [0, 1] diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 36f56a877..47b9a1a5c 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -163,10 +163,6 @@ class TextEditorComponent if @editor.isAlive() @updateParentViewFocusedClassIfNeeded() @updateParentViewMiniClass() - if grim.includeDeprecatedAPIs - @hostElement.__spacePenView.trigger 'cursor:moved' if cursorMoved - @hostElement.__spacePenView.trigger 'selection:changed' if selectionChanged - @hostElement.__spacePenView.trigger 'editor:display-updated' readAfterUpdateSync: => @linesComponent.measureCharactersInNewLines() if @isVisible() and not @newState.content.scrollingVertically diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index 55e4e8567..2e3fab355 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -6,7 +6,6 @@ TextBuffer = require 'text-buffer' Grim = require 'grim' TextEditor = require './text-editor' TextEditorComponent = require './text-editor-component' -TextEditorView = null ShadowStyleSheet = null @@ -22,7 +21,6 @@ class TextEditorElement extends HTMLElement createdCallback: -> @emitter = new Emitter @initializeContent() - @createSpacePenShim() if Grim.includeDeprecatedAPIs @addEventListener 'focus', @focused.bind(this) @addEventListener 'blur', @blurred.bind(this) @@ -56,10 +54,6 @@ class TextEditorElement extends HTMLElement @stylesElement = document.head.querySelector('atom-styles') @rootElement = this - createSpacePenShim: -> - TextEditorView ?= require './text-editor-view' - @__spacePenView = new TextEditorView(this) - attachedCallback: -> @buildModel() unless @getModel()? atom.assert(@model.isAlive(), "Attaching a view for a destroyed editor") @@ -90,7 +84,6 @@ class TextEditorElement extends HTMLElement @model.onDidChangeEncoding => @addEncodingAttribute() @model.onDidDestroy => @unmountComponent() @model.onDidChangeMini (mini) => if mini then @addMiniAttribute() else @removeMiniAttribute() - @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs @model getModel: -> diff --git a/src/text-editor-view.coffee b/src/text-editor-view.coffee deleted file mode 100644 index 4d513c6f8..000000000 --- a/src/text-editor-view.coffee +++ /dev/null @@ -1,324 +0,0 @@ -{View, $} = require 'space-pen' -{defaults} = require 'underscore-plus' -TextBuffer = require 'text-buffer' -TextEditor = require './text-editor' -TextEditorElement = require './text-editor-element' -TextEditorComponent = require './text-editor-component' -{deprecate} = require 'grim' - -# Deprecated: Represents the entire visual pane in Atom. -# -# The TextEditorView manages the {TextEditor}, which manages the file buffers. -# `TextEditorView` is intentionally sparse. Most of the things you'll want -# to do are on {TextEditor}. -# -# ## Examples -# -# Requiring in packages -# -# ```coffee -# {TextEditorView} = require 'atom' -# -# miniEditorView = new TextEditorView(mini: true) -# ``` -# -# Iterating over the open editor views -# -# ```coffee -# for editorView in atom.workspaceView.getEditorViews() -# console.log(editorView.getModel().getPath()) -# ``` -# -# Subscribing to every current and future editor -# -# ```coffee -# atom.workspace.eachEditorView (editorView) -> -# console.log(editorView.getModel().getPath()) -# ``` -module.exports = -class TextEditorView extends View - # The constructor for setting up an `TextEditorView` instance. - # - # * `modelOrParams` Either an {TextEditor}, or an object with one property, `mini`. - # If `mini` is `true`, a "miniature" `TextEditor` is constructed. - # Typically, this is ideal for scenarios where you need an Atom editor, - # but without all the chrome, like scrollbars, gutter, _e.t.c._. - # - constructor: (modelOrParams, props) -> - # Handle direct construction with an editor or params - unless modelOrParams instanceof HTMLElement - if modelOrParams instanceof TextEditor - model = modelOrParams - else - {editor, mini, placeholderText, attributes} = modelOrParams - model = editor ? new TextEditor - buffer: new TextBuffer - softWrapped: false - tabLength: 2 - softTabs: true - mini: mini - placeholderText: placeholderText - - element = new TextEditorElement - element.tileSize = props?.tileSize - element.setAttribute(name, value) for name, value of attributes if attributes? - element.setModel(model) - return element.__spacePenView - - # Handle construction with an element - @element = modelOrParams - super - - setModel: (@model) -> - @editor = @model - - @root = $(@element.rootElement) - - @scrollView = @root.find('.scroll-view') - - if atom.config.get('editor.useShadowDOM') - @underlayer = $("
    ").appendTo(this) - @overlayer = $("
    ").appendTo(this) - else - @underlayer = @find('.highlights').addClass('underlayer') - @overlayer = @find('.lines').addClass('overlayer') - - @hiddenInput = @root.find('.hidden-input') - - @hiddenInput.on = (args...) => - args[0] = 'blur' if args[0] is 'focusout' - $::on.apply(this, args) - - @subscribe atom.config.observe 'editor.showLineNumbers', => - @gutter = @root.find('.gutter') - - @gutter.removeClassFromAllLines = (klass) => - deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html') - @gutter.find('.line-number').removeClass(klass) - - @gutter.getLineNumberElement = (bufferRow) => - deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html') - @gutter.find("[data-buffer-row='#{bufferRow}']") - - @gutter.addClassToLine = (bufferRow, klass) => - deprecate('Use decorations instead: http://blog.atom.io/2014/07/24/decorations.html') - lines = @gutter.find("[data-buffer-row='#{bufferRow}']") - lines.addClass(klass) - lines.length > 0 - - find: -> - shadowResult = @root.find.apply(@root, arguments) - if shadowResult.length > 0 - shadowResult - else - super - - # Public: Get the underlying editor model for this view. - # - # Returns an {TextEditor} - getModel: -> @model - - getEditor: -> @model - - Object.defineProperty @prototype, 'lineHeight', get: -> @model.getLineHeightInPixels() - Object.defineProperty @prototype, 'charWidth', get: -> @model.getDefaultCharWidth() - Object.defineProperty @prototype, 'firstRenderedScreenRow', get: -> @component.getRenderedRowRange()[0] - Object.defineProperty @prototype, 'lastRenderedScreenRow', get: -> @component.getRenderedRowRange()[1] - Object.defineProperty @prototype, 'active', get: -> @is(@getPaneView()?.activeView) - Object.defineProperty @prototype, 'isFocused', get: -> document.activeElement is @element or document.activeElement is @element.component?.hiddenInputComponent?.getDomNode() - Object.defineProperty @prototype, 'mini', get: -> @model?.isMini() - Object.defineProperty @prototype, 'component', get: -> @element?.component - - afterAttach: (onDom) -> - return unless onDom - return if @attached - @attached = true - @trigger 'editor:attached', [this] - - beforeRemove: -> - @trigger 'editor:detached', [this] - @trigger 'editor:will-be-removed', [this] - @attached = false - - remove: (selector, keepData) -> - @model.destroy() unless keepData - super - - scrollTop: (scrollTop) -> - if scrollTop? - @model.setScrollTop(scrollTop) - else - @model.getScrollTop() - - scrollLeft: (scrollLeft) -> - if scrollLeft? - @model.setScrollLeft(scrollLeft) - else - @model.getScrollLeft() - - scrollToBottom: -> - deprecate 'Use TextEditor::scrollToBottom instead. You can get the editor via editorView.getModel()' - @model.setScrollBottom(Infinity) - - scrollToScreenPosition: (screenPosition, options) -> - deprecate 'Use TextEditor::scrollToScreenPosition instead. You can get the editor via editorView.getModel()' - @model.scrollToScreenPosition(screenPosition, options) - - scrollToBufferPosition: (bufferPosition, options) -> - deprecate 'Use TextEditor::scrollToBufferPosition instead. You can get the editor via editorView.getModel()' - @model.scrollToBufferPosition(bufferPosition, options) - - scrollToCursorPosition: -> - deprecate 'Use TextEditor::scrollToCursorPosition instead. You can get the editor via editorView.getModel()' - @model.scrollToCursorPosition() - - pixelPositionForBufferPosition: (bufferPosition) -> - deprecate 'Use TextEditorElement::pixelPositionForBufferPosition instead. You can get the editor via editorView.getModel()' - @model.pixelPositionForBufferPosition(bufferPosition, true) - - pixelPositionForScreenPosition: (screenPosition) -> - deprecate 'Use TextEditorElement::pixelPositionForScreenPosition instead. You can get the editor via editorView.getModel()' - @model.pixelPositionForScreenPosition(screenPosition, true) - - appendToLinesView: (view) -> - view.css('position', 'absolute') - view.css('z-index', 1) - @overlayer.append(view) - - splitLeft: -> - deprecate """ - Use Pane::splitLeft instead. - To duplicate this editor into the split use: - editorView.getPaneView().getModel().splitLeft(copyActiveItem: true) - """ - pane = @getPaneView() - pane?.splitLeft(pane?.copyActiveItem()).activeView - - splitRight: -> - deprecate """ - Use Pane::splitRight instead. - To duplicate this editor into the split use: - editorView.getPaneView().getModel().splitRight(copyActiveItem: true) - """ - pane = @getPaneView() - pane?.splitRight(pane?.copyActiveItem()).activeView - - splitUp: -> - deprecate """ - Use Pane::splitUp instead. - To duplicate this editor into the split use: - editorView.getPaneView().getModel().splitUp(copyActiveItem: true) - """ - pane = @getPaneView() - pane?.splitUp(pane?.copyActiveItem()).activeView - - splitDown: -> - deprecate """ - Use Pane::splitDown instead. - To duplicate this editor into the split use: - editorView.getPaneView().getModel().splitDown(copyActiveItem: true) - """ - pane = @getPaneView() - pane?.splitDown(pane?.copyActiveItem()).activeView - - # Public: Get this {TextEditorView}'s {PaneView}. - # - # Returns a {PaneView} - getPaneView: -> - @parent('.item-views').parents('atom-pane').view() - getPane: -> - deprecate 'Use TextEditorView::getPaneView() instead' - @getPaneView() - - show: -> - super - @component?.checkForVisibilityChange() - - hide: -> - super - @component?.checkForVisibilityChange() - - pageDown: -> - deprecate('Use editorView.getModel().pageDown()') - @model.pageDown() - - pageUp: -> - deprecate('Use editorView.getModel().pageUp()') - @model.pageUp() - - getFirstVisibleScreenRow: -> - deprecate 'Use TextEditorElement::getFirstVisibleScreenRow instead.' - @model.getFirstVisibleScreenRow(true) - - getLastVisibleScreenRow: -> - deprecate 'Use TextEditor::getLastVisibleScreenRow instead. You can get the editor via editorView.getModel()' - @model.getLastVisibleScreenRow() - - getFontFamily: -> - deprecate 'This is going away. Use atom.config.get("editor.fontFamily") instead' - @component?.getFontFamily() - - setFontFamily: (fontFamily) -> - deprecate 'This is going away. Use atom.config.set("editor.fontFamily", "my-font") instead' - @component?.setFontFamily(fontFamily) - - getFontSize: -> - deprecate 'This is going away. Use atom.config.get("editor.fontSize") instead' - @component?.getFontSize() - - setFontSize: (fontSize) -> - deprecate 'This is going away. Use atom.config.set("editor.fontSize", 12) instead' - @component?.setFontSize(fontSize) - - setLineHeight: (lineHeight) -> - deprecate 'This is going away. Use atom.config.set("editor.lineHeight", 1.5) instead' - @component.setLineHeight(lineHeight) - - setWidthInChars: (widthInChars) -> - @component.getDOMNode().style.width = (@model.getDefaultCharWidth() * widthInChars) + 'px' - - setShowIndentGuide: (showIndentGuide) -> - deprecate 'This is going away. Use atom.config.set("editor.showIndentGuide", true|false) instead' - atom.config.set("editor.showIndentGuide", showIndentGuide) - - setSoftWrap: (softWrapped) -> - deprecate 'Use TextEditor::setSoftWrapped instead. You can get the editor via editorView.getModel()' - @model.setSoftWrapped(softWrapped) - - setShowInvisibles: (showInvisibles) -> - deprecate 'This is going away. Use atom.config.set("editor.showInvisibles", true|false) instead' - @component.setShowInvisibles(showInvisibles) - - getText: -> - @model.getText() - - setText: (text) -> - @model.setText(text) - - insertText: (text) -> - @model.insertText(text) - - isInputEnabled: -> - @component.isInputEnabled() - - setInputEnabled: (inputEnabled) -> - @component.setInputEnabled(inputEnabled) - - requestDisplayUpdate: -> - deprecate('Please remove from your code. ::requestDisplayUpdate no longer does anything') - - updateDisplay: -> - deprecate('Please remove from your code. ::updateDisplay no longer does anything') - - resetDisplay: -> - deprecate('Please remove from your code. ::resetDisplay no longer does anything') - - redraw: -> - deprecate('Please remove from your code. ::redraw no longer does anything') - - setPlaceholderText: (placeholderText) -> - deprecate('Use TextEditor::setPlaceholderText instead. eg. editorView.getModel().setPlaceholderText(text)') - @model.setPlaceholderText(placeholderText) - - lineElementForScreenRow: (screenRow) -> - $(@component.lineNodeForScreenRow(screenRow)) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index e1b379c39..08c9f4786 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -17,7 +17,8 @@ GutterContainer = require './gutter-container' # Essential: This class represents all essential editing state for a single # {TextBuffer}, including cursor and selection positions, folds, and soft wraps. # If you're manipulating the state of an editor, use this class. If you're -# interested in the visual appearance of editors, use {TextEditorView} instead. +# interested in the visual appearance of editors, use {TextEditorElement} +# instead. # # A single {TextBuffer} can belong to multiple editors. For example, if the # same file is open in two different panes, Atom creates a separate editor for @@ -545,8 +546,8 @@ class TextEditor extends Model # Set the number of characters that can be displayed horizontally in the # editor. # - # * `editorWidthInChars` A {Number} representing the width of the {TextEditorView} - # in characters. + # * `editorWidthInChars` A {Number} representing the width of the + # {TextEditorElement} in characters. setEditorWidthInChars: (editorWidthInChars) -> @displayBuffer.setEditorWidthInChars(editorWidthInChars) @@ -3051,9 +3052,6 @@ if includeDeprecatedAPIs '$verticalScrollbarWidth', '$horizontalScrollbarHeight', '$scrollTop', '$scrollLeft', toProperty: 'displayBuffer' - TextEditor::getViewClass = -> - require './text-editor-view' - TextEditor::joinLine = -> deprecate("Use TextEditor::joinLines() instead") @joinLines() From c4cb690744033ad98f48f6514d54848eb122e86d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 15:47:02 -0600 Subject: [PATCH 31/78] =?UTF-8?q?Drop=20SpacePen=20exports=20from=20?= =?UTF-8?q?=E2=80=98atom=E2=80=99=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- exports/atom.coffee | 51 --------------------------------------------- 1 file changed, 51 deletions(-) diff --git a/exports/atom.coffee b/exports/atom.coffee index 8e7e639c9..2a41b7d70 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -24,57 +24,6 @@ unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE module.exports.Task = require '../src/task' module.exports.TextEditor = require '../src/text-editor' - if includeDeprecatedAPIs - {$, $$, $$$, View} = require '../src/space-pen-extensions' - - Object.defineProperty module.exports, 'Workspace', get: -> - deprecate """ - Requiring `Workspace` from `atom` is no longer supported. - If you need this, please open an issue on - https://github.com/atom/atom/issues/new - And let us know what you are using it for. - """ - require '../src/workspace' - - Object.defineProperty module.exports, '$', get: -> - deprecate """ - Requiring `$` from `atom` is no longer supported. - If you are using `space-pen`, please require `$` from `atom-space-pen-views`. Otherwise require `jquery` instead: - `{$} = require 'atom-space-pen-views'` - or - `$ = require 'jquery'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - Or add `"jquery": "^2"` to your package dependencies. - """ - $ - - Object.defineProperty module.exports, '$$', get: -> - deprecate """ - Requiring `$$` from `atom` is no longer supported. - Please require `atom-space-pen-views` instead: - `{$$} = require 'atom-space-pen-views'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - $$ - - Object.defineProperty module.exports, '$$$', get: -> - deprecate """ - Requiring `$$$` from `atom` is no longer supported. - Please require `atom-space-pen-views` instead: - `{$$$} = require 'atom-space-pen-views'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - $$$ - - Object.defineProperty module.exports, 'View', get: -> - deprecate """ - Requiring `View` from `atom` is no longer supported. - Please require `atom-space-pen-views` instead: - `{View} = require 'atom-space-pen-views'` - Add `"atom-space-pen-views": "^2.0.3"` to your package dependencies. - """ - View - if includeDeprecatedAPIs Object.defineProperty module.exports, 'Git', get: -> deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`" From ed17c5f376cb9f51679b69db45f6f740a2761a24 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 3 Sep 2015 15:49:09 -0600 Subject: [PATCH 32/78] Remove benchmark suite It depends on deprecated APIs and is broken anyway. Can always pull it out of history if we want to revive it. --- benchmark/benchmark-bootstrap.coffee | 12 - benchmark/benchmark-helper.coffee | 115 - benchmark/benchmark-suite.coffee | 221 - benchmark/browser-process-startup.coffee | 56 - benchmark/fixtures/huge.js | 9245 ---------------------- benchmark/fixtures/medium.coffee | 240 - 6 files changed, 9889 deletions(-) delete mode 100644 benchmark/benchmark-bootstrap.coffee delete mode 100644 benchmark/benchmark-helper.coffee delete mode 100644 benchmark/benchmark-suite.coffee delete mode 100755 benchmark/browser-process-startup.coffee delete mode 100644 benchmark/fixtures/huge.js delete mode 100644 benchmark/fixtures/medium.coffee diff --git a/benchmark/benchmark-bootstrap.coffee b/benchmark/benchmark-bootstrap.coffee deleted file mode 100644 index 6e9d6dd1f..000000000 --- a/benchmark/benchmark-bootstrap.coffee +++ /dev/null @@ -1,12 +0,0 @@ -require '../src/window' -Atom = require '../src/atom' -window.atom = Atom.loadOrCreate('spec') -atom.show() unless atom.getLoadSettings().exitWhenDone -window.atom = atom - -{runSpecSuite} = require '../spec/jasmine-helper' - -atom.openDevTools() - -document.title = "Benchmark Suite" -runSpecSuite('../benchmark/benchmark-suite', atom.getLoadSettings().logFile) diff --git a/benchmark/benchmark-helper.coffee b/benchmark/benchmark-helper.coffee deleted file mode 100644 index d831572c5..000000000 --- a/benchmark/benchmark-helper.coffee +++ /dev/null @@ -1,115 +0,0 @@ -require '../spec/spec-helper' - -path = require 'path' -{$} = require '../src/space-pen-extensions' -{Point} = require 'atom' -_ = require 'underscore-plus' -fs = require 'fs-plus' -Project = require '../src/project' -TokenizedBuffer = require '../src/tokenized-buffer' - -defaultCount = 100 -window.pbenchmark = (args...) -> window.benchmark(args..., profile: true) -window.fbenchmark = (args...) -> window.benchmark(args..., focused: true) -window.fpbenchmark = (args...) -> window.benchmark(args..., profile: true, focused: true) -window.pfbenchmark = window.fpbenchmark - -window.benchmarkFixturesProject = new Project(path.join(__dirname, 'fixtures')) - -beforeEach -> - window.project = window.benchmarkFixturesProject - jasmine.unspy(window, 'setTimeout') - jasmine.unspy(window, 'clearTimeout') - jasmine.unspy(TokenizedBuffer::, 'tokenizeInBackground') - -window.benchmark = (args...) -> - description = args.shift() - if typeof args[0] is 'number' - count = args.shift() - else - count = defaultCount - [fn, options] = args - {profile, focused} = (options ? {}) - - method = if focused then fit else it - method description, -> - total = measure -> - console.profile(description) if profile - _.times count, fn - console.profileEnd(description) if profile - avg = total / count - - fullname = @getFullName().replace(/\s|\.$/g, "") - report = "#{fullname}: #{total} / #{count} = #{avg}ms" - console.log(report) - - if atom.getLoadSettings().exitWhenDone - url = "https://github.com/_stats" - data = [type: 'timing', metric: "atom.#{fullname}", ms: avg] - $.ajax url, - async: false - data: JSON.stringify(data) - error: (args...) -> - console.log "Failed to send atom.#{fullname}\n#{JSON.stringify(args)}" - -window.measure = (fn) -> - start = new Date().getTime() - fn() - new Date().getTime() - start - -window.waitsForPromise = (fn) -> - window.waitsFor (moveOn) -> - fn().done(moveOn) - -window.keyIdentifierForKey = (key) -> - if key.length > 1 # named key - key - else - charCode = key.toUpperCase().charCodeAt(0) - "U+00" + charCode.toString(16) - -window.keydownEvent = (key, properties={}) -> - $.Event "keydown", _.extend({originalEvent: {keyIdentifier: keyIdentifierForKey(key)}}, properties) - -window.clickEvent = (properties={}) -> - $.Event "click", properties - -window.mouseEvent = (type, properties) -> - if properties.point - {point, editorView} = properties - {top, left} = @pagePixelPositionForPoint(editorView, point) - properties.pageX = left + 1 - properties.pageY = top + 1 - properties.originalEvent ?= {detail: 1} - $.Event type, properties - -window.mousedownEvent = (properties={}) -> - window.mouseEvent('mousedown', properties) - -window.mousemoveEvent = (properties={}) -> - window.mouseEvent('mousemove', properties) - -window.pagePixelPositionForPoint = (editorView, point) -> - point = Point.fromObject point - top = editorView.lines.offset().top + point.row * editorView.lineHeight - left = editorView.lines.offset().left + point.column * editorView.charWidth - editorView.lines.scrollLeft() - {top, left} - -window.seteditorViewWidthInChars = (editorView, widthInChars, charWidth=editorView.charWidth) -> - editorView.width(charWidth * widthInChars + editorView.lines.position().left) - -$.fn.resultOfTrigger = (type) -> - event = $.Event(type) - this.trigger(event) - event.result - -$.fn.enableKeymap = -> - @on 'keydown', (e) -> window.keymap.handleKeyEvent(e) - -$.fn.attachToDom = -> - $('#jasmine-content').append(this) - -$.fn.textInput = (data) -> - event = document.createEvent 'TextEvent' - event.initTextEvent('textInput', true, true, window, data) - this.each -> this.dispatchEvent(event) diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee deleted file mode 100644 index daee36e9f..000000000 --- a/benchmark/benchmark-suite.coffee +++ /dev/null @@ -1,221 +0,0 @@ -# - -require './benchmark-helper' -{$} = require '../src/space-pen-extensions' -_ = require 'underscore-plus' -{WorkspaceView} = require 'atom' -TokenizedBuffer = require '../src/tokenized-buffer' - -describe "editorView.", -> - editorView = null - - beforeEach -> - atom.workspaceParentSelectorctor = '#jasmine-content' - atom.workspaceView = atom.views.getView(atom.workspace).__spacePenView - atom.workspaceView.attachToDom() - - atom.workspaceView.width(1024) - atom.workspaceView.height(768) - atom.workspaceView.openSync() - editorView = atom.workspaceView.getActiveView() - - afterEach -> - if editorView.pendingDisplayUpdate - waitsFor "editor to finish rendering", (done) -> - editorView.on 'editor:display-updated', done - - describe "keymap.", -> - event = null - - beforeEach -> - event = keydownEvent('x', target: editorView.hiddenInput[0]) - - benchmark "keydown-event-with-no-binding", 10, -> - keymap.handleKeyEvent(event) - - describe "opening-buffers.", -> - benchmark "300-line-file.", -> - buffer = project.bufferForPathSync('medium.coffee') - - describe "empty-file.", -> - benchmark "insert-delete", -> - editorView.insertText('x') - editorView.backspace() - - describe "300-line-file.", -> - beforeEach -> - atom.workspaceView.openSync('medium.coffee') - - describe "at-begining.", -> - benchmark "insert-delete", -> - editorView.insertText('x') - editorView.backspace() - - benchmark "insert-delete-rehighlight", -> - editorView.insertText('"') - editorView.backspace() - - describe "at-end.", -> - beforeEach -> - editorView.moveToBottom() - - benchmark "insert-delete", -> - editorView.insertText('"') - editorView.backspace() - - describe "empty-vs-set-innerHTML.", -> - [firstRow, lastRow] = [] - beforeEach -> - firstRow = editorView.getModel().getFirstVisibleScreenRow() - lastRow = editorView.getModel().getLastVisibleScreenRow() - - benchmark "build-gutter-html.", 1000, -> - editorView.gutter.renderLineNumbers(null, firstRow, lastRow) - - benchmark "set-innerHTML.", 1000, -> - editorView.gutter.renderLineNumbers(null, firstRow, lastRow) - editorView.gutter.lineNumbers[0].innerHtml = '' - - benchmark "empty.", 1000, -> - editorView.gutter.renderLineNumbers(null, firstRow, lastRow) - editorView.gutter.lineNumbers.empty() - - describe "positionLeftForLineAndColumn.", -> - line = null - beforeEach -> - editorView.scrollTop(2000) - editorView.resetDisplay() - line = editorView.lineElementForScreenRow(106)[0] - - describe "one-line.", -> - beforeEach -> - editorView.clearCharacterWidthCache() - - benchmark "uncached", 5000, -> - editorView.positionLeftForLineAndColumn(line, 106, 82) - editorView.clearCharacterWidthCache() - - benchmark "cached", 5000, -> - editorView.positionLeftForLineAndColumn(line, 106, 82) - - describe "multiple-lines.", -> - [firstRow, lastRow] = [] - beforeEach -> - firstRow = editorView.getModel().getFirstVisibleScreenRow() - lastRow = editorView.getModel().getLastVisibleScreenRow() - - benchmark "cache-entire-visible-area", 100, -> - for i in [firstRow..lastRow] - line = editorView.lineElementForScreenRow(i)[0] - editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.getModel().lineTextForBufferRow(i).length)) - - describe "text-rendering.", -> - beforeEach -> - editorView.scrollTop(2000) - - benchmark "resetDisplay", 50, -> - editorView.resetDisplay() - - benchmark "htmlForScreenRows", 1000, -> - lastRow = editorView.getLastScreenRow() - editorView.htmlForScreenRows(0, lastRow) - - benchmark "htmlForScreenRows.htmlParsing", 50, -> - lastRow = editorView.getLastScreenRow() - html = editorView.htmlForScreenRows(0, lastRow) - - div = document.createElement('div') - div.innerHTML = html - - describe "gutter-api.", -> - describe "getLineNumberElementsForClass.", -> - beforeEach -> - editorView.gutter.addClassToLine(20, 'omgwow') - editorView.gutter.addClassToLine(40, 'omgwow') - - benchmark "DOM", 20000, -> - editorView.gutter.getLineNumberElementsForClass('omgwow') - - benchmark "getLineNumberElement.DOM", 20000, -> - editorView.gutter.getLineNumberElement(12) - - benchmark "toggle-class", 2000, -> - editorView.gutter.addClassToLine(40, 'omgwow') - editorView.gutter.removeClassFromLine(40, 'omgwow') - - describe "find-then-unset.", -> - classes = ['one', 'two', 'three', 'four'] - - benchmark "single-class", 200, -> - editorView.gutter.addClassToLine(30, 'omgwow') - editorView.gutter.addClassToLine(40, 'omgwow') - editorView.gutter.removeClassFromAllLines('omgwow') - - benchmark "multiple-class", 200, -> - editorView.gutter.addClassToLine(30, 'one') - editorView.gutter.addClassToLine(30, 'two') - - editorView.gutter.addClassToLine(40, 'two') - editorView.gutter.addClassToLine(40, 'three') - editorView.gutter.addClassToLine(40, 'four') - - for klass in classes - editorView.gutter.removeClassFromAllLines(klass) - - describe "line-htmlification.", -> - div = null - html = null - beforeEach -> - lastRow = editorView.getLastScreenRow() - html = editorView.htmlForScreenRows(0, lastRow) - div = document.createElement('div') - - benchmark "setInnerHTML", 1, -> - div.innerHTML = html - - describe "9000-line-file.", -> - benchmark "opening.", 5, -> - atom.workspaceView.openSync('huge.js') - - describe "after-opening.", -> - beforeEach -> - atom.workspaceView.openSync('huge.js') - - benchmark "moving-to-eof.", 1, -> - editorView.moveToBottom() - - describe "on-first-line.", -> - benchmark "inserting-newline", 5, -> - editorView.insertNewline() - - describe "on-last-visible-line.", -> - beforeEach -> - editorView.setCursorScreenPosition([editorView.getLastVisibleScreenRow(), 0]) - - benchmark "move-down-and-scroll", 300, -> - editorView.trigger 'move-down' - - describe "at-eof.", -> - endPosition = null - - beforeEach -> - editorView.moveToBottom() - endPosition = editorView.getCursorScreenPosition() - - benchmark "move-to-beginning-of-word", -> - editorView.moveToBeginningOfWord() - editorView.setCursorScreenPosition(endPosition) - - benchmark "insert", -> - editorView.insertText('x') - -describe "TokenizedBuffer.", -> - describe "coffee-script-grammar.", -> - [languageMode, buffer] = [] - - beforeEach -> - editor = benchmarkFixturesProject.openSync('medium.coffee') - {languageMode, buffer} = editor - - benchmark "construction", 20, -> - new TokenizedBuffer(buffer, {languageMode, tabLength: 2}) diff --git a/benchmark/browser-process-startup.coffee b/benchmark/browser-process-startup.coffee deleted file mode 100755 index 2b06eaaa4..000000000 --- a/benchmark/browser-process-startup.coffee +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env coffee - -{spawn, exec} = require 'child_process' -fs = require 'fs' -os = require 'os' -path = require 'path' -_ = require 'underscore-plus' -temp = require 'temp' - -directoryToOpen = temp.mkdirSync('browser-process-startup-') -socketPath = path.join(os.tmpdir(), "atom-#{process.env.USER}.sock") -numberOfRuns = 10 - -deleteSocketFile = -> - try - fs.unlinkSync(socketPath) if fs.existsSync(socketPath) - catch error - console.error(error) - -launchAtom = (callback) -> - deleteSocketFile() - - cmd = 'atom' - args = ['--safe', '--new-window', '--foreground', directoryToOpen] - atomProcess = spawn(cmd, args) - - output = '' - startupTimes = [] - dataListener = (data) -> - output += data - if match = /App load time: (\d+)/.exec(output) - startupTime = parseInt(match[1]) - atomProcess.stderr.removeListener 'data', dataListener - atomProcess.kill() - exec 'pkill -9 Atom', (error) -> - console.error(error) if error? - callback(startupTime) - - atomProcess.stderr.on 'data', dataListener - -startupTimes = [] -collector = (startupTime) -> - startupTimes.push(startupTime) - if startupTimes.length < numberOfRuns - launchAtom(collector) - else - maxTime = _.max(startupTimes) - minTime = _.min(startupTimes) - totalTime = startupTimes.reduce (previousValue=0, currentValue) -> previousValue + currentValue - console.log "Startup Runs: #{startupTimes.length}" - console.log "First run time: #{startupTimes[0]}ms" - console.log "Max time: #{maxTime}ms" - console.log "Min time: #{minTime}ms" - console.log "Average time: #{Math.round(totalTime/startupTimes.length)}ms" - -launchAtom(collector) diff --git a/benchmark/fixtures/huge.js b/benchmark/fixtures/huge.js deleted file mode 100644 index 0b1562f14..000000000 --- a/benchmark/fixtures/huge.js +++ /dev/null @@ -1,9245 +0,0 @@ -/*! - * jQuery JavaScript Library v1.7.1 - * http://jquery.com/ - * - * Copyright 2011, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Mon Nov 21 21:11:03 2011 -0500 - */ -(function( window, undefined ) { - -// Use the correct document accordingly with window argument (sandbox) -var document = window.document, - navigator = window.navigator, - location = window.location; -var jQuery = (function() { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // A central reference to the root jQuery(document) - rootjQuery, - - // A simple way to check for HTML strings or ID strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - trimLeft = /^\s+/, - trimRight = /\s+$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // JSON RegExp - rvalidchars = /^[\],:{}\s]*$/, - rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - - // Useragent RegExp - rwebkit = /(webkit)[ \/]([\w.]+)/, - ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, - rmsie = /(msie) ([\w.]+)/, - rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, - - // Matches dashed string for camelizing - rdashAlpha = /-([a-z]|[0-9])/ig, - rmsPrefix = /^-ms-/, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return ( letter + "" ).toUpperCase(); - }, - - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // The deferred used on DOM ready - readyList, - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwn = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - trim = String.prototype.trim, - indexOf = Array.prototype.indexOf, - - // [[Class]] -> type pairs - class2type = {}; - -jQuery.fn = jQuery.prototype = { - constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context && document.body ) { - this.context = document; - this[0] = document.body; - this.selector = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = quickExpr.exec( selector ); - } - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - doc = ( context ? context.ownerDocument || context : document ); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); - selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; - } - - return jQuery.merge( this, selector ); - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.7.1", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = this.constructor(); - - if ( jQuery.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - jQuery.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); - - // Add the callback - readyList.add( fn ); - - return this; - }, - - eq: function( i ) { - i = +i; - return i === -1 ? - this.slice( i ) : - this.slice( i, i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } - - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - // Either a released hold or an DOMready/load event and not yet ready - if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 1 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.fireWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger( "ready" ).off( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyList ) { - return; - } - - readyList = jQuery.Callbacks( "once memory" ); - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - return setTimeout( jQuery.ready, 1 ); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent( "onreadystatechange", DOMContentLoaded ); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray || function( obj ) { - return jQuery.type(obj) === "array"; - }, - - // A crude way of determining if an object is a window - isWindow: function( obj ) { - return obj && typeof obj === "object" && "setInterval" in obj; - }, - - isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - return obj == null ? - String( obj ) : - class2type[ toString.call(obj) ] || "object"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - try { - // Not own constructor property must be Object - if ( obj.constructor && - !hasOwn.call(obj, "constructor") && - !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - } catch ( e ) { - // IE8,9 Will throw exceptions on certain host objects #9897 - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwn.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw new Error( msg ); - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure leading/trailing whitespace is removed (IE can't handle it) - data = jQuery.trim( data ); - - // Attempt to parse using the native JSON parser first - if ( window.JSON && window.JSON.parse ) { - return window.JSON.parse( data ); - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( rvalidchars.test( data.replace( rvalidescape, "@" ) - .replace( rvalidtokens, "]" ) - .replace( rvalidbraces, "")) ) { - - return ( new Function( "return " + data ) )(); - - } - jQuery.error( "Invalid JSON: " + data ); - }, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - try { - if ( window.DOMParser ) { // Standard - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } else { // IE - xml = new ActiveXObject( "Microsoft.XMLDOM" ); - xml.async = "false"; - xml.loadXML( data ); - } - } catch( e ) { - xml = undefined; - } - if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - - // Evaluates a script in a global context - // Workarounds based on findings by Jim Driscoll - // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context - globalEval: function( data ) { - if ( data && rnotwhite.test( data ) ) { - // We use execScript on Internet Explorer - // We use an anonymous function so that context is window - // rather than jQuery in Firefox - ( window.execScript || function( data ) { - window[ "eval" ].call( window, data ); - } )( data ); - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction( object ); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { - break; - } - } - } - } - - return object; - }, - - // Use native String.trim function wherever possible - trim: trim ? - function( text ) { - return text == null ? - "" : - trim.call( text ); - } : - - // Otherwise use our own trimming functionality - function( text ) { - return text == null ? - "" : - text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 - var type = jQuery.type( array ); - - if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array, i ) { - var len; - - if ( array ) { - if ( indexOf ) { - return indexOf.call( array, elem, i ); - } - - len = array.length; - i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; - - for ( ; i < len; i++ ) { - // Skip accessing in sparse arrays - if ( i in array && array[ i ] === elem ) { - return i; - } - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, - j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = [], retVal; - inv = !!inv; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, key, ret = [], - i = 0, - length = elems.length, - // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; - - // Go through the array, translating each of the items to their - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - // Go through every key on the object, - } else { - for ( key in elems ) { - value = callback( elems[ key ], key, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - } - - // Flatten any nested arrays - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - if ( typeof context === "string" ) { - var tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - var args = slice.call( arguments, 2 ), - proxy = function() { - return fn.apply( context, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - - return proxy; - }, - - // Mutifunctional method to get and set values to a collection - // The value/s can optionally be executed if it's a function - access: function( elems, key, value, exec, fn, pass ) { - var length = elems.length; - - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - jQuery.access( elems, k, key[k], exec, fn, value ); - } - return elems; - } - - // Setting one attribute - if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = !pass && exec && jQuery.isFunction(value); - - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - - return elems; - } - - // Getting an attribute - return length ? fn( elems[0], key ) : undefined; - }, - - now: function() { - return ( new Date() ).getTime(); - }, - - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = rwebkit.exec( ua ) || - ropera.exec( ua ) || - rmsie.exec( ua ) || - ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - sub: function() { - function jQuerySub( selector, context ) { - return new jQuerySub.fn.init( selector, context ); - } - jQuery.extend( true, jQuerySub, this ); - jQuerySub.superclass = this; - jQuerySub.fn = jQuerySub.prototype = this(); - jQuerySub.fn.constructor = jQuerySub; - jQuerySub.sub = this.sub; - jQuerySub.fn.init = function init( selector, context ) { - if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { - context = jQuerySub( context ); - } - - return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); - }; - jQuerySub.fn.init.prototype = jQuerySub.fn; - var rootjQuerySub = jQuerySub(document); - return jQuerySub; - }, - - browser: {} -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} - -// IE doesn't match non-breaking spaces with \s -if ( rnotwhite.test( "\xA0" ) ) { - trimLeft = /^[\s\xA0]+/; - trimRight = /[\s\xA0]+$/; -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch(e) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -return jQuery; - -})(); - - -// String to Object flags format cache -var flagsCache = {}; - -// Convert String-formatted flags into Object-formatted ones and store in cache -function createFlags( flags ) { - var object = flagsCache[ flags ] = {}, - i, length; - flags = flags.split( /\s+/ ); - for ( i = 0, length = flags.length; i < length; i++ ) { - object[ flags[i] ] = true; - } - return object; -} - -/* - * Create a callback list using the following parameters: - * - * flags: an optional list of space-separated flags that will change how - * the callback list behaves - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible flags: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( flags ) { - - // Convert flags from String-formatted to Object-formatted - // (we check in cache first) - flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; - - var // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = [], - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Add one or several callbacks to the list - add = function( args ) { - var i, - length, - elem, - type, - actual; - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = jQuery.type( elem ); - if ( type === "array" ) { - // Inspect recursively - add( elem ); - } else if ( type === "function" ) { - // Add if not in unique mode and callback is not in - if ( !flags.unique || !self.has( elem ) ) { - list.push( elem ); - } - } - } - }, - // Fire callbacks - fire = function( context, args ) { - args = args || []; - memory = !flags.memory || [ context, args ]; - firing = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { - memory = true; // Mark as halted - break; - } - } - firing = false; - if ( list ) { - if ( !flags.once ) { - if ( stack && stack.length ) { - memory = stack.shift(); - self.fireWith( memory[ 0 ], memory[ 1 ] ); - } - } else if ( memory === true ) { - self.disable(); - } else { - list = []; - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - var length = list.length; - add( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away, unless previous - // firing was halted (stopOnFalse) - } else if ( memory && memory !== true ) { - firingStart = length; - fire( memory[ 0 ], memory[ 1 ] ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - var args = arguments, - argIndex = 0, - argLength = args.length; - for ( ; argIndex < argLength ; argIndex++ ) { - for ( var i = 0; i < list.length; i++ ) { - if ( args[ argIndex ] === list[ i ] ) { - // Handle firingIndex and firingLength - if ( firing ) { - if ( i <= firingLength ) { - firingLength--; - if ( i <= firingIndex ) { - firingIndex--; - } - } - } - // Remove the element - list.splice( i--, 1 ); - // If we have some unicity property then - // we only need to do this once - if ( flags.unique ) { - break; - } - } - } - } - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - if ( list ) { - var i = 0, - length = list.length; - for ( ; i < length; i++ ) { - if ( fn === list[ i ] ) { - return true; - } - } - } - return false; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory || memory === true ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( stack ) { - if ( firing ) { - if ( !flags.once ) { - stack.push( [ context, args ] ); - } - } else if ( !( flags.once && memory ) ) { - fire( context, args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!memory; - } - }; - - return self; -}; - - - - -var // Static reference to slice - sliceDeferred = [].slice; - -jQuery.extend({ - - Deferred: function( func ) { - var doneList = jQuery.Callbacks( "once memory" ), - failList = jQuery.Callbacks( "once memory" ), - progressList = jQuery.Callbacks( "memory" ), - state = "pending", - lists = { - resolve: doneList, - reject: failList, - notify: progressList - }, - promise = { - done: doneList.add, - fail: failList.add, - progress: progressList.add, - - state: function() { - return state; - }, - - // Deprecated - isResolved: doneList.fired, - isRejected: failList.fired, - - then: function( doneCallbacks, failCallbacks, progressCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); - return this; - }, - always: function() { - deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); - return this; - }, - pipe: function( fnDone, fnFail, fnProgress ) { - return jQuery.Deferred(function( newDefer ) { - jQuery.each( { - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ], - progress: [ fnProgress, "notify" ] - }, function( handler, data ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( jQuery.isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); - } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( obj == null ) { - obj = promise; - } else { - for ( var key in promise ) { - obj[ key ] = promise[ key ]; - } - } - return obj; - } - }, - deferred = promise.promise({}), - key; - - for ( key in lists ) { - deferred[ key ] = lists[ key ].fire; - deferred[ key + "With" ] = lists[ key ].fireWith; - } - - // Handle state - deferred.done( function() { - state = "resolved"; - }, failList.disable, progressList.lock ).fail( function() { - state = "rejected"; - }, doneList.disable, progressList.lock ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( firstParam ) { - var args = sliceDeferred.call( arguments, 0 ), - i = 0, - length = args.length, - pValues = new Array( length ), - count = length, - pCount = length, - deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? - firstParam : - jQuery.Deferred(), - promise = deferred.promise(); - function resolveFunc( i ) { - return function( value ) { - args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - if ( !( --count ) ) { - deferred.resolveWith( deferred, args ); - } - }; - } - function progressFunc( i ) { - return function( value ) { - pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; - deferred.notifyWith( promise, pValues ); - }; - } - if ( length > 1 ) { - for ( ; i < length; i++ ) { - if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); - } else { - --count; - } - } - if ( !count ) { - deferred.resolveWith( deferred, args ); - } - } else if ( deferred !== firstParam ) { - deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); - } - return promise; - } -}); - - - - -jQuery.support = (function() { - - var support, - all, - a, - select, - opt, - input, - marginDiv, - fragment, - tds, - events, - eventName, - i, - isSupported, - div = document.createElement( "div" ), - documentElement = document.documentElement; - - // Preliminary tests - div.setAttribute("className", "t"); - div.innerHTML = "
    a"; - - all = div.getElementsByTagName( "*" ); - a = div.getElementsByTagName( "a" )[ 0 ]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return {}; - } - - // First batch of supports tests - select = document.createElement( "select" ); - opt = select.appendChild( document.createElement("option") ); - input = div.getElementsByTagName( "input" )[ 0 ]; - - support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: ( div.firstChild.nodeType === 3 ), - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText instead) - style: /top/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: ( a.getAttribute("href") === "/a" ), - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: ( input.value === "on" ), - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: opt.selected, - - // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) - getSetAttribute: div.className !== "t", - - // Tests for enctype support on a form(#6743) - enctype: !!document.createElement("form").enctype, - - // Makes sure cloning an html5 element does not cause problems - // Where outerHTML is undefined, this still works - html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", - - // Will be defined later - submitBubbles: true, - changeBubbles: true, - focusinBubbles: false, - deleteExpando: true, - noCloneEvent: true, - inlineBlockNeedsLayout: false, - shrinkWrapBlocks: false, - reliableMarginRight: true - }; - - // Make sure checked status is properly cloned - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; - - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; - - // Test to see if it's possible to delete an expando from an element - // Fails in Internet Explorer - try { - delete div.test; - } catch( e ) { - support.deleteExpando = false; - } - - if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { - div.attachEvent( "onclick", function() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - support.noCloneEvent = false; - }); - div.cloneNode( true ).fireEvent( "onclick" ); - } - - // Check if a radio maintains its value - // after being appended to the DOM - input = document.createElement("input"); - input.value = "t"; - input.setAttribute("type", "radio"); - support.radioValue = input.value === "t"; - - input.setAttribute("checked", "checked"); - div.appendChild( input ); - fragment = document.createDocumentFragment(); - fragment.appendChild( div.lastChild ); - - // WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Check if a disconnected checkbox will retain its checked - // value of true after appended to the DOM (IE6/7) - support.appendChecked = input.checked; - - fragment.removeChild( input ); - fragment.appendChild( div ); - - div.innerHTML = ""; - - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. For more - // info see bug #3333 - // Fails in WebKit before Feb 2011 nightlies - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - if ( window.getComputedStyle ) { - marginDiv = document.createElement( "div" ); - marginDiv.style.width = "0"; - marginDiv.style.marginRight = "0"; - div.style.width = "2px"; - div.appendChild( marginDiv ); - support.reliableMarginRight = - ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; - } - - // Technique from Juriy Zaytsev - // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ - // We only care about the case where non-standard event systems - // are used, namely in IE. Short-circuiting here helps us to - // avoid an eval call (in setAttribute) which can cause CSP - // to go haywire. See: https://developer.mozilla.org/en/Security/CSP - if ( div.attachEvent ) { - for( i in { - submit: 1, - change: 1, - focusin: 1 - }) { - eventName = "on" + i; - isSupported = ( eventName in div ); - if ( !isSupported ) { - div.setAttribute( eventName, "return;" ); - isSupported = ( typeof div[ eventName ] === "function" ); - } - support[ i + "Bubbles" ] = isSupported; - } - } - - fragment.removeChild( div ); - - // Null elements to avoid leaks in IE - fragment = select = opt = marginDiv = div = input = null; - - // Run tests that need a body at doc ready - jQuery(function() { - var container, outer, inner, table, td, offsetSupport, - conMarginTop, ptlm, vb, style, html, - body = document.getElementsByTagName("body")[0]; - - if ( !body ) { - // Return for frameset docs that don't have a body - return; - } - - conMarginTop = 1; - ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;"; - vb = "visibility:hidden;border:0;"; - style = "style='" + ptlm + "border:5px solid #000;padding:0;'"; - html = "
    " + - "" + - "
    "; - - container = document.createElement("div"); - container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; - body.insertBefore( container, body.firstChild ); - - // Construct the test element - div = document.createElement("div"); - container.appendChild( div ); - - // Check if table cells still have offsetWidth/Height when they are set - // to display:none and there are still other visible table cells in a - // table row; if so, offsetWidth/Height are not reliable for use when - // determining if an element has been hidden directly using - // display:none (it is still safe to use offsets if a parent element is - // hidden; don safety goggles and see bug #4512 for more information). - // (only IE 8 fails this test) - div.innerHTML = "
    t
    "; - tds = div.getElementsByTagName( "td" ); - isSupported = ( tds[ 0 ].offsetHeight === 0 ); - - tds[ 0 ].style.display = ""; - tds[ 1 ].style.display = "none"; - - // Check if empty table cells still have offsetWidth/Height - // (IE <= 8 fail this test) - support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); - - // Figure out if the W3C box model works as expected - div.innerHTML = ""; - div.style.width = div.style.paddingLeft = "1px"; - jQuery.boxModel = support.boxModel = div.offsetWidth === 2; - - if ( typeof div.style.zoom !== "undefined" ) { - // Check if natively block-level elements act like inline-block - // elements when setting their display to 'inline' and giving - // them layout - // (IE < 8 does this) - div.style.display = "inline"; - div.style.zoom = 1; - support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 ); - - // Check if elements with layout shrink-wrap their children - // (IE 6 does this) - div.style.display = ""; - div.innerHTML = "
    "; - support.shrinkWrapBlocks = ( div.offsetWidth !== 2 ); - } - - div.style.cssText = ptlm + vb; - div.innerHTML = html; - - outer = div.firstChild; - inner = outer.firstChild; - td = outer.nextSibling.firstChild.firstChild; - - offsetSupport = { - doesNotAddBorder: ( inner.offsetTop !== 5 ), - doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) - }; - - inner.style.position = "fixed"; - inner.style.top = "20px"; - - // safari subtracts parent border width here which is 5px - offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); - inner.style.position = inner.style.top = ""; - - outer.style.overflow = "hidden"; - outer.style.position = "relative"; - - offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); - offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); - - body.removeChild( container ); - div = container = null; - - jQuery.extend( support, offsetSupport ); - }); - - return support; -})(); - - - - -var rbrace = /^(?:\{.*\}|\[.*\])$/, - rmultiDash = /([A-Z])/g; - -jQuery.extend({ - cache: {}, - - // Please use with caution - uuid: 0, - - // Unique for each copy of jQuery on the page - // Non-digits removed to match rinlinejQuery - expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - // Ban all objects except for Flash (which handle expandos) - "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", - "applet": true - }, - - hasData: function( elem ) { - elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; - return !!elem && !isEmptyDataObject( elem ); - }, - - data: function( elem, name, data, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var privateCache, thisCache, ret, - internalKey = jQuery.expando, - getByName = typeof name === "string", - - // We have to handle DOM nodes and JS objects differently because IE6-7 - // can't GC object references properly across the DOM-JS boundary - isNode = elem.nodeType, - - // Only DOM nodes need the global jQuery cache; JS object data is - // attached directly to the object so GC can occur automatically - cache = isNode ? jQuery.cache : elem, - - // Only defining an ID for JS objects if its cache already exists allows - // the code to shortcut on the same path as a DOM node with no cache - id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, - isEvents = name === "events"; - - // Avoid doing any more work than we need to when trying to get data on an - // object that has no data at all - if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { - return; - } - - if ( !id ) { - // Only DOM nodes need a new unique ID for each element since their data - // ends up in the global cache - if ( isNode ) { - elem[ internalKey ] = id = ++jQuery.uuid; - } else { - id = internalKey; - } - } - - if ( !cache[ id ] ) { - cache[ id ] = {}; - - // Avoids exposing jQuery metadata on plain JS objects when the object - // is serialized using JSON.stringify - if ( !isNode ) { - cache[ id ].toJSON = jQuery.noop; - } - } - - // An object can be passed to jQuery.data instead of a key/value pair; this gets - // shallow copied over onto the existing cache - if ( typeof name === "object" || typeof name === "function" ) { - if ( pvt ) { - cache[ id ] = jQuery.extend( cache[ id ], name ); - } else { - cache[ id ].data = jQuery.extend( cache[ id ].data, name ); - } - } - - privateCache = thisCache = cache[ id ]; - - // jQuery data() is stored in a separate object inside the object's internal data - // cache in order to avoid key collisions between internal data and user-defined - // data. - if ( !pvt ) { - if ( !thisCache.data ) { - thisCache.data = {}; - } - - thisCache = thisCache.data; - } - - if ( data !== undefined ) { - thisCache[ jQuery.camelCase( name ) ] = data; - } - - // Users should not attempt to inspect the internal events object using jQuery.data, - // it is undocumented and subject to change. But does anyone listen? No. - if ( isEvents && !thisCache[ name ] ) { - return privateCache.events; - } - - // Check for both converted-to-camel and non-converted data property names - // If a data property was specified - if ( getByName ) { - - // First Try to find as-is property data - ret = thisCache[ name ]; - - // Test for null|undefined property data - if ( ret == null ) { - - // Try to find the camelCased property - ret = thisCache[ jQuery.camelCase( name ) ]; - } - } else { - ret = thisCache; - } - - return ret; - }, - - removeData: function( elem, name, pvt /* Internal Use Only */ ) { - if ( !jQuery.acceptData( elem ) ) { - return; - } - - var thisCache, i, l, - - // Reference to internal data cache key - internalKey = jQuery.expando, - - isNode = elem.nodeType, - - // See jQuery.data for more information - cache = isNode ? jQuery.cache : elem, - - // See jQuery.data for more information - id = isNode ? elem[ internalKey ] : internalKey; - - // If there is already no cache entry for this object, there is no - // purpose in continuing - if ( !cache[ id ] ) { - return; - } - - if ( name ) { - - thisCache = pvt ? cache[ id ] : cache[ id ].data; - - if ( thisCache ) { - - // Support array or space separated string names for data keys - if ( !jQuery.isArray( name ) ) { - - // try the string as a key before any manipulation - if ( name in thisCache ) { - name = [ name ]; - } else { - - // split the camel cased version by spaces unless a key with the spaces exists - name = jQuery.camelCase( name ); - if ( name in thisCache ) { - name = [ name ]; - } else { - name = name.split( " " ); - } - } - } - - for ( i = 0, l = name.length; i < l; i++ ) { - delete thisCache[ name[i] ]; - } - - // If there is no data left in the cache, we want to continue - // and let the cache object itself get destroyed - if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { - return; - } - } - } - - // See jQuery.data for more information - if ( !pvt ) { - delete cache[ id ].data; - - // Don't destroy the parent cache unless the internal data object - // had been the only thing left in it - if ( !isEmptyDataObject(cache[ id ]) ) { - return; - } - } - - // Browsers that fail expando deletion also refuse to delete expandos on - // the window, but it will allow it on all other JS objects; other browsers - // don't care - // Ensure that `cache` is not a window object #10080 - if ( jQuery.support.deleteExpando || !cache.setInterval ) { - delete cache[ id ]; - } else { - cache[ id ] = null; - } - - // We destroyed the cache and need to eliminate the expando on the node to avoid - // false lookups in the cache for entries that no longer exist - if ( isNode ) { - // IE does not allow us to delete expando properties from nodes, - // nor does it have a removeAttribute function on Document nodes; - // we must handle all of these cases - if ( jQuery.support.deleteExpando ) { - delete elem[ internalKey ]; - } else if ( elem.removeAttribute ) { - elem.removeAttribute( internalKey ); - } else { - elem[ internalKey ] = null; - } - } - }, - - // For internal use only. - _data: function( elem, name, data ) { - return jQuery.data( elem, name, data, true ); - }, - - // A method for determining if a DOM node can handle the data expando - acceptData: function( elem ) { - if ( elem.nodeName ) { - var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; - - if ( match ) { - return !(match === true || elem.getAttribute("classid") !== match); - } - } - - return true; - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var parts, attr, name, - data = null; - - if ( typeof key === "undefined" ) { - if ( this.length ) { - data = jQuery.data( this[0] ); - - if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { - attr = this[0].attributes; - for ( var i = 0, l = attr.length; i < l; i++ ) { - name = attr[i].name; - - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.substring(5) ); - - dataAttr( this[0], name, data[ name ] ); - } - } - jQuery._data( this[0], "parsedAttrs", true ); - } - } - - return data; - - } else if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - // Try to fetch any internally stored data first - if ( data === undefined && this.length ) { - data = jQuery.data( this[0], key ); - data = dataAttr( this[0], key, data ); - } - - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - - } else { - return this.each(function() { - var self = jQuery( this ), - args = [ parts[0], value ]; - - self.triggerHandler( "setData" + parts[1] + "!", args ); - jQuery.data( this, key, value ); - self.triggerHandler( "changeData" + parts[1] + "!", args ); - }); - } - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); - -function dataAttr( elem, key, data ) { - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - - var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - jQuery.isNumeric( data ) ? parseFloat( data ) : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - jQuery.data( elem, key, data ); - - } else { - data = undefined; - } - } - - return data; -} - -// checks a cache object for emptiness -function isEmptyDataObject( obj ) { - for ( var name in obj ) { - - // if the public data object is empty, the private is still empty - if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { - continue; - } - if ( name !== "toJSON" ) { - return false; - } - } - - return true; -} - - - - -function handleQueueMarkDefer( elem, type, src ) { - var deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - defer = jQuery._data( elem, deferDataKey ); - if ( defer && - ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && - ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { - // Give room for hard-coded callbacks to fire first - // and eventually mark/queue something else on the element - setTimeout( function() { - if ( !jQuery._data( elem, queueDataKey ) && - !jQuery._data( elem, markDataKey ) ) { - jQuery.removeData( elem, deferDataKey, true ); - defer.fire(); - } - }, 0 ); - } -} - -jQuery.extend({ - - _mark: function( elem, type ) { - if ( elem ) { - type = ( type || "fx" ) + "mark"; - jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); - } - }, - - _unmark: function( force, elem, type ) { - if ( force !== true ) { - type = elem; - elem = force; - force = false; - } - if ( elem ) { - type = type || "fx"; - var key = type + "mark", - count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); - if ( count ) { - jQuery._data( elem, key, count ); - } else { - jQuery.removeData( elem, key, true ); - handleQueueMarkDefer( elem, type, "mark" ); - } - } - }, - - queue: function( elem, type, data ) { - var q; - if ( elem ) { - type = ( type || "fx" ) + "queue"; - q = jQuery._data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !q || jQuery.isArray(data) ) { - q = jQuery._data( elem, type, jQuery.makeArray(data) ); - } else { - q.push( data ); - } - } - return q || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - fn = queue.shift(), - hooks = {}; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - jQuery._data( elem, type + ".run", hooks ); - fn.call( elem, function() { - jQuery.dequeue( elem, type ); - }, hooks ); - } - - if ( !queue.length ) { - jQuery.removeData( elem, type + "queue " + type + ".run", true ); - handleQueueMarkDefer( elem, type, "queue" ); - } - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) { - return jQuery.queue( this[0], type ); - } - return this.each(function() { - var queue = jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, object ) { - if ( typeof type !== "string" ) { - object = type; - type = undefined; - } - type = type || "fx"; - var defer = jQuery.Deferred(), - elements = this, - i = elements.length, - count = 1, - deferDataKey = type + "defer", - queueDataKey = type + "queue", - markDataKey = type + "mark", - tmp; - function resolve() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - } - while( i-- ) { - if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || - ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || - jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && - jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { - count++; - tmp.add( resolve ); - } - } - resolve(); - return defer.promise(); - } -}); - - - - -var rclass = /[\n\t\r]/g, - rspace = /\s+/, - rreturn = /\r/g, - rtype = /^(?:button|input)$/i, - rfocusable = /^(?:button|input|object|select|textarea)$/i, - rclickable = /^a(?:rea)?$/i, - rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, - getSetAttribute = jQuery.support.getSetAttribute, - nodeHook, boolHook, fixSpecified; - -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.attr ); - }, - - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, name, value, true, jQuery.prop ); - }, - - removeProp: function( name ) { - name = jQuery.propFix[ name ] || name; - return this.each(function() { - // try/catch handles cases where IE balks (such as removing a property on window) - try { - this[ name ] = undefined; - delete this[ name ]; - } catch( e ) {} - }); - }, - - addClass: function( value ) { - var classNames, i, l, elem, - setClass, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call(this, j, this.className) ); - }); - } - - if ( value && typeof value === "string" ) { - classNames = value.split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className && classNames.length === 1 ) { - elem.className = value; - - } else { - setClass = " " + elem.className + " "; - - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { - setClass += classNames[ c ] + " "; - } - } - elem.className = jQuery.trim( setClass ); - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classNames, i, l, elem, className, c, cl; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call(this, j, this.className) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - classNames = ( value || "" ).split( rspace ); - - for ( i = 0, l = this.length; i < l; i++ ) { - elem = this[ i ]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - className = (" " + elem.className + " ").replace( rclass, " " ); - for ( c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[ c ] + " ", " "); - } - elem.className = jQuery.trim( className ); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, - isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space seperated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery._data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var self = jQuery(this), val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, self.val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, i, max, option, - index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - i = one ? index : 0; - max = one ? index + 1 : options.length; - for ( ; i < max; i++ ) { - option = options[ i ]; - - // Don't return options that are disabled or in a disabled optgroup - if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && - (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - // Fixes Bug #2551 -- select.val() broken in IE after form.reset() - if ( one && !values.length && options.length ) { - return jQuery( options[ index ] ).val(); - } - - return values; - }, - - set: function( elem, value ) { - var values = jQuery.makeArray( value ); - - jQuery(elem).find("option").each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attr: function( elem, name, value, pass ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - if ( pass && name in jQuery.attrFn ) { - return jQuery( elem )[ name ]( value ); - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === "undefined" ) { - return jQuery.prop( elem, name, value ); - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( notxml ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - return; - - } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, "" + value ); - return value; - } - - } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - - ret = elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var propName, attrNames, name, l, - i = 0; - - if ( value && elem.nodeType === 1 ) { - attrNames = value.toLowerCase().split( rspace ); - l = attrNames.length; - - for ( ; i < l; i++ ) { - name = attrNames[ i ]; - - if ( name ) { - propName = jQuery.propFix[ name ] || name; - - // See #9699 for explanation of this approach (setting first, then removal) - jQuery.attr( elem, name, "" ); - elem.removeAttribute( getSetAttribute ? name : propName ); - - // Set corresponding property to false for boolean attributes - if ( rboolean.test( name ) && propName in elem ) { - elem[ propName ] = false; - } - } - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to it's default in case type is set after value - // This is for element creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - }, - // Use the value property for back compat - // Use the nodeHook for button elements in IE6/7 (#1954) - value: { - get: function( elem, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.get( elem, name ); - } - return name in elem ? - elem.value : - null; - }, - set: function( elem, value, name ) { - if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { - return nodeHook.set( elem, value, name ); - } - // Does not return so that setAttribute is also used - elem.value = value; - } - } - }, - - propFix: { - tabindex: "tabIndex", - readonly: "readOnly", - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder", - contenteditable: "contentEditable" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - return ( elem[ name ] = value ); - } - - } else { - if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - return elem[ name ]; - } - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - var attributeNode = elem.getAttributeNode("tabindex"); - - return attributeNode && attributeNode.specified ? - parseInt( attributeNode.value, 10 ) : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - } - } -}); - -// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) -jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; - -// Hook for boolean attributes -boolHook = { - get: function( elem, name ) { - // Align boolean attributes with corresponding properties - // Fall back to attribute presence where some booleans are not supported - var attrNode, - property = jQuery.prop( elem, name ); - return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? - name.toLowerCase() : - undefined; - }, - set: function( elem, value, name ) { - var propName; - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - // value is true since we know at this point it's type boolean and not false - // Set boolean attributes to the same name and set the DOM property - propName = jQuery.propFix[ name ] || name; - if ( propName in elem ) { - // Only set the IDL specifically if it already exists on the element - elem[ propName ] = true; - } - - elem.setAttribute( name, name.toLowerCase() ); - } - return name; - } -}; - -// IE6/7 do not support getting/setting some attributes with get/setAttribute -if ( !getSetAttribute ) { - - fixSpecified = { - name: true, - id: true - }; - - // Use this for any attribute in IE6/7 - // This fixes almost every IE6/7 issue - nodeHook = jQuery.valHooks.button = { - get: function( elem, name ) { - var ret; - ret = elem.getAttributeNode( name ); - return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? - ret.nodeValue : - undefined; - }, - set: function( elem, value, name ) { - // Set the existing or create a new attribute node - var ret = elem.getAttributeNode( name ); - if ( !ret ) { - ret = document.createAttribute( name ); - elem.setAttributeNode( ret ); - } - return ( ret.nodeValue = value + "" ); - } - }; - - // Apply the nodeHook to tabindex - jQuery.attrHooks.tabindex.set = nodeHook.set; - - // Set width and height to auto instead of 0 on empty string( Bug #8150 ) - // This is for removals - jQuery.each([ "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - set: function( elem, value ) { - if ( value === "" ) { - elem.setAttribute( name, "auto" ); - return value; - } - } - }); - }); - - // Set contenteditable to false on removals(#10429) - // Setting to empty string throws an error as an invalid value - jQuery.attrHooks.contenteditable = { - get: nodeHook.get, - set: function( elem, value, name ) { - if ( value === "" ) { - value = "false"; - } - nodeHook.set( elem, value, name ); - } - }; -} - - -// Some attributes require a special call on IE -if ( !jQuery.support.hrefNormalized ) { - jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { - jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { - get: function( elem ) { - var ret = elem.getAttribute( name, 2 ); - return ret === null ? undefined : ret; - } - }); - }); -} - -if ( !jQuery.support.style ) { - jQuery.attrHooks.style = { - get: function( elem ) { - // Return undefined in the case of empty string - // Normalize to lowercase since IE uppercases css property names - return elem.style.cssText.toLowerCase() || undefined; - }, - set: function( elem, value ) { - return ( elem.style.cssText = "" + value ); - } - }; -} - -// Safari mis-reports the default selected property of an option -// Accessing the parent's selectedIndex property fixes it -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { - get: function( elem ) { - var parent = elem.parentNode; - - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - return null; - } - }); -} - -// IE6/7 call enctype encoding -if ( !jQuery.support.enctype ) { - jQuery.propFix.enctype = "encoding"; -} - -// Radios and checkboxes getter/setter -if ( !jQuery.support.checkOn ) { - jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - get: function( elem ) { - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - } - }; - }); -} -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }); -}); - - - - -var rformElems = /^(?:textarea|input|select)$/i, - rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, - rhoverHack = /\bhover(\.\S+)?\b/, - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, - quickParse = function( selector ) { - var quick = rquickIs.exec( selector ); - if ( quick ) { - // 0 1 2 3 - // [ _, tag, id, class ] - quick[1] = ( quick[1] || "" ).toLowerCase(); - quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); - } - return quick; - }, - quickIs = function( elem, m ) { - var attrs = elem.attributes || {}; - return ( - (!m[1] || elem.nodeName.toLowerCase() === m[1]) && - (!m[2] || (attrs.id || {}).value === m[2]) && - (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) - ); - }, - hoverHack = function( events ) { - return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); - }; - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - add: function( elem, types, handler, data, selector ) { - - var elemData, eventHandle, events, - t, tns, type, namespaces, handleObj, - handleObjIn, quick, handlers, special; - - // Don't attach events to noData or text/comment nodes (allow plain objects tho) - if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - events = elemData.events; - if ( !events ) { - elemData.events = events = {}; - } - eventHandle = elemData.handle; - if ( !eventHandle ) { - elemData.handle = eventHandle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; - }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; - } - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = jQuery.trim( hoverHack(types) ).split( " " ); - for ( t = 0; t < types.length; t++ ) { - - tns = rtypenamespace.exec( types[t] ) || []; - type = tns[1]; - namespaces = ( tns[2] || "" ).split( "." ).sort(); - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: tns[1], - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - quick: quickParse( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - handlers = events[ type ]; - if ( !handlers ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener/attachEvent if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, eventHandle ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), - t, tns, type, origType, namespaces, origCount, - j, events, special, handle, eventType, handleObj; - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = jQuery.trim( hoverHack( types || "" ) ).split(" "); - for ( t = 0; t < types.length; t++ ) { - tns = rtypenamespace.exec( types[t] ) || []; - type = origType = tns[1]; - namespaces = tns[2]; - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector? special.delegateType : special.bindType ) || type; - eventType = events[ type ] || []; - origCount = eventType.length; - namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - - // Remove matching events - for ( j = 0; j < eventType.length; j++ ) { - handleObj = eventType[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !namespaces || namespaces.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - eventType.splice( j--, 1 ); - - if ( handleObj.selector ) { - eventType.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( eventType.length === 0 && origCount !== eventType.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - handle = elemData.handle; - if ( handle ) { - handle.elem = null; - } - - // removeData also checks for emptiness and clears the expando if empty - // so use it instead of delete - jQuery.removeData( elem, [ "events", "handle" ], true ); - } - }, - - // Events that are safe to short-circuit if no handlers are attached. - // Native DOM events should not be added, they may have inline handlers. - customEvent: { - "getData": true, - "setData": true, - "changeData": true - }, - - trigger: function( event, data, elem, onlyHandlers ) { - // Don't do events on text and comment nodes - if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { - return; - } - - // Event object or event type - var type = event.type || event, - namespaces = [], - cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf( "!" ) >= 0 ) { - // Exclusive events trigger only for the exact event (no namespaces) - type = type.slice(0, -1); - exclusive = true; - } - - if ( type.indexOf( "." ) >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - - if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { - // No jQuery handlers for this event type, and it can't have inline handlers - return; - } - - // Caller can pass in an Event, Object, or just an event type string - event = typeof event === "object" ? - // jQuery.Event object - event[ jQuery.expando ] ? event : - // Object literal - new jQuery.Event( type, event ) : - // Just the event type (string) - new jQuery.Event( type ); - - event.type = type; - event.isTrigger = true; - event.exclusive = exclusive; - event.namespace = namespaces.join( "." ); - event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; - ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; - - // Handle a global trigger - if ( !elem ) { - - // TODO: Stop taunting the data cache; remove global events and always attach to document - cache = jQuery.cache; - for ( i in cache ) { - if ( cache[ i ].events && cache[ i ].events[ type ] ) { - jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); - } - } - return; - } - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data != null ? jQuery.makeArray( data ) : []; - data.unshift( event ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - eventPath = [[ elem, special.bindType || type ]]; - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; - old = null; - for ( ; cur; cur = cur.parentNode ) { - eventPath.push([ cur, bubbleType ]); - old = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( old && old === elem.ownerDocument ) { - eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); - } - } - - // Fire handlers on the event path - for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { - - cur = eventPath[i][0]; - event.type = eventPath[i][1]; - - handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - // Note that this is a bare JS function and not a jQuery handler - handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { - event.preventDefault(); - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && - !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Can't use an .isFunction() check here because IE6/7 fails that test. - // Don't do default actions on window, that's where global variables be (#6170) - // IE<9 dies on focus/blur to hidden element (#1486) - if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - old = elem[ ontype ]; - - if ( old ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( old ) { - elem[ ontype ] = old; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event || window.event ); - - var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), - delegateCount = handlers.delegateCount, - args = [].slice.call( arguments, 0 ), - run_all = !event.exclusive && !event.namespace, - handlerQueue = [], - i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Determine handlers that should run if there are delegated events - // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox (#3861) - if ( delegateCount && !event.target.disabled && !(event.button && event.type === "click") ) { - - // Pregenerate a single jQuery object for reuse with .is() - jqcur = jQuery(this); - jqcur.context = this.ownerDocument || this; - - for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { - selMatch = {}; - matches = []; - jqcur[0] = cur; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - sel = handleObj.selector; - - if ( selMatch[ sel ] === undefined ) { - selMatch[ sel ] = ( - handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) - ); - } - if ( selMatch[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, matches: matches }); - } - } - } - - // Add the remaining (directly-bound) handlers - if ( handlers.length > delegateCount ) { - handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); - } - - // Run delegates first; they may want to stop propagation beneath us - for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { - matched = handlerQueue[ i ]; - event.currentTarget = matched.elem; - - for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { - handleObj = matched.matches[ j ]; - - // Triggered event must either 1) be non-exclusive and have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { - - event.data = handleObj.data; - event.handleObj = handleObj; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - return event.result; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** - props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button, - fromElement = original.fromElement; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && fromElement ) { - event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, - originalEvent = event, - fixHook = jQuery.event.fixHooks[ event.type ] || {}, - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = jQuery.Event( originalEvent ); - - for ( i = copy.length; i; ) { - prop = copy[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) - if ( !event.target ) { - event.target = originalEvent.srcElement || document; - } - - // Target should not be a text node (#504, Safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) - if ( event.metaKey === undefined ) { - event.metaKey = event.ctrlKey; - } - - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady - }, - - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - - focus: { - delegateType: "focusin" - }, - blur: { - delegateType: "focusout" - }, - - beforeunload: { - setup: function( data, namespaces, eventHandle ) { - // We only want to do this special case on windows - if ( jQuery.isWindow( this ) ) { - this.onbeforeunload = eventHandle; - } - }, - - teardown: function( namespaces, eventHandle ) { - if ( this.onbeforeunload === eventHandle ) { - this.onbeforeunload = null; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -// Some plugins are using, but it's undocumented/deprecated and will be removed. -// The 1.7 special event interface should provide all the hooks needed now. -jQuery.event.handle = jQuery.event.dispatch; - -jQuery.removeEvent = document.removeEventListener ? - function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } - } : - function( elem, type, handle ) { - if ( elem.detachEvent ) { - elem.detachEvent( "on" + type, handle ); - } - }; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - - // otherwise set the returnValue property of the original event to false (IE) - } else { - e.returnValue = false; - } - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var target = this, - related = event.relatedTarget, - handleObj = event.handleObj, - selector = handleObj.selector, - ret; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// IE submit delegation -if ( !jQuery.support.submitBubbles ) { - - jQuery.event.special.submit = { - setup: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Lazy-add a submit handler when a descendant form may potentially be submitted - jQuery.event.add( this, "click._submit keypress._submit", function( e ) { - // Node name check avoids a VML-related crash in IE (#9807) - var elem = e.target, - form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; - if ( form && !form._submit_attached ) { - jQuery.event.add( form, "submit._submit", function( event ) { - // If form was submitted by the user, bubble the event up the tree - if ( this.parentNode && !event.isTrigger ) { - jQuery.event.simulate( "submit", this.parentNode, event, true ); - } - }); - form._submit_attached = true; - } - }); - // return undefined since we don't need an event listener - }, - - teardown: function() { - // Only need this for delegated form submit events - if ( jQuery.nodeName( this, "form" ) ) { - return false; - } - - // Remove delegated handlers; cleanData eventually reaps submit handlers attached above - jQuery.event.remove( this, "._submit" ); - } - }; -} - -// IE change delegation and checkbox/radio fix -if ( !jQuery.support.changeBubbles ) { - - jQuery.event.special.change = { - - setup: function() { - - if ( rformElems.test( this.nodeName ) ) { - // IE doesn't fire change on a check/radio until blur; trigger it on click - // after a propertychange. Eat the blur-change in special.change.handle. - // This still fires onchange a second time for check/radio after blur. - if ( this.type === "checkbox" || this.type === "radio" ) { - jQuery.event.add( this, "propertychange._change", function( event ) { - if ( event.originalEvent.propertyName === "checked" ) { - this._just_changed = true; - } - }); - jQuery.event.add( this, "click._change", function( event ) { - if ( this._just_changed && !event.isTrigger ) { - this._just_changed = false; - jQuery.event.simulate( "change", this, event, true ); - } - }); - } - return false; - } - // Delegated event; lazy-add a change handler on descendant inputs - jQuery.event.add( this, "beforeactivate._change", function( e ) { - var elem = e.target; - - if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { - jQuery.event.add( elem, "change._change", function( event ) { - if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { - jQuery.event.simulate( "change", this.parentNode, event, true ); - } - }); - elem._change_attached = true; - } - }); - }, - - handle: function( event ) { - var elem = event.target; - - // Swallow native change events from checkbox/radio, we already triggered them above - if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { - return event.handleObj.handler.apply( this, arguments ); - } - }, - - teardown: function() { - jQuery.event.remove( this, "._change" ); - - return rformElems.test( this.nodeName ); - } - }; -} - -// Create "bubbling" focus and blur events -if ( !jQuery.support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); - } - }, - teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on.call( this, types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - var handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace? handleObj.type + "." + handleObj.namespace : handleObj.type, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( var type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - bind: function( types, data, fn ) { - return this.on( types, null, data, fn ); - }, - unbind: function( types, fn ) { - return this.off( types, null, fn ); - }, - - live: function( types, data, fn ) { - jQuery( this.context ).on( types, this.selector, data, fn ); - return this; - }, - die: function( types, fn ) { - jQuery( this.context ).off( types, this.selector || "**", fn ); - return this; - }, - - delegate: function( selector, types, data, fn ) { - return this.on( types, selector, data, fn ); - }, - undelegate: function( selector, types, fn ) { - // ( namespace ) or ( selector, types [, fn] ) - return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - if ( this[0] ) { - return jQuery.event.trigger( type, data, this[0], true ); - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, - guid = fn.guid || jQuery.guid++, - i = 0, - toggler = function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - }; - - // link all the functions, so any of them can unbind this click handler - toggler.guid = guid; - while ( i < args.length ) { - args[ i++ ].guid = guid; - } - - return this.click( toggler ); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( data, fn ) { - if ( fn == null ) { - fn = data; - data = null; - } - - return arguments.length > 0 ? - this.on( name, null, data, fn ) : - this.trigger( name ); - }; - - if ( jQuery.attrFn ) { - jQuery.attrFn[ name ] = true; - } - - if ( rkeyEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; - } - - if ( rmouseEvent.test( name ) ) { - jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; - } -}); - - - -/*! - * Sizzle CSS Selector Engine - * Copyright 2011, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - expando = "sizcache" + (Math.random() + '').replace('.', ''), - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true, - rBackslash = /\\/g, - rReturn = /\r\n/g, - rNonWord = /\W/; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function() { - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function( selector, context, results, seed ) { - results = results || []; - context = context || document; - - var origContext = context; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var m, set, checkSet, extra, ret, cur, pop, i, - prune = true, - contextXML = Sizzle.isXML( context ), - parts = [], - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - do { - chunker.exec( "" ); - m = chunker.exec( soFar ); - - if ( m ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - } while ( m ); - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context, seed ); - - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set, seed ); - } - } - - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - - ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? - Sizzle.filter( ret.expr, ret.set )[0] : - ret.set[0]; - } - - if ( context ) { - ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - - set = ret.expr ? - Sizzle.filter( ret.expr, ret.set ) : - ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray( set ); - - } else { - prune = false; - } - - while ( parts.length ) { - cur = parts.pop(); - pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - - } else if ( context && context.nodeType === 1 ) { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - - } else { - for ( i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function( results ) { - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort( sortOrder ); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[ i - 1 ] ) { - results.splice( i--, 1 ); - } - } - } - } - - return results; -}; - -Sizzle.matches = function( expr, set ) { - return Sizzle( expr, null, null, set ); -}; - -Sizzle.matchesSelector = function( node, expr ) { - return Sizzle( expr, null, null, [node] ).length > 0; -}; - -Sizzle.find = function( expr, context, isXML ) { - var set, i, len, match, type, left; - - if ( !expr ) { - return []; - } - - for ( i = 0, len = Expr.order.length; i < len; i++ ) { - type = Expr.order[i]; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - left = match[1]; - match.splice( 1, 1 ); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace( rBackslash, "" ); - set = Expr.find[ type ]( match, context, isXML ); - - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = typeof context.getElementsByTagName !== "undefined" ? - context.getElementsByTagName( "*" ) : - []; - } - - return { set: set, expr: expr }; -}; - -Sizzle.filter = function( expr, set, inplace, not ) { - var match, anyFound, - type, found, item, filter, left, - i, pass, - old = expr, - result = [], - curLoop = set, - isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); - - while ( expr && set.length ) { - for ( type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - filter = Expr.filter[ type ]; - left = match[1]; - - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - pass = not ^ found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - - } else { - curLoop[i] = false; - } - - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Utility function for retreiving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -var getText = Sizzle.getText = function( elem ) { - var i, node, - nodeType = elem.nodeType, - ret = ""; - - if ( nodeType ) { - if ( nodeType === 1 || nodeType === 9 ) { - // Use textContent || innerText for elements - if ( typeof elem.textContent === 'string' ) { - return elem.textContent; - } else if ( typeof elem.innerText === 'string' ) { - // Replace IE's carriage returns - return elem.innerText.replace( rReturn, '' ); - } else { - // Traverse it's children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - } else { - - // If no nodeType, this is expected to be an array - for ( i = 0; (node = elem[i]); i++ ) { - // Do not traverse comment nodes - if ( node.nodeType !== 8 ) { - ret += getText( node ); - } - } - } - return ret; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - - match: { - ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - - leftMatch: {}, - - attrMap: { - "class": "className", - "for": "htmlFor" - }, - - attrHandle: { - href: function( elem ) { - return elem.getAttribute( "href" ); - }, - type: function( elem ) { - return elem.getAttribute( "type" ); - } - }, - - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !rNonWord.test( part ), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - - ">": function( checkSet, part ) { - var elem, - isPartStr = typeof part === "string", - i = 0, - l = checkSet.length; - - if ( isPartStr && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - - } else { - for ( ; i < l; i++ ) { - elem = checkSet[i]; - - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - - "": function(checkSet, part, isXML){ - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); - }, - - "~": function( checkSet, part, isXML ) { - var nodeCheck, - doneName = done++, - checkFn = dirCheck; - - if ( typeof part === "string" && !rNonWord.test( part ) ) { - part = part.toLowerCase(); - nodeCheck = part; - checkFn = dirNodeCheck; - } - - checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); - } - }, - - find: { - ID: function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; - } - }, - - NAME: function( match, context ) { - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], - results = context.getElementsByName( match[1] ); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - - TAG: function( match, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( match[1] ); - } - } - }, - preFilter: { - CLASS: function( match, curLoop, inplace, result, not, isXML ) { - match = " " + match[1].replace( rBackslash, "" ) + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - - ID: function( match ) { - return match[1].replace( rBackslash, "" ); - }, - - TAG: function( match, curLoop ) { - return match[1].replace( rBackslash, "" ).toLowerCase(); - }, - - CHILD: function( match ) { - if ( match[1] === "nth" ) { - if ( !match[2] ) { - Sizzle.error( match[0] ); - } - - match[2] = match[2].replace(/^\+|\s*/g, ''); - - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - else if ( match[2] ) { - Sizzle.error( match[0] ); - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - - ATTR: function( match, curLoop, inplace, result, not, isXML ) { - var name = match[1] = match[1].replace( rBackslash, "" ); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - // Handle if an un-quoted value was used - match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - - PSEUDO: function( match, curLoop, inplace, result, not ) { - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - - if ( !inplace ) { - result.push.apply( result, ret ); - } - - return false; - } - - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - - POS: function( match ) { - match.unshift( true ); - - return match; - } - }, - - filters: { - enabled: function( elem ) { - return elem.disabled === false && elem.type !== "hidden"; - }, - - disabled: function( elem ) { - return elem.disabled === true; - }, - - checked: function( elem ) { - return elem.checked === true; - }, - - selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - parent: function( elem ) { - return !!elem.firstChild; - }, - - empty: function( elem ) { - return !elem.firstChild; - }, - - has: function( elem, i, match ) { - return !!Sizzle( match[3], elem ).length; - }, - - header: function( elem ) { - return (/h\d/i).test( elem.nodeName ); - }, - - text: function( elem ) { - var attr = elem.getAttribute( "type" ), type = elem.type; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case - return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); - }, - - radio: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; - }, - - checkbox: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; - }, - - file: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; - }, - - password: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; - }, - - submit: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "submit" === elem.type; - }, - - image: function( elem ) { - return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; - }, - - reset: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && "reset" === elem.type; - }, - - button: function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && "button" === elem.type || name === "button"; - }, - - input: function( elem ) { - return (/input|select|textarea|button/i).test( elem.nodeName ); - }, - - focus: function( elem ) { - return elem === elem.ownerDocument.activeElement; - } - }, - setFilters: { - first: function( elem, i ) { - return i === 0; - }, - - last: function( elem, i, match, array ) { - return i === array.length - 1; - }, - - even: function( elem, i ) { - return i % 2 === 0; - }, - - odd: function( elem, i ) { - return i % 2 === 1; - }, - - lt: function( elem, i, match ) { - return i < match[3] - 0; - }, - - gt: function( elem, i, match ) { - return i > match[3] - 0; - }, - - nth: function( elem, i, match ) { - return match[3] - 0 === i; - }, - - eq: function( elem, i, match ) { - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function( elem, match, i, array ) { - var name = match[1], - filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; - - } else if ( name === "not" ) { - var not = match[3]; - - for ( var j = 0, l = not.length; j < l; j++ ) { - if ( not[j] === elem ) { - return false; - } - } - - return true; - - } else { - Sizzle.error( name ); - } - }, - - CHILD: function( elem, match ) { - var first, last, - doneName, parent, cache, - count, diff, - type = match[1], - node = elem; - - switch ( type ) { - case "only": - case "first": - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - if ( type === "first" ) { - return true; - } - - node = elem; - - case "last": - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - - return true; - - case "nth": - first = match[2]; - last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - doneName = match[0]; - parent = elem.parentNode; - - if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { - count = 0; - - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - - parent[ expando ] = doneName; - } - - diff = elem.nodeIndex - last; - - if ( first === 0 ) { - return diff === 0; - - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - - ID: function( elem, match ) { - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - - TAG: function( elem, match ) { - return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; - }, - - CLASS: function( elem, match ) { - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - - ATTR: function( elem, match ) { - var name = match[1], - result = Sizzle.attr ? - Sizzle.attr( elem, name ) : - Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - !type && Sizzle.attr ? - result != null : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - - POS: function( elem, match, i, array ) { - var name = match[2], - filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS, - fescape = function(all, num){ - return "\\" + (num - 0 + 1); - }; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); -} - -var makeArray = function( array, results ) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -// Also verifies that the returned array holds DOM nodes -// (which is not the case in the Blackberry browser) -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; - -// Provide a fallback method if it does not work -} catch( e ) { - makeArray = function( array, results ) { - var i = 0, - ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - - } else { - if ( typeof array.length === "number" ) { - for ( var l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - - } else { - for ( ; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder, siblingCheck; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - return a.compareDocumentPosition ? -1 : 1; - } - - return a.compareDocumentPosition(b) & 4 ? -1 : 1; - }; - -} else { - sortOrder = function( a, b ) { - // The nodes are identical, we can exit early - if ( a === b ) { - hasDuplicate = true; - return 0; - - // Fallback to using sourceIndex (in IE) if it's available on both nodes - } else if ( a.sourceIndex && b.sourceIndex ) { - return a.sourceIndex - b.sourceIndex; - } - - var al, bl, - ap = [], - bp = [], - aup = a.parentNode, - bup = b.parentNode, - cur = aup; - - // If the nodes are siblings (or identical) we can do a quick check - if ( aup === bup ) { - return siblingCheck( a, b ); - - // If no parents were found then the nodes are disconnected - } else if ( !aup ) { - return -1; - - } else if ( !bup ) { - return 1; - } - - // Otherwise they're somewhere else in the tree so we need - // to build up a full list of the parentNodes for comparison - while ( cur ) { - ap.unshift( cur ); - cur = cur.parentNode; - } - - cur = bup; - - while ( cur ) { - bp.unshift( cur ); - cur = cur.parentNode; - } - - al = ap.length; - bl = bp.length; - - // Start walking down the tree looking for a discrepancy - for ( var i = 0; i < al && i < bl; i++ ) { - if ( ap[i] !== bp[i] ) { - return siblingCheck( ap[i], bp[i] ); - } - } - - // We ended someplace up the tree so do a sibling check - return i === al ? - siblingCheck( a, bp[i], -1 ) : - siblingCheck( ap[i], b, 1 ); - }; - - siblingCheck = function( a, b, ret ) { - if ( a === b ) { - return ret; - } - - var cur = a.nextSibling; - - while ( cur ) { - if ( cur === b ) { - return -1; - } - - cur = cur.nextSibling; - } - - return 1; - }; -} - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date()).getTime(), - root = document.documentElement; - - form.innerHTML = ""; - - // Inject it into the root element, check its status, and remove it quickly - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function( match, context, isXML ) { - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - - return m ? - m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? - [m] : - undefined : - []; - } - }; - - Expr.filter.ID = function( elem, match ) { - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - - // release memory in IE - root = form = null; -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function( match, context ) { - var results = context.getElementsByTagName( match[1] ); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = ""; - - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - - Expr.attrHandle.href = function( elem ) { - return elem.getAttribute( "href", 2 ); - }; - } - - // release memory in IE - div = null; -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, - div = document.createElement("div"), - id = "__sizzle__"; - - div.innerHTML = "

    "; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function( query, context, extra, seed ) { - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && !Sizzle.isXML(context) ) { - // See if we find a selector to speed up - var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); - - if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { - // Speed-up: Sizzle("TAG") - if ( match[1] ) { - return makeArray( context.getElementsByTagName( query ), extra ); - - // Speed-up: Sizzle(".CLASS") - } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { - return makeArray( context.getElementsByClassName( match[2] ), extra ); - } - } - - if ( context.nodeType === 9 ) { - // Speed-up: Sizzle("body") - // The body element only exists once, optimize finding it - if ( query === "body" && context.body ) { - return makeArray( [ context.body ], extra ); - - // Speed-up: Sizzle("#ID") - } else if ( match && match[3] ) { - var elem = context.getElementById( match[3] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id === match[3] ) { - return makeArray( [ elem ], extra ); - } - - } else { - return makeArray( [], extra ); - } - } - - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(qsaError) {} - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - var oldContext = context, - old = context.getAttribute( "id" ), - nid = old || id, - hasParent = context.parentNode, - relativeHierarchySelector = /^\s*[+~]/.test( query ); - - if ( !old ) { - context.setAttribute( "id", nid ); - } else { - nid = nid.replace( /'/g, "\\$&" ); - } - if ( relativeHierarchySelector && hasParent ) { - context = context.parentNode; - } - - try { - if ( !relativeHierarchySelector || hasParent ) { - return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); - } - - } catch(pseudoError) { - } finally { - if ( !old ) { - oldContext.removeAttribute( "id" ); - } - } - } - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - // release memory in IE - div = null; - })(); -} - -(function(){ - var html = document.documentElement, - matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; - - if ( matches ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9 fails this) - var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), - pseudoWorks = false; - - try { - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( document.documentElement, "[test!='']:sizzle" ); - - } catch( pseudoError ) { - pseudoWorks = true; - } - - Sizzle.matchesSelector = function( node, expr ) { - // Make sure that attribute selectors are quoted - expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); - - if ( !Sizzle.isXML( node ) ) { - try { - if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { - var ret = matches.call( node, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || !disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9, so check for that - node.document && node.document.nodeType !== 11 ) { - return ret; - } - } - } catch(e) {} - } - - return Sizzle(expr, null, null, [node]).length > 0; - }; - } -})(); - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "
    "; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByClassName actually exists - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByClassName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function( match, context, isXML ) { - if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { - return context.getElementsByClassName(match[1]); - } - }; - - // release memory in IE - div = null; -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - - if ( elem ) { - var match = false; - - elem = elem[dir]; - - while ( elem ) { - if ( elem[ expando ] === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem[ expando ] = doneName; - elem.sizset = i; - } - - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -if ( document.documentElement.contains ) { - Sizzle.contains = function( a, b ) { - return a !== b && (a.contains ? a.contains(b) : true); - }; - -} else if ( document.documentElement.compareDocumentPosition ) { - Sizzle.contains = function( a, b ) { - return !!(a.compareDocumentPosition(b) & 16); - }; - -} else { - Sizzle.contains = function() { - return false; - }; -} - -Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function( selector, context, seed ) { - var match, - tmpSet = [], - later = "", - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet, seed ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -// Override sizzle attribute retrieval -Sizzle.attr = jQuery.attr; -Sizzle.selectors.attrMap = {}; -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.filters; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - -})(); - - -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - isSimple = /^.[^:#\[\.,]*$/, - slice = Array.prototype.slice, - POS = jQuery.expr.match.POS, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.fn.extend({ - find: function( selector ) { - var self = this, - i, l; - - if ( typeof selector !== "string" ) { - return jQuery( selector ).filter(function() { - for ( i = 0, l = self.length; i < l; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }); - } - - var ret = this.pushStack( "", "find", selector ), - length, n, r; - - for ( i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( n = length; n < ret.length; n++ ) { - for ( r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && ( - typeof selector === "string" ? - // If this is a positional selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - POS.test( selector ) ? - jQuery( selector, this.context ).index( this[0] ) >= 0 : - jQuery.filter( selector, this ).length > 0 : - this.filter( selector ).length > 0 ); - }, - - closest: function( selectors, context ) { - var ret = [], i, l, cur = this[0]; - - // Array (deprecated as of jQuery 1.7) - if ( jQuery.isArray( selectors ) ) { - var level = 1; - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( i = 0; i < selectors.length; i++ ) { - - if ( jQuery( cur ).is( selectors[ i ] ) ) { - ret.push({ selector: selectors[ i ], elem: cur, level: level }); - } - } - - cur = cur.parentNode; - level++; - } - - return ret; - } - - // String - var pos = POS.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( i = 0, l = this.length; i < l; i++ ) { - cur = this[i]; - - while ( cur ) { - if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { - ret.push( cur ); - break; - - } else { - cur = cur.parentNode; - if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { - break; - } - } - } - } - - ret = ret.length > 1 ? jQuery.unique( ret ) : ret; - - return this.pushStack( ret, "closest", selectors ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return jQuery.inArray( this[0], jQuery( elem ) ); - } - - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( elem.parentNode.firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, slice.call( arguments ).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 ? - jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : - jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], - cur = elem[ dir ]; - - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, keep ) { - - // Can't pass null or undefined to indexOf in Firefox 4 - // Set to 0 to skip string check - qualifier = qualifier || 0; - - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - var retVal = !!qualifier.call( elem, i, elem ); - return retVal === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return ( elem === qualifier ) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; - }); -} - - - - -function createSafeFragment( document ) { - var list = nodeNames.split( "|" ), - safeFrag = document.createDocumentFragment(); - - if ( safeFrag.createElement ) { - while ( list.length ) { - safeFrag.createElement( - list.pop() - ); - } - } - return safeFrag; -} - -var nodeNames = "abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" + - "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", - rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, - rtagName = /<([\w:]+)/, - rtbody = /", "" ], - legend: [ 1, "
    ", "
    " ], - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - col: [ 2, "", "
    " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }, - safeFragment = createSafeFragment( document ); - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and