diff --git a/src/core/public/chrome/recently_accessed/persisted_log.test.ts b/src/core/public/chrome/recently_accessed/persisted_log.test.ts index 4229efdf7ca9d..345ad8f3a1f5a 100644 --- a/src/core/public/chrome/recently_accessed/persisted_log.test.ts +++ b/src/core/public/chrome/recently_accessed/persisted_log.test.ts @@ -28,12 +28,6 @@ const createMockStorage = () => ({ length: 0, }); -jest.mock('ui/chrome', () => { - return { - getBasePath: () => `/some/base/path`, - }; -}); - const historyName = 'testHistory'; const historyLimit = 10; const payload = [ diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 74e1ec5e2b4ed..486c8563c5456 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -59,7 +59,6 @@ export default { '@elastic/eui/lib/(.*)?': '/node_modules/@elastic/eui/test-env/$1', '^src/plugins/(.*)': '/src/plugins/$1', '^plugins/([^/.]*)(.*)': '/src/legacy/core_plugins/$1/public$2', - '^ui/(.*)': '/src/legacy/ui/public/$1', '^uiExports/(.*)': '/src/dev/jest/mocks/file_mock.js', '^test_utils/(.*)': '/src/test_utils/public/$1', '^fixtures/(.*)': '/src/fixtures/$1', diff --git a/src/dev/jest/setup/mocks.js b/src/dev/jest/setup/mocks.js index 6e7160e858cd7..cea28d8abdbd1 100644 --- a/src/dev/jest/setup/mocks.js +++ b/src/dev/jest/setup/mocks.js @@ -34,10 +34,6 @@ * The mocks that are enabled that way live inside the `__mocks__` folders beside their implementation files. */ -jest.mock('ui/metadata'); -jest.mock('ui/documentation_links/documentation_links'); -jest.mock('ui/chrome'); - jest.mock('moment-timezone', () => { // We always want to mock the timezone moment-timezone guesses, since otherwise // test results might be depending on which time zone you are running them. diff --git a/src/fixtures/stubbed_logstash_index_pattern.js b/src/fixtures/stubbed_logstash_index_pattern.js index 5bb926799fcf6..5735b01eb3db4 100644 --- a/src/fixtures/stubbed_logstash_index_pattern.js +++ b/src/fixtures/stubbed_logstash_index_pattern.js @@ -21,7 +21,12 @@ import StubIndexPattern from 'test_utils/stub_index_pattern'; import stubbedLogstashFields from 'fixtures/logstash_fields'; import { getKbnFieldType } from '../plugins/data/common'; -import { npSetup } from '../legacy/ui/public/new_platform/new_platform.karma_mock'; +import { uiSettingsServiceMock } from '../core/public/ui_settings/ui_settings_service.mock'; + +const uiSettingSetupMock = uiSettingsServiceMock.createSetupContract(); +uiSettingSetupMock.get.mockImplementation((item, defaultValue) => { + return defaultValue; +}); export default function stubbedLogstashIndexPatternService() { const mockLogstashFields = stubbedLogstashFields(); @@ -41,13 +46,9 @@ export default function stubbedLogstashIndexPatternService() { }; }); - const indexPattern = new StubIndexPattern( - 'logstash-*', - (cfg) => cfg, - 'time', - fields, - npSetup.core - ); + const indexPattern = new StubIndexPattern('logstash-*', (cfg) => cfg, 'time', fields, { + uiSettings: uiSettingSetupMock, + }); indexPattern.id = 'logstash-*'; indexPattern.isTimeNanosBased = () => false; diff --git a/src/legacy/ui/public/.eslintrc b/src/legacy/ui/public/.eslintrc deleted file mode 100644 index cc44af915ba25..0000000000000 --- a/src/legacy/ui/public/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -rules: - no-console: 2 - 'import/no-default-export': error diff --git a/src/legacy/ui/public/__mocks__/metadata.ts b/src/legacy/ui/public/__mocks__/metadata.ts deleted file mode 100644 index b7f944de27463..0000000000000 --- a/src/legacy/ui/public/__mocks__/metadata.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { metadata as metadataImpl } from '../metadata'; - -export const metadata: typeof metadataImpl = { - branch: 'jest-metadata-mock-branch', - version: '42.23.26', -}; diff --git a/src/legacy/ui/public/__tests__/events.js b/src/legacy/ui/public/__tests__/events.js deleted file mode 100644 index c225c2a8ac1c0..0000000000000 --- a/src/legacy/ui/public/__tests__/events.js +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; -import { EventsProvider } from '../events'; -import expect from '@kbn/expect'; -import '../private'; -import { createDefer } from 'ui/promises'; -import { createLegacyClass } from '../utils/legacy_class'; - -describe('Events', function () { - require('test_utils/no_digest_promises').activateForSuite(); - - let Events; - let Promise; - let eventsInstance; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function ($injector, Private) { - Promise = $injector.get('Promise'); - Events = Private(EventsProvider); - eventsInstance = new Events(); - }) - ); - - it('should handle on events', function () { - const obj = new Events(); - const prom = obj.on('test', function (message) { - expect(message).to.equal('Hello World'); - }); - - obj.emit('test', 'Hello World'); - - return prom; - }); - - it('should work with inherited objects', function () { - createLegacyClass(MyEventedObject).inherits(Events); - function MyEventedObject() { - MyEventedObject.Super.call(this); - } - const obj = new MyEventedObject(); - - const prom = obj.on('test', function (message) { - expect(message).to.equal('Hello World'); - }); - - obj.emit('test', 'Hello World'); - - return prom; - }); - - it('should clear events when off is called', function () { - const obj = new Events(); - obj.on('test', _.noop); - expect(obj._listeners).to.have.property('test'); - expect(obj._listeners.test).to.have.length(1); - obj.off(); - expect(obj._listeners).to.not.have.property('test'); - }); - - it('should clear a specific handler when off is called for an event', function () { - const obj = new Events(); - const handler1 = sinon.stub(); - const handler2 = sinon.stub(); - obj.on('test', handler1); - obj.on('test', handler2); - expect(obj._listeners).to.have.property('test'); - obj.off('test', handler1); - - return obj.emit('test', 'Hello World').then(function () { - sinon.assert.calledOnce(handler2); - sinon.assert.notCalled(handler1); - }); - }); - - it('should clear a all handlers when off is called for an event', function () { - const obj = new Events(); - const handler1 = sinon.stub(); - obj.on('test', handler1); - expect(obj._listeners).to.have.property('test'); - obj.off('test'); - expect(obj._listeners).to.not.have.property('test'); - - return obj.emit('test', 'Hello World').then(function () { - sinon.assert.notCalled(handler1); - }); - }); - - it('should handle multiple identical emits in the same tick', function () { - const obj = new Events(); - const handler1 = sinon.stub(); - - obj.on('test', handler1); - const emits = [obj.emit('test', 'one'), obj.emit('test', 'two'), obj.emit('test', 'three')]; - - return Promise.all(emits).then(function () { - expect(handler1.callCount).to.be(emits.length); - expect(handler1.getCall(0).calledWith('one')).to.be(true); - expect(handler1.getCall(1).calledWith('two')).to.be(true); - expect(handler1.getCall(2).calledWith('three')).to.be(true); - }); - }); - - it('should handle emits from the handler', function () { - const obj = new Events(); - const secondEmit = createDefer(Promise); - - const handler1 = sinon.spy(function () { - if (handler1.calledTwice) { - return; - } - obj.emit('test').then(_.bindKey(secondEmit, 'resolve')); - }); - - obj.on('test', handler1); - - return Promise.all([obj.emit('test'), secondEmit.promise]).then(function () { - expect(handler1.callCount).to.be(2); - }); - }); - - it('should only emit to handlers registered before emit is called', function () { - const obj = new Events(); - const handler1 = sinon.stub(); - const handler2 = sinon.stub(); - - obj.on('test', handler1); - const emits = [obj.emit('test', 'one'), obj.emit('test', 'two'), obj.emit('test', 'three')]; - - return Promise.all(emits).then(function () { - expect(handler1.callCount).to.be(emits.length); - - obj.on('test', handler2); - - const emits2 = [obj.emit('test', 'four'), obj.emit('test', 'five'), obj.emit('test', 'six')]; - - return Promise.all(emits2).then(function () { - expect(handler1.callCount).to.be(emits.length + emits2.length); - expect(handler2.callCount).to.be(emits2.length); - }); - }); - }); - - it('should pass multiple arguments from the emitter', function () { - const obj = new Events(); - const handler = sinon.stub(); - const payload = ['one', { hello: 'tests' }, null]; - - obj.on('test', handler); - - return obj.emit('test', payload[0], payload[1], payload[2]).then(function () { - expect(handler.callCount).to.be(1); - expect(handler.calledWithExactly(payload[0], payload[1], payload[2])).to.be(true); - }); - }); - - it('should preserve the scope of the handler', function () { - const obj = new Events(); - const expected = 'some value'; - let testValue; - - function handler() { - testValue = this.getVal(); - } - handler.getVal = _.constant(expected); - - obj.on('test', handler); - return obj.emit('test').then(function () { - expect(testValue).to.equal(expected); - }); - }); - - it('should always emit in the same order', function () { - const handler = sinon.stub(); - - const obj = new Events(); - obj.on('block', _.partial(handler, 'block')); - obj.on('last', _.partial(handler, 'last')); - - return Promise.all([ - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('block'), - obj.emit('last'), - ]).then(function () { - expect(handler.callCount).to.be(10); - handler.args.forEach(function (args, i) { - expect(args[0]).to.be(i < 9 ? 'block' : 'last'); - }); - }); - }); - - it('calls emitted handlers asynchronously', (done) => { - const listenerStub = sinon.stub(); - eventsInstance.on('test', listenerStub); - eventsInstance.emit('test'); - sinon.assert.notCalled(listenerStub); - - setTimeout(() => { - sinon.assert.calledOnce(listenerStub); - done(); - }, 100); - }); - - it('calling off after an emit that has not yet triggered the handler, will not call the handler', (done) => { - const listenerStub = sinon.stub(); - eventsInstance.on('test', listenerStub); - eventsInstance.emit('test'); - // It's called asynchronously so it shouldn't be called yet. - sinon.assert.notCalled(listenerStub); - eventsInstance.off('test', listenerStub); - - setTimeout(() => { - sinon.assert.notCalled(listenerStub); - done(); - }, 100); - }); -}); diff --git a/src/legacy/ui/public/__tests__/metadata.js b/src/legacy/ui/public/__tests__/metadata.js deleted file mode 100644 index c5051d70849cd..0000000000000 --- a/src/legacy/ui/public/__tests__/metadata.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { metadata } from '../metadata'; -describe('ui/metadata', () => { - it('is immutable', () => { - expect(() => (metadata.foo = 'something')).to.throw; - expect(() => (metadata.version = 'something')).to.throw; - expect(() => (metadata.vars = {})).to.throw; - expect(() => (metadata.vars.kbnIndex = 'something')).to.throw; - }); -}); diff --git a/src/legacy/ui/public/_index.scss b/src/legacy/ui/public/_index.scss deleted file mode 100644 index a441b773d4a4e..0000000000000 --- a/src/legacy/ui/public/_index.scss +++ /dev/null @@ -1,9 +0,0 @@ -// Prefix all styles with "kbn" to avoid conflicts. -// Examples -// kbnChart -// kbnChart__legend -// kbnChart__legend--small -// kbnChart__legend-isLoading - -@import './accessibility/index'; -@import './directives/index'; diff --git a/src/legacy/ui/public/accessibility/__tests__/kbn_accessible_click.js b/src/legacy/ui/public/accessibility/__tests__/kbn_accessible_click.js deleted file mode 100644 index f3b7ab29d8a14..0000000000000 --- a/src/legacy/ui/public/accessibility/__tests__/kbn_accessible_click.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import '../kbn_accessible_click'; -import { keys } from '@elastic/eui'; - -describe('kbnAccessibleClick directive', () => { - let $compile; - let $rootScope; - - beforeEach(ngMock.module('kibana')); - - beforeEach( - ngMock.inject(function (_$compile_, _$rootScope_) { - $compile = _$compile_; - $rootScope = _$rootScope_; - }) - ); - - describe('throws an error', () => { - it('when the element is a button', () => { - const html = ``; - expect(() => { - $compile(html)($rootScope); - }).to.throwError(/kbnAccessibleClick doesn't need to be used on a button./); - }); - - it('when the element is a link with an href', () => { - const html = ``; - expect(() => { - $compile(html)($rootScope); - }).to.throwError( - /kbnAccessibleClick doesn't need to be used on a link if it has a href attribute./ - ); - }); - - it(`when the element doesn't have an ng-click`, () => { - const html = `
`; - expect(() => { - $compile(html)($rootScope); - }).to.throwError(/kbnAccessibleClick requires ng-click to be defined on its element./); - }); - }); - - describe(`doesn't throw an error`, () => { - it('when the element is a link without an href', () => { - const html = ``; - expect(() => { - $compile(html)($rootScope); - }).not.to.throwError(); - }); - }); - - describe('adds accessibility attributes', () => { - it('tabindex', () => { - const html = `
`; - const element = $compile(html)($rootScope); - expect(element.attr('tabindex')).to.be('0'); - }); - - it('role', () => { - const html = `
`; - const element = $compile(html)($rootScope); - expect(element.attr('role')).to.be('button'); - }); - }); - - describe(`doesn't override pre-existing accessibility attributes`, () => { - it('tabindex', () => { - const html = `
`; - const element = $compile(html)($rootScope); - expect(element.attr('tabindex')).to.be('1'); - }); - - it('role', () => { - const html = `
`; - const element = $compile(html)($rootScope); - expect(element.attr('role')).to.be('submit'); - }); - }); - - describe(`calls ng-click`, () => { - let scope; - let element; - - beforeEach(function () { - scope = $rootScope.$new(); - scope.handleClick = sinon.stub(); - const html = `
`; - element = $compile(html)(scope); - }); - - it(`on ENTER keyup`, () => { - const e = angular.element.Event('keyup'); // eslint-disable-line new-cap - e.key = keys.ENTER; - element.trigger(e); - sinon.assert.calledOnce(scope.handleClick); - }); - - it(`on SPACE keyup`, () => { - const e = angular.element.Event('keyup'); // eslint-disable-line new-cap - e.key = keys.SPACE; - element.trigger(e); - sinon.assert.calledOnce(scope.handleClick); - }); - }); -}); diff --git a/src/legacy/ui/public/accessibility/__tests__/kbn_ui_ace_keyboard_mode.js b/src/legacy/ui/public/accessibility/__tests__/kbn_ui_ace_keyboard_mode.js deleted file mode 100644 index ce1bf95bf0fb7..0000000000000 --- a/src/legacy/ui/public/accessibility/__tests__/kbn_ui_ace_keyboard_mode.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import '../kbn_ui_ace_keyboard_mode'; -import { keys } from '@elastic/eui'; - -describe('kbnUiAceKeyboardMode directive', () => { - let element; - - beforeEach(ngMock.module('kibana')); - - beforeEach( - ngMock.inject(($compile, $rootScope) => { - element = $compile(`
`)($rootScope.$new()); - }) - ); - - it('should add the hint element', () => { - expect(element.find('.kbnUiAceKeyboardHint').length).to.be(1); - }); - - describe('hint element', () => { - it('should be tabable', () => { - expect(element.find('.kbnUiAceKeyboardHint').attr('tabindex')).to.be('0'); - }); - - it('should move focus to textbox and be inactive if pressed enter on it', () => { - const textarea = element.find('textarea'); - sinon.spy(textarea[0], 'focus'); - const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap - ev.key = keys.ENTER; - element.find('.kbnUiAceKeyboardHint').trigger(ev); - expect(textarea[0].focus.called).to.be(true); - expect( - element.find('.kbnUiAceKeyboardHint').hasClass('kbnUiAceKeyboardHint-isInactive') - ).to.be(true); - }); - - it('should be shown again, when pressing Escape in ace editor', () => { - const textarea = element.find('textarea'); - const hint = element.find('.kbnUiAceKeyboardHint'); - sinon.spy(hint[0], 'focus'); - const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap - ev.key = keys.ESCAPE; - textarea.trigger(ev); - expect(hint[0].focus.called).to.be(true); - expect(hint.hasClass('kbnUiAceKeyboardHint-isInactive')).to.be(false); - }); - }); - - describe('ui-ace textarea', () => { - it('should not be tabable anymore', () => { - expect(element.find('textarea').attr('tabindex')).to.be('-1'); - }); - }); -}); - -describe('kbnUiAceKeyboardModeService', () => { - let element; - - beforeEach(ngMock.module('kibana')); - - beforeEach( - ngMock.inject(($compile, $rootScope, kbnUiAceKeyboardModeService) => { - const scope = $rootScope.$new(); - element = $compile(`
`)(scope); - kbnUiAceKeyboardModeService.initialize(scope, element); - }) - ); - - it('should add the hint element', () => { - expect(element.find('.kbnUiAceKeyboardHint').length).to.be(1); - }); - - describe('hint element', () => { - it('should be tabable', () => { - expect(element.find('.kbnUiAceKeyboardHint').attr('tabindex')).to.be('0'); - }); - - it('should move focus to textbox and be inactive if pressed enter on it', () => { - const textarea = element.find('textarea'); - sinon.spy(textarea[0], 'focus'); - const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap - ev.key = keys.ENTER; - element.find('.kbnUiAceKeyboardHint').trigger(ev); - expect(textarea[0].focus.called).to.be(true); - expect( - element.find('.kbnUiAceKeyboardHint').hasClass('kbnUiAceKeyboardHint-isInactive') - ).to.be(true); - }); - - it('should be shown again, when pressing Escape in ace editor', () => { - const textarea = element.find('textarea'); - const hint = element.find('.kbnUiAceKeyboardHint'); - sinon.spy(hint[0], 'focus'); - const ev = angular.element.Event('keydown'); // eslint-disable-line new-cap - ev.key = keys.ESCAPE; - textarea.trigger(ev); - expect(hint[0].focus.called).to.be(true); - expect(hint.hasClass('kbnUiAceKeyboardHint-isInactive')).to.be(false); - }); - }); - - describe('ui-ace textarea', () => { - it('should not be tabable anymore', () => { - expect(element.find('textarea').attr('tabindex')).to.be('-1'); - }); - }); -}); diff --git a/src/legacy/ui/public/accessibility/__tests__/scrollto_activedescendant.js b/src/legacy/ui/public/accessibility/__tests__/scrollto_activedescendant.js deleted file mode 100644 index d5ccbf887e79b..0000000000000 --- a/src/legacy/ui/public/accessibility/__tests__/scrollto_activedescendant.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import '../scrollto_activedescendant'; - -describe('scrolltoActivedescendant directive', () => { - let $compile; - let $rootScope; - - beforeEach(ngMock.module('kibana')); - - beforeEach( - ngMock.inject((_$compile_, _$rootScope_) => { - $compile = _$compile_; - $rootScope = _$rootScope_; - }) - ); - - it('should call scrollIntoView on aria-activedescendant changes', () => { - const scope = $rootScope.$new(); - scope.ad = ''; - const element = $compile(`
- - -
`)(scope); - const child1 = element.find('#child1'); - const child2 = element.find('#child2'); - sinon.spy(child1[0], 'scrollIntoView'); - sinon.spy(child2[0], 'scrollIntoView'); - scope.ad = 'child1'; - scope.$digest(); - expect(child1[0].scrollIntoView.calledOnce).to.be.eql(true); - scope.ad = 'child2'; - scope.$digest(); - expect(child2[0].scrollIntoView.calledOnce).to.be.eql(true); - }); -}); diff --git a/src/legacy/ui/public/accessibility/_index.scss b/src/legacy/ui/public/accessibility/_index.scss deleted file mode 100644 index 95062449e4b13..0000000000000 --- a/src/legacy/ui/public/accessibility/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './kbn_ui_ace_keyboard_mode'; diff --git a/src/legacy/ui/public/accessibility/_kbn_ui_ace_keyboard_mode.scss b/src/legacy/ui/public/accessibility/_kbn_ui_ace_keyboard_mode.scss deleted file mode 100644 index 9ace600db4197..0000000000000 --- a/src/legacy/ui/public/accessibility/_kbn_ui_ace_keyboard_mode.scss +++ /dev/null @@ -1,24 +0,0 @@ -.kbnUiAceKeyboardHint { - position: absolute; - top: 0; - bottom: 0; - right: 0; - left: 0; - background: transparentize($euiColorEmptyShade, 0.3); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - text-align: center; - opacity: 0; - - &:focus { - opacity: 1; - border: 2px solid $euiColorPrimary; - z-index: 1000; - } - - &.kbnUiAceKeyboardHint-isInactive { - display: none; - } -} diff --git a/src/legacy/ui/public/accessibility/angular_aria.js b/src/legacy/ui/public/accessibility/angular_aria.js deleted file mode 100644 index 4335eddf0d8cd..0000000000000 --- a/src/legacy/ui/public/accessibility/angular_aria.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import 'angular-aria'; -import { uiModules } from '../modules'; - -/** - * This module will take care of attaching appropriate aria tags related to some angular stuff, - * e.g. it will attach aria-invalid if the model state is set to invalid. - * - * You can find more infos in the official documentation: https://docs.angularjs.org/api/ngAria. - * - * Three settings are disabled: it won't automatically attach `tabindex`, `role=button` or - * handling keyboard events for `ngClick` directives. Kibana uses `kbnAccessibleClick` to handle - * those cases where you need an `ngClick` non button element to have keyboard access. - */ -uiModules.get('kibana', ['ngAria']).config(($ariaProvider) => { - $ariaProvider.config({ - bindKeydown: false, - bindRoleForClick: false, - tabindex: false, - }); -}); diff --git a/src/legacy/ui/public/accessibility/index.js b/src/legacy/ui/public/accessibility/index.js deleted file mode 100644 index 5ff2521421866..0000000000000 --- a/src/legacy/ui/public/accessibility/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './angular_aria'; -import './kbn_accessible_click'; -import './scrollto_activedescendant'; diff --git a/src/legacy/ui/public/accessibility/kbn_accessible_click.js b/src/legacy/ui/public/accessibility/kbn_accessible_click.js deleted file mode 100644 index a57fbc6be82fd..0000000000000 --- a/src/legacy/ui/public/accessibility/kbn_accessible_click.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * Interactive elements must be able to receive focus. - * - * Ideally, this means using elements that are natively keyboard accessible (, - * , or - - -`; diff --git a/src/legacy/ui/public/exit_full_screen/_index.scss b/src/legacy/ui/public/exit_full_screen/_index.scss deleted file mode 100644 index 33dff05e2a687..0000000000000 --- a/src/legacy/ui/public/exit_full_screen/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../plugins/kibana_react/public/exit_full_screen_button/index'; diff --git a/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.test.js b/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.test.js deleted file mode 100644 index d4273c0fdb207..0000000000000 --- a/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.test.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock( - 'ui/chrome', - () => ({ - getKibanaVersion: () => '6.0.0', - setVisible: () => {}, - }), - { virtual: true } -); - -import React from 'react'; -import { mountWithIntl, renderWithIntl } from 'test_utils/enzyme_helpers'; -import sinon from 'sinon'; -import chrome from 'ui/chrome'; - -import { ExitFullScreenButton } from './exit_full_screen_button'; - -import { keys } from '@elastic/eui'; - -test('is rendered', () => { - const component = renderWithIntl( {}} />); - - expect(component).toMatchSnapshot(); -}); - -describe('onExitFullScreenMode', () => { - test('is called when the button is pressed', () => { - const onExitHandler = sinon.stub(); - - const component = mountWithIntl(); - - component.find('button').simulate('click'); - - sinon.assert.calledOnce(onExitHandler); - }); - - test('is called when the ESC key is pressed', () => { - const onExitHandler = sinon.stub(); - - mountWithIntl(); - - const escapeKeyEvent = new KeyboardEvent('keydown', { key: keys.ESCAPE }); - document.dispatchEvent(escapeKeyEvent); - - sinon.assert.calledOnce(onExitHandler); - }); -}); - -describe('chrome.setVisible', () => { - test('is called with false when the component is rendered', () => { - chrome.setVisible = sinon.stub(); - - const component = mountWithIntl( {}} />); - - component.find('button').simulate('click'); - - sinon.assert.calledOnce(chrome.setVisible); - sinon.assert.calledWith(chrome.setVisible, false); - }); - - test('is called with true the component is unmounted', () => { - const component = mountWithIntl( {}} />); - - chrome.setVisible = sinon.stub(); - component.unmount(); - - sinon.assert.calledOnce(chrome.setVisible); - sinon.assert.calledWith(chrome.setVisible, true); - }); -}); diff --git a/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.tsx b/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.tsx deleted file mode 100644 index db4101010f6d6..0000000000000 --- a/src/legacy/ui/public/exit_full_screen/exit_full_screen_button.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { PureComponent } from 'react'; -import chrome from 'ui/chrome'; -import { ExitFullScreenButton as ExitFullScreenButtonUi } from '../../../../plugins/kibana_react/public'; - -/** - * DO NOT USE THIS COMPONENT, IT IS DEPRECATED. - * Use the one in `src/plugins/kibana_react`. - */ - -interface Props { - onExitFullScreenMode: () => void; -} - -export class ExitFullScreenButton extends PureComponent { - public UNSAFE_componentWillMount() { - chrome.setVisible(false); - } - - public componentWillUnmount() { - chrome.setVisible(true); - } - - public render() { - return ; - } -} diff --git a/src/legacy/ui/public/exit_full_screen/index.ts b/src/legacy/ui/public/exit_full_screen/index.ts deleted file mode 100644 index a965fd776e0c2..0000000000000 --- a/src/legacy/ui/public/exit_full_screen/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { ExitFullScreenButton } from './exit_full_screen_button'; diff --git a/src/legacy/ui/public/flot-charts/API.md b/src/legacy/ui/public/flot-charts/API.md deleted file mode 100644 index 699e2500f4942..0000000000000 --- a/src/legacy/ui/public/flot-charts/API.md +++ /dev/null @@ -1,1498 +0,0 @@ -# Flot Reference # - -**Table of Contents** - -[Introduction](#introduction) -| [Data Format](#data-format) -| [Plot Options](#plot-options) -| [Customizing the legend](#customizing-the-legend) -| [Customizing the axes](#customizing-the-axes) -| [Multiple axes](#multiple-axes) -| [Time series data](#time-series-data) -| [Customizing the data series](#customizing-the-data-series) -| [Customizing the grid](#customizing-the-grid) -| [Specifying gradients](#specifying-gradients) -| [Plot Methods](#plot-methods) -| [Hooks](#hooks) -| [Plugins](#plugins) -| [Version number](#version-number) - ---- - -## Introduction ## - -Consider a call to the plot function: - -```js -var plot = $.plot(placeholder, data, options) -``` - -The placeholder is a jQuery object or DOM element or jQuery expression -that the plot will be put into. This placeholder needs to have its -width and height set as explained in the [README](README.md) (go read that now if -you haven't, it's short). The plot will modify some properties of the -placeholder so it's recommended you simply pass in a div that you -don't use for anything else. Make sure you check any fancy styling -you apply to the div, e.g. background images have been reported to be a -problem on IE 7. - -The plot function can also be used as a jQuery chainable property. This form -naturally can't return the plot object directly, but you can still access it -via the 'plot' data key, like this: - -```js -var plot = $("#placeholder").plot(data, options).data("plot"); -``` - -The format of the data is documented below, as is the available -options. The plot object returned from the call has some methods you -can call. These are documented separately below. - -Note that in general Flot gives no guarantees if you change any of the -objects you pass in to the plot function or get out of it since -they're not necessarily deep-copied. - - -## Data Format ## - -The data is an array of data series: - -```js -[ series1, series2, ... ] -``` - -A series can either be raw data or an object with properties. The raw -data format is an array of points: - -```js -[ [x1, y1], [x2, y2], ... ] -``` - -E.g. - -```js -[ [1, 3], [2, 14.01], [3.5, 3.14] ] -``` - -Note that to simplify the internal logic in Flot both the x and y -values must be numbers (even if specifying time series, see below for -how to do this). This is a common problem because you might retrieve -data from the database and serialize them directly to JSON without -noticing the wrong type. If you're getting mysterious errors, double -check that you're inputting numbers and not strings. - -If a null is specified as a point or if one of the coordinates is null -or couldn't be converted to a number, the point is ignored when -drawing. As a special case, a null value for lines is interpreted as a -line segment end, i.e. the points before and after the null value are -not connected. - -Lines and points take two coordinates. For filled lines and bars, you -can specify a third coordinate which is the bottom of the filled -area/bar (defaults to 0). - -The format of a single series object is as follows: - -```js -{ - color: color or number - data: rawdata - label: string - lines: specific lines options - bars: specific bars options - points: specific points options - xaxis: number - yaxis: number - clickable: boolean - hoverable: boolean - shadowSize: number - highlightColor: color or number -} -``` - -You don't have to specify any of them except the data, the rest are -options that will get default values. Typically you'd only specify -label and data, like this: - -```js -{ - label: "y = 3", - data: [[0, 3], [10, 3]] -} -``` - -The label is used for the legend, if you don't specify one, the series -will not show up in the legend. - -If you don't specify color, the series will get a color from the -auto-generated colors. The color is either a CSS color specification -(like "rgb(255, 100, 123)") or an integer that specifies which of -auto-generated colors to select, e.g. 0 will get color no. 0, etc. - -The latter is mostly useful if you let the user add and remove series, -in which case you can hard-code the color index to prevent the colors -from jumping around between the series. - -The "xaxis" and "yaxis" options specify which axis to use. The axes -are numbered from 1 (default), so { yaxis: 2} means that the series -should be plotted against the second y axis. - -"clickable" and "hoverable" can be set to false to disable -interactivity for specific series if interactivity is turned on in -the plot, see below. - -The rest of the options are all documented below as they are the same -as the default options passed in via the options parameter in the plot -command. When you specify them for a specific data series, they will -override the default options for the plot for that data series. - -Here's a complete example of a simple data specification: - -```js -[ { label: "Foo", data: [ [10, 1], [17, -14], [30, 5] ] }, - { label: "Bar", data: [ [11, 13], [19, 11], [30, -7] ] } -] -``` - - -## Plot Options ## - -All options are completely optional. They are documented individually -below, to change them you just specify them in an object, e.g. - -```js -var options = { - series: { - lines: { show: true }, - points: { show: true } - } -}; - -$.plot(placeholder, data, options); -``` - - -## Customizing the legend ## - -```js -legend: { - show: boolean - labelFormatter: null or (fn: string, series object -> string) - labelBoxBorderColor: color - noColumns: number - position: "ne" or "nw" or "se" or "sw" - margin: number of pixels or [x margin, y margin] - backgroundColor: null or color - backgroundOpacity: number between 0 and 1 - container: null or jQuery object/DOM element/jQuery expression - sorted: null/false, true, "ascending", "descending", "reverse", or a comparator -} -``` - -The legend is generated as a table with the data series labels and -small label boxes with the color of the series. If you want to format -the labels in some way, e.g. make them to links, you can pass in a -function for "labelFormatter". Here's an example that makes them -clickable: - -```js -labelFormatter: function(label, series) { - // series is the series object for the label - return '' + label + ''; -} -``` - -To prevent a series from showing up in the legend, simply have the function -return null. - -"noColumns" is the number of columns to divide the legend table into. -"position" specifies the overall placement of the legend within the -plot (top-right, top-left, etc.) and margin the distance to the plot -edge (this can be either a number or an array of two numbers like [x, -y]). "backgroundColor" and "backgroundOpacity" specifies the -background. The default is a partly transparent auto-detected -background. - -If you want the legend to appear somewhere else in the DOM, you can -specify "container" as a jQuery object/expression to put the legend -table into. The "position" and "margin" etc. options will then be -ignored. Note that Flot will overwrite the contents of the container. - -Legend entries appear in the same order as their series by default. If "sorted" -is "reverse" then they appear in the opposite order from their series. To sort -them alphabetically, you can specify true, "ascending" or "descending", where -true and "ascending" are equivalent. - -You can also provide your own comparator function that accepts two -objects with "label" and "color" properties, and returns zero if they -are equal, a positive value if the first is greater than the second, -and a negative value if the first is less than the second. - -```js -sorted: function(a, b) { - // sort alphabetically in ascending order - return a.label == b.label ? 0 : ( - a.label > b.label ? 1 : -1 - ) -} -``` - - -## Customizing the axes ## - -```js -xaxis, yaxis: { - show: null or true/false - position: "bottom" or "top" or "left" or "right" - mode: null or "time" ("time" requires jquery.flot.time.js plugin) - timezone: null, "browser" or timezone (only makes sense for mode: "time") - - color: null or color spec - tickColor: null or color spec - font: null or font spec object - - min: null or number - max: null or number - autoscaleMargin: null or number - - transform: null or fn: number -> number - inverseTransform: null or fn: number -> number - - ticks: null or number or ticks array or (fn: axis -> ticks array) - tickSize: number or array - minTickSize: number or array - tickFormatter: (fn: number, object -> string) or string - tickDecimals: null or number - - labelWidth: null or number - labelHeight: null or number - reserveSpace: null or true - - tickLength: null or number - - alignTicksWithAxis: null or number -} -``` - -All axes have the same kind of options. The following describes how to -configure one axis, see below for what to do if you've got more than -one x axis or y axis. - -If you don't set the "show" option (i.e. it is null), visibility is -auto-detected, i.e. the axis will show up if there's data associated -with it. You can override this by setting the "show" option to true or -false. - -The "position" option specifies where the axis is placed, bottom or -top for x axes, left or right for y axes. The "mode" option determines -how the data is interpreted, the default of null means as decimal -numbers. Use "time" for time series data; see the time series data -section. The time plugin (jquery.flot.time.js) is required for time -series support. - -The "color" option determines the color of the line and ticks for the axis, and -defaults to the grid color with transparency. For more fine-grained control you -can also set the color of the ticks separately with "tickColor". - -You can customize the font and color used to draw the axis tick labels with CSS -or directly via the "font" option. When "font" is null - the default - each -tick label is given the 'flot-tick-label' class. For compatibility with Flot -0.7 and earlier the labels are also given the 'tickLabel' class, but this is -deprecated and scheduled to be removed with the release of version 1.0.0. - -To enable more granular control over styles, labels are divided between a set -of text containers, with each holding the labels for one axis. These containers -are given the classes 'flot-[x|y]-axis', and 'flot-[x|y]#-axis', where '#' is -the number of the axis when there are multiple axes. For example, the x-axis -labels for a simple plot with only a single x-axis might look like this: - -```html -
-
January 2013
- ... -
-``` - -For direct control over label styles you can also provide "font" as an object -with this format: - -```js -{ - size: 11, - lineHeight: 13, - style: "italic", - weight: "bold", - family: "sans-serif", - variant: "small-caps", - color: "#545454" -} -``` - -The size and lineHeight must be expressed in pixels; CSS units such as 'em' -or 'smaller' are not allowed. - -The options "min"/"max" are the precise minimum/maximum value on the -scale. If you don't specify either of them, a value will automatically -be chosen based on the minimum/maximum data values. Note that Flot -always examines all the data values you feed to it, even if a -restriction on another axis may make some of them invisible (this -makes interactive use more stable). - -The "autoscaleMargin" is a bit esoteric: it's the fraction of margin -that the scaling algorithm will add to avoid that the outermost points -ends up on the grid border. Note that this margin is only applied when -a min or max value is not explicitly set. If a margin is specified, -the plot will furthermore extend the axis end-point to the nearest -whole tick. The default value is "null" for the x axes and 0.02 for y -axes which seems appropriate for most cases. - -"transform" and "inverseTransform" are callbacks you can put in to -change the way the data is drawn. You can design a function to -compress or expand certain parts of the axis non-linearly, e.g. -suppress weekends or compress far away points with a logarithm or some -other means. When Flot draws the plot, each value is first put through -the transform function. Here's an example, the x axis can be turned -into a natural logarithm axis with the following code: - -```js -xaxis: { - transform: function (v) { return Math.log(v); }, - inverseTransform: function (v) { return Math.exp(v); } -} -``` - -Similarly, for reversing the y axis so the values appear in inverse -order: - -```js -yaxis: { - transform: function (v) { return -v; }, - inverseTransform: function (v) { return -v; } -} -``` - -Note that for finding extrema, Flot assumes that the transform -function does not reorder values (it should be monotone). - -The inverseTransform is simply the inverse of the transform function -(so v == inverseTransform(transform(v)) for all relevant v). It is -required for converting from canvas coordinates to data coordinates, -e.g. for a mouse interaction where a certain pixel is clicked. If you -don't use any interactive features of Flot, you may not need it. - - -The rest of the options deal with the ticks. - -If you don't specify any ticks, a tick generator algorithm will make -some for you. The algorithm has two passes. It first estimates how -many ticks would be reasonable and uses this number to compute a nice -round tick interval size. Then it generates the ticks. - -You can specify how many ticks the algorithm aims for by setting -"ticks" to a number. The algorithm always tries to generate reasonably -round tick values so even if you ask for three ticks, you might get -five if that fits better with the rounding. If you don't want any -ticks at all, set "ticks" to 0 or an empty array. - -Another option is to skip the rounding part and directly set the tick -interval size with "tickSize". If you set it to 2, you'll get ticks at -2, 4, 6, etc. Alternatively, you can specify that you just don't want -ticks at a size less than a specific tick size with "minTickSize". -Note that for time series, the format is an array like [2, "month"], -see the next section. - -If you want to completely override the tick algorithm, you can specify -an array for "ticks", either like this: - -```js -ticks: [0, 1.2, 2.4] -``` - -Or like this where the labels are also customized: - -```js -ticks: [[0, "zero"], [1.2, "one mark"], [2.4, "two marks"]] -``` - -You can mix the two if you like. - -For extra flexibility you can specify a function as the "ticks" -parameter. The function will be called with an object with the axis -min and max and should return a ticks array. Here's a simplistic tick -generator that spits out intervals of pi, suitable for use on the x -axis for trigonometric functions: - -```js -function piTickGenerator(axis) { - var res = [], i = Math.floor(axis.min / Math.PI); - do { - var v = i * Math.PI; - res.push([v, i + "\u03c0"]); - ++i; - } while (v < axis.max); - return res; -} -``` - -You can control how the ticks look like with "tickDecimals", the -number of decimals to display (default is auto-detected). - -Alternatively, for ultimate control over how ticks are formatted you can -provide a function to "tickFormatter". The function is passed two -parameters, the tick value and an axis object with information, and -should return a string. The default formatter looks like this: - -```js -function formatter(val, axis) { - return val.toFixed(axis.tickDecimals); -} -``` - -The axis object has "min" and "max" with the range of the axis, -"tickDecimals" with the number of decimals to round the value to and -"tickSize" with the size of the interval between ticks as calculated -by the automatic axis scaling algorithm (or specified by you). Here's -an example of a custom formatter: - -```js -function suffixFormatter(val, axis) { - if (val > 1000000) - return (val / 1000000).toFixed(axis.tickDecimals) + " MB"; - else if (val > 1000) - return (val / 1000).toFixed(axis.tickDecimals) + " kB"; - else - return val.toFixed(axis.tickDecimals) + " B"; -} -``` - -"labelWidth" and "labelHeight" specifies a fixed size of the tick -labels in pixels. They're useful in case you need to align several -plots. "reserveSpace" means that even if an axis isn't shown, Flot -should reserve space for it - it is useful in combination with -labelWidth and labelHeight for aligning multi-axis charts. - -"tickLength" is the length of the tick lines in pixels. By default, the -innermost axes will have ticks that extend all across the plot, while -any extra axes use small ticks. A value of null means use the default, -while a number means small ticks of that length - set it to 0 to hide -the lines completely. - -If you set "alignTicksWithAxis" to the number of another axis, e.g. -alignTicksWithAxis: 1, Flot will ensure that the autogenerated ticks -of this axis are aligned with the ticks of the other axis. This may -improve the looks, e.g. if you have one y axis to the left and one to -the right, because the grid lines will then match the ticks in both -ends. The trade-off is that the forced ticks won't necessarily be at -natural places. - - -## Multiple axes ## - -If you need more than one x axis or y axis, you need to specify for -each data series which axis they are to use, as described under the -format of the data series, e.g. { data: [...], yaxis: 2 } specifies -that a series should be plotted against the second y axis. - -To actually configure that axis, you can't use the xaxis/yaxis options -directly - instead there are two arrays in the options: - -```js -xaxes: [] -yaxes: [] -``` - -Here's an example of configuring a single x axis and two y axes (we -can leave options of the first y axis empty as the defaults are fine): - -```js -{ - xaxes: [ { position: "top" } ], - yaxes: [ { }, { position: "right", min: 20 } ] -} -``` - -The arrays get their default values from the xaxis/yaxis settings, so -say you want to have all y axes start at zero, you can simply specify -yaxis: { min: 0 } instead of adding a min parameter to all the axes. - -Generally, the various interfaces in Flot dealing with data points -either accept an xaxis/yaxis parameter to specify which axis number to -use (starting from 1), or lets you specify the coordinate directly as -x2/x3/... or x2axis/x3axis/... instead of "x" or "xaxis". - - -## Time series data ## - -Please note that it is now required to include the time plugin, -jquery.flot.time.js, for time series support. - -Time series are a bit more difficult than scalar data because -calendars don't follow a simple base 10 system. For many cases, Flot -abstracts most of this away, but it can still be a bit difficult to -get the data into Flot. So we'll first discuss the data format. - -The time series support in Flot is based on JavaScript timestamps, -i.e. everywhere a time value is expected or handed over, a JavaScript -timestamp number is used. This is a number, not a Date object. A -JavaScript timestamp is the number of milliseconds since January 1, -1970 00:00:00 UTC. This is almost the same as Unix timestamps, except it's -in milliseconds, so remember to multiply by 1000! - -You can see a timestamp like this - -```js -alert((new Date()).getTime()) -``` - -There are different schools of thought when it comes to display of -timestamps. Many will want the timestamps to be displayed according to -a certain time zone, usually the time zone in which the data has been -produced. Some want the localized experience, where the timestamps are -displayed according to the local time of the visitor. Flot supports -both. Optionally you can include a third-party library to get -additional timezone support. - -Default behavior is that Flot always displays timestamps according to -UTC. The reason being that the core JavaScript Date object does not -support other fixed time zones. Often your data is at another time -zone, so it may take a little bit of tweaking to work around this -limitation. - -The easiest way to think about it is to pretend that the data -production time zone is UTC, even if it isn't. So if you have a -datapoint at 2002-02-20 08:00, you can generate a timestamp for eight -o'clock UTC even if it really happened eight o'clock UTC+0200. - -In PHP you can get an appropriate timestamp with: - -```php -strtotime("2002-02-20 UTC") * 1000 -``` - -In Python you can get it with something like: - -```python -calendar.timegm(datetime_object.timetuple()) * 1000 -``` -In Ruby you can get it using the `#to_i` method on the -[`Time`](http://apidock.com/ruby/Time/to_i) object. If you're using the -`active_support` gem (default for Ruby on Rails applications) `#to_i` is also -available on the `DateTime` and `ActiveSupport::TimeWithZone` objects. You -simply need to multiply the result by 1000: - -```ruby -Time.now.to_i * 1000 # => 1383582043000 -# ActiveSupport examples: -DateTime.now.to_i * 1000 # => 1383582043000 -ActiveSupport::TimeZone.new('Asia/Shanghai').now.to_i * 1000 -# => 1383582043000 -``` - -In .NET you can get it with something like: - -```aspx -public static int GetJavaScriptTimestamp(System.DateTime input) -{ - System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks); - System.DateTime time = input.Subtract(span); - return (long)(time.Ticks / 10000); -} -``` - -JavaScript also has some support for parsing date strings, so it is -possible to generate the timestamps manually client-side. - -If you've already got the real UTC timestamp, it's too late to use the -pretend trick described above. But you can fix up the timestamps by -adding the time zone offset, e.g. for UTC+0200 you would add 2 hours -to the UTC timestamp you got. Then it'll look right on the plot. Most -programming environments have some means of getting the timezone -offset for a specific date (note that you need to get the offset for -each individual timestamp to account for daylight savings). - -The alternative with core JavaScript is to interpret the timestamps -according to the time zone that the visitor is in, which means that -the ticks will shift with the time zone and daylight savings of each -visitor. This behavior is enabled by setting the axis option -"timezone" to the value "browser". - -If you need more time zone functionality than this, there is still -another option. If you include the "timezone-js" library - in the page and set axis.timezone -to a value recognized by said library, Flot will use timezone-js to -interpret the timestamps according to that time zone. - -Once you've gotten the timestamps into the data and specified "time" -as the axis mode, Flot will automatically generate relevant ticks and -format them. As always, you can tweak the ticks via the "ticks" option -- just remember that the values should be timestamps (numbers), not -Date objects. - -Tick generation and formatting can also be controlled separately -through the following axis options: - -```js -minTickSize: array -timeformat: null or format string -monthNames: null or array of size 12 of strings -dayNames: null or array of size 7 of strings -twelveHourClock: boolean -``` - -Here "timeformat" is a format string to use. You might use it like -this: - -```js -xaxis: { - mode: "time", - timeformat: "%Y/%m/%d" -} -``` - -This will result in tick labels like "2000/12/24". A subset of the -standard strftime specifiers are supported (plus the nonstandard %q): - -```js -%a: weekday name (customizable) -%b: month name (customizable) -%d: day of month, zero-padded (01-31) -%e: day of month, space-padded ( 1-31) -%H: hours, 24-hour time, zero-padded (00-23) -%I: hours, 12-hour time, zero-padded (01-12) -%m: month, zero-padded (01-12) -%M: minutes, zero-padded (00-59) -%q: quarter (1-4) -%S: seconds, zero-padded (00-59) -%y: year (two digits) -%Y: year (four digits) -%p: am/pm -%P: AM/PM (uppercase version of %p) -%w: weekday as number (0-6, 0 being Sunday) -``` - -Flot 0.8 switched from %h to the standard %H hours specifier. The %h specifier -is still available, for backwards-compatibility, but is deprecated and -scheduled to be removed permanently with the release of version 1.0. - -You can customize the month names with the "monthNames" option. For -instance, for Danish you might specify: - -```js -monthNames: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"] -``` - -Similarly you can customize the weekday names with the "dayNames" -option. An example in French: - -```js -dayNames: ["dim", "lun", "mar", "mer", "jeu", "ven", "sam"] -``` - -If you set "twelveHourClock" to true, the autogenerated timestamps -will use 12 hour AM/PM timestamps instead of 24 hour. This only -applies if you have not set "timeformat". Use the "%I" and "%p" or -"%P" options if you want to build your own format string with 12-hour -times. - -If the Date object has a strftime property (and it is a function), it -will be used instead of the built-in formatter. Thus you can include -a strftime library such as http://hacks.bluesmoon.info/strftime/ for -more powerful date/time formatting. - -If everything else fails, you can control the formatting by specifying -a custom tick formatter function as usual. Here's a simple example -which will format December 24 as 24/12: - -```js -tickFormatter: function (val, axis) { - var d = new Date(val); - return d.getUTCDate() + "/" + (d.getUTCMonth() + 1); -} -``` - -Note that for the time mode "tickSize" and "minTickSize" are a bit -special in that they are arrays on the form "[value, unit]" where unit -is one of "second", "minute", "hour", "day", "month" and "year". So -you can specify - -```js -minTickSize: [1, "month"] -``` - -to get a tick interval size of at least 1 month and correspondingly, -if axis.tickSize is [2, "day"] in the tick formatter, the ticks have -been produced with two days in-between. - - -## Customizing the data series ## - -```js -series: { - lines, points, bars: { - show: boolean - lineWidth: number - fill: boolean or number - fillColor: null or color/gradient - } - - lines, bars: { - zero: boolean - } - - points: { - radius: number - symbol: "circle" or function - } - - bars: { - barWidth: number - align: "left", "right" or "center" - horizontal: boolean - } - - lines: { - steps: boolean - } - - shadowSize: number - highlightColor: color or number -} - -colors: [ color1, color2, ... ] -``` - -The options inside "series: {}" are copied to each of the series. So -you can specify that all series should have bars by putting it in the -global options, or override it for individual series by specifying -bars in a particular the series object in the array of data. - -The most important options are "lines", "points" and "bars" that -specify whether and how lines, points and bars should be shown for -each data series. In case you don't specify anything at all, Flot will -default to showing lines (you can turn this off with -lines: { show: false }). You can specify the various types -independently of each other, and Flot will happily draw each of them -in turn (this is probably only useful for lines and points), e.g. - -```js -var options = { - series: { - lines: { show: true, fill: true, fillColor: "rgba(255, 255, 255, 0.8)" }, - points: { show: true, fill: false } - } -}; -``` - -"lineWidth" is the thickness of the line or outline in pixels. You can -set it to 0 to prevent a line or outline from being drawn; this will -also hide the shadow. - -"fill" is whether the shape should be filled. For lines, this produces -area graphs. You can use "fillColor" to specify the color of the fill. -If "fillColor" evaluates to false (default for everything except -points which are filled with white), the fill color is auto-set to the -color of the data series. You can adjust the opacity of the fill by -setting fill to a number between 0 (fully transparent) and 1 (fully -opaque). - -For bars, fillColor can be a gradient, see the gradient documentation -below. "barWidth" is the width of the bars in units of the x axis (or -the y axis if "horizontal" is true), contrary to most other measures -that are specified in pixels. For instance, for time series the unit -is milliseconds so 24 * 60 * 60 * 1000 produces bars with the width of -a day. "align" specifies whether a bar should be left-aligned -(default), right-aligned or centered on top of the value it represents. -When "horizontal" is on, the bars are drawn horizontally, i.e. from the -y axis instead of the x axis; note that the bar end points are still -defined in the same way so you'll probably want to swap the -coordinates if you've been plotting vertical bars first. - -Area and bar charts normally start from zero, regardless of the data's range. -This is because they convey information through size, and starting from a -different value would distort their meaning. In cases where the fill is purely -for decorative purposes, however, "zero" allows you to override this behavior. -It defaults to true for filled lines and bars; setting it to false tells the -series to use the same automatic scaling as an un-filled line. - -For lines, "steps" specifies whether two adjacent data points are -connected with a straight (possibly diagonal) line or with first a -horizontal and then a vertical line. Note that this transforms the -data by adding extra points. - -For points, you can specify the radius and the symbol. The only -built-in symbol type is circles, for other types you can use a plugin -or define them yourself by specifying a callback: - -```js -function cross(ctx, x, y, radius, shadow) { - var size = radius * Math.sqrt(Math.PI) / 2; - ctx.moveTo(x - size, y - size); - ctx.lineTo(x + size, y + size); - ctx.moveTo(x - size, y + size); - ctx.lineTo(x + size, y - size); -} -``` - -The parameters are the drawing context, x and y coordinates of the -center of the point, a radius which corresponds to what the circle -would have used and whether the call is to draw a shadow (due to -limited canvas support, shadows are currently faked through extra -draws). It's good practice to ensure that the area covered by the -symbol is the same as for the circle with the given radius, this -ensures that all symbols have approximately the same visual weight. - -"shadowSize" is the default size of shadows in pixels. Set it to 0 to -remove shadows. - -"highlightColor" is the default color of the translucent overlay used -to highlight the series when the mouse hovers over it. - -The "colors" array specifies a default color theme to get colors for -the data series from. You can specify as many colors as you like, like -this: - -```js -colors: ["#d18b2c", "#dba255", "#919733"] -``` - -If there are more data series than colors, Flot will try to generate -extra colors by lightening and darkening colors in the theme. - - -## Customizing the grid ## - -```js -grid: { - show: boolean - aboveData: boolean - color: color - backgroundColor: color/gradient or null - margin: number or margin object - labelMargin: number - axisMargin: number - markings: array of markings or (fn: axes -> array of markings) - borderWidth: number or object with "top", "right", "bottom" and "left" properties with different widths - borderColor: color or null or object with "top", "right", "bottom" and "left" properties with different colors - minBorderMargin: number or null - clickable: boolean - hoverable: boolean - autoHighlight: boolean - mouseActiveRadius: number -} - -interaction: { - redrawOverlayInterval: number or -1 -} -``` - -The grid is the thing with the axes and a number of ticks. Many of the -things in the grid are configured under the individual axes, but not -all. "color" is the color of the grid itself whereas "backgroundColor" -specifies the background color inside the grid area, here null means -that the background is transparent. You can also set a gradient, see -the gradient documentation below. - -You can turn off the whole grid including tick labels by setting -"show" to false. "aboveData" determines whether the grid is drawn -above the data or below (below is default). - -"margin" is the space in pixels between the canvas edge and the grid, -which can be either a number or an object with individual margins for -each side, in the form: - -```js -margin: { - top: top margin in pixels - left: left margin in pixels - bottom: bottom margin in pixels - right: right margin in pixels -} -``` - -"labelMargin" is the space in pixels between tick labels and axis -line, and "axisMargin" is the space in pixels between axes when there -are two next to each other. - -"borderWidth" is the width of the border around the plot. Set it to 0 -to disable the border. Set it to an object with "top", "right", -"bottom" and "left" properties to use different widths. You can -also set "borderColor" if you want the border to have a different color -than the grid lines. Set it to an object with "top", "right", "bottom" -and "left" properties to use different colors. "minBorderMargin" controls -the default minimum margin around the border - it's used to make sure -that points aren't accidentally clipped by the canvas edge so by default -the value is computed from the point radius. - -"markings" is used to draw simple lines and rectangular areas in the -background of the plot. You can either specify an array of ranges on -the form { xaxis: { from, to }, yaxis: { from, to } } (with multiple -axes, you can specify coordinates for other axes instead, e.g. as -x2axis/x3axis/...) or with a function that returns such an array given -the axes for the plot in an object as the first parameter. - -You can set the color of markings by specifying "color" in the ranges -object. Here's an example array: - -```js -markings: [ { xaxis: { from: 0, to: 2 }, yaxis: { from: 10, to: 10 }, color: "#bb0000" }, ... ] -``` - -If you leave out one of the values, that value is assumed to go to the -border of the plot. So for example if you only specify { xaxis: { -from: 0, to: 2 } } it means an area that extends from the top to the -bottom of the plot in the x range 0-2. - -A line is drawn if from and to are the same, e.g. - -```js -markings: [ { yaxis: { from: 1, to: 1 } }, ... ] -``` - -would draw a line parallel to the x axis at y = 1. You can control the -line width with "lineWidth" in the range object. - -An example function that makes vertical stripes might look like this: - -```js -markings: function (axes) { - var markings = []; - for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2) - markings.push({ xaxis: { from: x, to: x + 1 } }); - return markings; -} -``` - -If you set "clickable" to true, the plot will listen for click events -on the plot area and fire a "plotclick" event on the placeholder with -a position and a nearby data item object as parameters. The coordinates -are available both in the unit of the axes (not in pixels) and in -global screen coordinates. - -Likewise, if you set "hoverable" to true, the plot will listen for -mouse move events on the plot area and fire a "plothover" event with -the same parameters as the "plotclick" event. If "autoHighlight" is -true (the default), nearby data items are highlighted automatically. -If needed, you can disable highlighting and control it yourself with -the highlight/unhighlight plot methods described elsewhere. - -You can use "plotclick" and "plothover" events like this: - -```js -$.plot($("#placeholder"), [ d ], { grid: { clickable: true } }); - -$("#placeholder").bind("plotclick", function (event, pos, item) { - alert("You clicked at " + pos.x + ", " + pos.y); - // axis coordinates for other axes, if present, are in pos.x2, pos.x3, ... - // if you need global screen coordinates, they are pos.pageX, pos.pageY - - if (item) { - highlight(item.series, item.datapoint); - alert("You clicked a point!"); - } -}); -``` - -The item object in this example is either null or a nearby object on the form: - -```js -item: { - datapoint: the point, e.g. [0, 2] - dataIndex: the index of the point in the data array - series: the series object - seriesIndex: the index of the series - pageX, pageY: the global screen coordinates of the point -} -``` - -For instance, if you have specified the data like this - -```js -$.plot($("#placeholder"), [ { label: "Foo", data: [[0, 10], [7, 3]] } ], ...); -``` - -and the mouse is near the point (7, 3), "datapoint" is [7, 3], -"dataIndex" will be 1, "series" is a normalized series object with -among other things the "Foo" label in series.label and the color in -series.color, and "seriesIndex" is 0. Note that plugins and options -that transform the data can shift the indexes from what you specified -in the original data array. - -If you use the above events to update some other information and want -to clear out that info in case the mouse goes away, you'll probably -also need to listen to "mouseout" events on the placeholder div. - -"mouseActiveRadius" specifies how far the mouse can be from an item -and still activate it. If there are two or more points within this -radius, Flot chooses the closest item. For bars, the top-most bar -(from the latest specified data series) is chosen. - -If you want to disable interactivity for a specific data series, you -can set "hoverable" and "clickable" to false in the options for that -series, like this: - -```js -{ data: [...], label: "Foo", clickable: false } -``` - -"redrawOverlayInterval" specifies the maximum time to delay a redraw -of interactive things (this works as a rate limiting device). The -default is capped to 60 frames per second. You can set it to -1 to -disable the rate limiting. - - -## Specifying gradients ## - -A gradient is specified like this: - -```js -{ colors: [ color1, color2, ... ] } -``` - -For instance, you might specify a background on the grid going from -black to gray like this: - -```js -grid: { - backgroundColor: { colors: ["#000", "#999"] } -} -``` - -For the series you can specify the gradient as an object that -specifies the scaling of the brightness and the opacity of the series -color, e.g. - -```js -{ colors: [{ opacity: 0.8 }, { brightness: 0.6, opacity: 0.8 } ] } -``` - -where the first color simply has its alpha scaled, whereas the second -is also darkened. For instance, for bars the following makes the bars -gradually disappear, without outline: - -```js -bars: { - show: true, - lineWidth: 0, - fill: true, - fillColor: { colors: [ { opacity: 0.8 }, { opacity: 0.1 } ] } -} -``` - -Flot currently only supports vertical gradients drawn from top to -bottom because that's what works with IE. - - -## Plot Methods ## - -The Plot object returned from the plot function has some methods you -can call: - - - highlight(series, datapoint) - - Highlight a specific datapoint in the data series. You can either - specify the actual objects, e.g. if you got them from a - "plotclick" event, or you can specify the indices, e.g. - highlight(1, 3) to highlight the fourth point in the second series - (remember, zero-based indexing). - - - unhighlight(series, datapoint) or unhighlight() - - Remove the highlighting of the point, same parameters as - highlight. - - If you call unhighlight with no parameters, e.g. as - plot.unhighlight(), all current highlights are removed. - - - setData(data) - - You can use this to reset the data used. Note that axis scaling, - ticks, legend etc. will not be recomputed (use setupGrid() to do - that). You'll probably want to call draw() afterwards. - - You can use this function to speed up redrawing a small plot if - you know that the axes won't change. Put in the new data with - setData(newdata), call draw(), and you're good to go. Note that - for large datasets, almost all the time is consumed in draw() - plotting the data so in this case don't bother. - - - setupGrid() - - Recalculate and set axis scaling, ticks, legend etc. - - Note that because of the drawing model of the canvas, this - function will immediately redraw (actually reinsert in the DOM) - the labels and the legend, but not the actual tick lines because - they're drawn on the canvas. You need to call draw() to get the - canvas redrawn. - - - draw() - - Redraws the plot canvas. - - - triggerRedrawOverlay() - - Schedules an update of an overlay canvas used for drawing - interactive things like a selection and point highlights. This - is mostly useful for writing plugins. The redraw doesn't happen - immediately, instead a timer is set to catch multiple successive - redraws (e.g. from a mousemove). You can get to the overlay by - setting up a drawOverlay hook. - - - width()/height() - - Gets the width and height of the plotting area inside the grid. - This is smaller than the canvas or placeholder dimensions as some - extra space is needed (e.g. for labels). - - - offset() - - Returns the offset of the plotting area inside the grid relative - to the document, useful for instance for calculating mouse - positions (event.pageX/Y minus this offset is the pixel position - inside the plot). - - - pointOffset({ x: xpos, y: ypos }) - - Returns the calculated offset of the data point at (x, y) in data - space within the placeholder div. If you are working with multiple - axes, you can specify the x and y axis references, e.g. - - ```js - o = pointOffset({ x: xpos, y: ypos, xaxis: 2, yaxis: 3 }) - // o.left and o.top now contains the offset within the div - ```` - - - resize() - - Tells Flot to resize the drawing canvas to the size of the - placeholder. You need to run setupGrid() and draw() afterwards as - canvas resizing is a destructive operation. This is used - internally by the resize plugin. - - - shutdown() - - Cleans up any event handlers Flot has currently registered. This - is used internally. - -There are also some members that let you peek inside the internal -workings of Flot which is useful in some cases. Note that if you change -something in the objects returned, you're changing the objects used by -Flot to keep track of its state, so be careful. - - - getData() - - Returns an array of the data series currently used in normalized - form with missing settings filled in according to the global - options. So for instance to find out what color Flot has assigned - to the data series, you could do this: - - ```js - var series = plot.getData(); - for (var i = 0; i < series.length; ++i) - alert(series[i].color); - ``` - - A notable other interesting field besides color is datapoints - which has a field "points" with the normalized data points in a - flat array (the field "pointsize" is the increment in the flat - array to get to the next point so for a dataset consisting only of - (x,y) pairs it would be 2). - - - getAxes() - - Gets an object with the axes. The axes are returned as the - attributes of the object, so for instance getAxes().xaxis is the - x axis. - - Various things are stuffed inside an axis object, e.g. you could - use getAxes().xaxis.ticks to find out what the ticks are for the - xaxis. Two other useful attributes are p2c and c2p, functions for - transforming from data point space to the canvas plot space and - back. Both returns values that are offset with the plot offset. - Check the Flot source code for the complete set of attributes (or - output an axis with console.log() and inspect it). - - With multiple axes, the extra axes are returned as x2axis, x3axis, - etc., e.g. getAxes().y2axis is the second y axis. You can check - y2axis.used to see whether the axis is associated with any data - points and y2axis.show to see if it is currently shown. - - - getPlaceholder() - - Returns placeholder that the plot was put into. This can be useful - for plugins for adding DOM elements or firing events. - - - getCanvas() - - Returns the canvas used for drawing in case you need to hack on it - yourself. You'll probably need to get the plot offset too. - - - getPlotOffset() - - Gets the offset that the grid has within the canvas as an object - with distances from the canvas edges as "left", "right", "top", - "bottom". I.e., if you draw a circle on the canvas with the center - placed at (left, top), its center will be at the top-most, left - corner of the grid. - - - getOptions() - - Gets the options for the plot, normalized, with default values - filled in. You get a reference to actual values used by Flot, so - if you modify the values in here, Flot will use the new values. - If you change something, you probably have to call draw() or - setupGrid() or triggerRedrawOverlay() to see the change. - - -## Hooks ## - -In addition to the public methods, the Plot object also has some hooks -that can be used to modify the plotting process. You can install a -callback function at various points in the process, the function then -gets access to the internal data structures in Flot. - -Here's an overview of the phases Flot goes through: - - 1. Plugin initialization, parsing options - - 2. Constructing the canvases used for drawing - - 3. Set data: parsing data specification, calculating colors, - copying raw data points into internal format, - normalizing them, finding max/min for axis auto-scaling - - 4. Grid setup: calculating axis spacing, ticks, inserting tick - labels, the legend - - 5. Draw: drawing the grid, drawing each of the series in turn - - 6. Setting up event handling for interactive features - - 7. Responding to events, if any - - 8. Shutdown: this mostly happens in case a plot is overwritten - -Each hook is simply a function which is put in the appropriate array. -You can add them through the "hooks" option, and they are also available -after the plot is constructed as the "hooks" attribute on the returned -plot object, e.g. - -```js - // define a simple draw hook - function hellohook(plot, canvascontext) { alert("hello!"); }; - - // pass it in, in an array since we might want to specify several - var plot = $.plot(placeholder, data, { hooks: { draw: [hellohook] } }); - - // we can now find it again in plot.hooks.draw[0] unless a plugin - // has added other hooks -``` - -The available hooks are described below. All hook callbacks get the -plot object as first parameter. You can find some examples of defined -hooks in the plugins bundled with Flot. - - - processOptions [phase 1] - - ```function(plot, options)``` - - Called after Flot has parsed and merged options. Useful in the - instance where customizations beyond simple merging of default - values is needed. A plugin might use it to detect that it has been - enabled and then turn on or off other options. - - - - processRawData [phase 3] - - ```function(plot, series, data, datapoints)``` - - Called before Flot copies and normalizes the raw data for the given - series. If the function fills in datapoints.points with normalized - points and sets datapoints.pointsize to the size of the points, - Flot will skip the copying/normalization step for this series. - - In any case, you might be interested in setting datapoints.format, - an array of objects for specifying how a point is normalized and - how it interferes with axis scaling. It accepts the following options: - - ```js - { - x, y: boolean, - number: boolean, - required: boolean, - defaultValue: value, - autoscale: boolean - } - ``` - - "x" and "y" specify whether the value is plotted against the x or y axis, - and is currently used only to calculate axis min-max ranges. The default - format array, for example, looks like this: - - ```js - [ - { x: true, number: true, required: true }, - { y: true, number: true, required: true } - ] - ``` - - This indicates that a point, i.e. [0, 25], consists of two values, with the - first being plotted on the x axis and the second on the y axis. - - If "number" is true, then the value must be numeric, and is set to null if - it cannot be converted to a number. - - "defaultValue" provides a fallback in case the original value is null. This - is for instance handy for bars, where one can omit the third coordinate - (the bottom of the bar), which then defaults to zero. - - If "required" is true, then the value must exist (be non-null) for the - point as a whole to be valid. If no value is provided, then the entire - point is cleared out with nulls, turning it into a gap in the series. - - "autoscale" determines whether the value is considered when calculating an - automatic min-max range for the axes that the value is plotted against. - - - processDatapoints [phase 3] - - ```function(plot, series, datapoints)``` - - Called after normalization of the given series but before finding - min/max of the data points. This hook is useful for implementing data - transformations. "datapoints" contains the normalized data points in - a flat array as datapoints.points with the size of a single point - given in datapoints.pointsize. Here's a simple transform that - multiplies all y coordinates by 2: - - ```js - function multiply(plot, series, datapoints) { - var points = datapoints.points, ps = datapoints.pointsize; - for (var i = 0; i < points.length; i += ps) - points[i + 1] *= 2; - } - ``` - - Note that you must leave datapoints in a good condition as Flot - doesn't check it or do any normalization on it afterwards. - - - processOffset [phase 4] - - ```function(plot, offset)``` - - Called after Flot has initialized the plot's offset, but before it - draws any axes or plot elements. This hook is useful for customizing - the margins between the grid and the edge of the canvas. "offset" is - an object with attributes "top", "bottom", "left" and "right", - corresponding to the margins on the four sides of the plot. - - - drawBackground [phase 5] - - ```function(plot, canvascontext)``` - - Called before all other drawing operations. Used to draw backgrounds - or other custom elements before the plot or axes have been drawn. - - - drawSeries [phase 5] - - ```function(plot, canvascontext, series)``` - - Hook for custom drawing of a single series. Called just before the - standard drawing routine has been called in the loop that draws - each series. - - - draw [phase 5] - - ```function(plot, canvascontext)``` - - Hook for drawing on the canvas. Called after the grid is drawn - (unless it's disabled or grid.aboveData is set) and the series have - been plotted (in case any points, lines or bars have been turned - on). For examples of how to draw things, look at the source code. - - - bindEvents [phase 6] - - ```function(plot, eventHolder)``` - - Called after Flot has setup its event handlers. Should set any - necessary event handlers on eventHolder, a jQuery object with the - canvas, e.g. - - ```js - function (plot, eventHolder) { - eventHolder.mousedown(function (e) { - alert("You pressed the mouse at " + e.pageX + " " + e.pageY); - }); - } - ``` - - Interesting events include click, mousemove, mouseup/down. You can - use all jQuery events. Usually, the event handlers will update the - state by drawing something (add a drawOverlay hook and call - triggerRedrawOverlay) or firing an externally visible event for - user code. See the crosshair plugin for an example. - - Currently, eventHolder actually contains both the static canvas - used for the plot itself and the overlay canvas used for - interactive features because some versions of IE get the stacking - order wrong. The hook only gets one event, though (either for the - overlay or for the static canvas). - - Note that custom plot events generated by Flot are not generated on - eventHolder, but on the div placeholder supplied as the first - argument to the plot call. You can get that with - plot.getPlaceholder() - that's probably also the one you should use - if you need to fire a custom event. - - - drawOverlay [phase 7] - - ```function (plot, canvascontext)``` - - The drawOverlay hook is used for interactive things that need a - canvas to draw on. The model currently used by Flot works the way - that an extra overlay canvas is positioned on top of the static - canvas. This overlay is cleared and then completely redrawn - whenever something interesting happens. This hook is called when - the overlay canvas is to be redrawn. - - "canvascontext" is the 2D context of the overlay canvas. You can - use this to draw things. You'll most likely need some of the - metrics computed by Flot, e.g. plot.width()/plot.height(). See the - crosshair plugin for an example. - - - shutdown [phase 8] - - ```function (plot, eventHolder)``` - - Run when plot.shutdown() is called, which usually only happens in - case a plot is overwritten by a new plot. If you're writing a - plugin that adds extra DOM elements or event handlers, you should - add a callback to clean up after you. Take a look at the section in - the [PLUGINS](PLUGINS.md) document for more info. - - -## Plugins ## - -Plugins extend the functionality of Flot. To use a plugin, simply -include its JavaScript file after Flot in the HTML page. - -If you're worried about download size/latency, you can concatenate all -the plugins you use, and Flot itself for that matter, into one big file -(make sure you get the order right), then optionally run it through a -JavaScript minifier such as YUI Compressor. - -Here's a brief explanation of how the plugin plumbings work: - -Each plugin registers itself in the global array $.plot.plugins. When -you make a new plot object with $.plot, Flot goes through this array -calling the "init" function of each plugin and merging default options -from the "option" attribute of the plugin. The init function gets a -reference to the plot object created and uses this to register hooks -and add new public methods if needed. - -See the [PLUGINS](PLUGINS.md) document for details on how to write a plugin. As the -above description hints, it's actually pretty easy. - - -## Version number ## - -The version number of Flot is available in ```$.plot.version```. diff --git a/src/legacy/ui/public/flot-charts/index.js b/src/legacy/ui/public/flot-charts/index.js deleted file mode 100644 index f98aca30d2dec..0000000000000 --- a/src/legacy/ui/public/flot-charts/index.js +++ /dev/null @@ -1,42 +0,0 @@ -/* @notice - * - * This product includes code that is based on flot-charts, which was available - * under a "MIT" license. - * - * The MIT License (MIT) - * - * Copyright (c) 2007-2014 IOLA and Ole Laursen - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -import $ from 'jquery'; -if (window) window.jQuery = $; -require('ui/flot-charts/jquery.flot'); -require('ui/flot-charts/jquery.flot.time'); -require('ui/flot-charts/jquery.flot.canvas'); -require('ui/flot-charts/jquery.flot.symbol'); -require('ui/flot-charts/jquery.flot.crosshair'); -require('ui/flot-charts/jquery.flot.selection'); -require('ui/flot-charts/jquery.flot.pie'); -require('ui/flot-charts/jquery.flot.stack'); -require('ui/flot-charts/jquery.flot.threshold'); -require('ui/flot-charts/jquery.flot.fillbetween'); -require('ui/flot-charts/jquery.flot.log'); -module.exports = $; diff --git a/src/legacy/ui/public/flot-charts/jquery.colorhelpers.js b/src/legacy/ui/public/flot-charts/jquery.colorhelpers.js deleted file mode 100644 index b2f6dc4e433a3..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.colorhelpers.js +++ /dev/null @@ -1,180 +0,0 @@ -/* Plugin for jQuery for working with colors. - * - * Version 1.1. - * - * Inspiration from jQuery color animation plugin by John Resig. - * - * Released under the MIT license by Ole Laursen, October 2009. - * - * Examples: - * - * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() - * var c = $.color.extract($("#mydiv"), 'background-color'); - * console.log(c.r, c.g, c.b, c.a); - * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" - * - * Note that .scale() and .add() return the same modified object - * instead of making a new one. - * - * V. 1.1: Fix error handling so e.g. parsing an empty string does - * produce a color rather than just crashing. - */ - -(function($) { - $.color = {}; - - // construct color object with some convenient chainable helpers - $.color.make = function (r, g, b, a) { - var o = {}; - o.r = r || 0; - o.g = g || 0; - o.b = b || 0; - o.a = a != null ? a : 1; - - o.add = function (c, d) { - for (var i = 0; i < c.length; ++i) - o[c.charAt(i)] += d; - return o.normalize(); - }; - - o.scale = function (c, f) { - for (var i = 0; i < c.length; ++i) - o[c.charAt(i)] *= f; - return o.normalize(); - }; - - o.toString = function () { - if (o.a >= 1.0) { - return "rgb("+[o.r, o.g, o.b].join(",")+")"; - } else { - return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")"; - } - }; - - o.normalize = function () { - function clamp(min, value, max) { - return value < min ? min: (value > max ? max: value); - } - - o.r = clamp(0, parseInt(o.r), 255); - o.g = clamp(0, parseInt(o.g), 255); - o.b = clamp(0, parseInt(o.b), 255); - o.a = clamp(0, o.a, 1); - return o; - }; - - o.clone = function () { - return $.color.make(o.r, o.b, o.g, o.a); - }; - - return o.normalize(); - } - - // extract CSS color property from element, going up in the DOM - // if it's "transparent" - $.color.extract = function (elem, css) { - var c; - - do { - c = elem.css(css).toLowerCase(); - // keep going until we find an element that has color, or - // we hit the body or root (have no parent) - if (c != '' && c != 'transparent') - break; - elem = elem.parent(); - } while (elem.length && !$.nodeName(elem.get(0), "body")); - - // catch Safari's way of signalling transparent - if (c == "rgba(0, 0, 0, 0)") - c = "transparent"; - - return $.color.parse(c); - } - - // parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), - // returns color object, if parsing failed, you get black (0, 0, - // 0) out - $.color.parse = function (str) { - var res, m = $.color.make; - - // Look for rgb(num,num,num) - if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) - return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); - - // Look for rgba(num,num,num,num) - if (res = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) - return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); - - // Look for rgb(num%,num%,num%) - if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) - return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55); - - // Look for rgba(num%,num%,num%,num) - if (res = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str)) - return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4])); - - // Look for #a0b1c2 - if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) - return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); - - // Look for #fff - if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) - return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16)); - - // Otherwise, we're most likely dealing with a named color - var name = $.trim(str).toLowerCase(); - if (name == "transparent") - return m(255, 255, 255, 0); - else { - // default to black - res = lookupColors[name] || [0, 0, 0]; - return m(res[0], res[1], res[2]); - } - } - - var lookupColors = { - aqua:[0,255,255], - azure:[240,255,255], - beige:[245,245,220], - black:[0,0,0], - blue:[0,0,255], - brown:[165,42,42], - cyan:[0,255,255], - darkblue:[0,0,139], - darkcyan:[0,139,139], - darkgrey:[169,169,169], - darkgreen:[0,100,0], - darkkhaki:[189,183,107], - darkmagenta:[139,0,139], - darkolivegreen:[85,107,47], - darkorange:[255,140,0], - darkorchid:[153,50,204], - darkred:[139,0,0], - darksalmon:[233,150,122], - darkviolet:[148,0,211], - fuchsia:[255,0,255], - gold:[255,215,0], - green:[0,128,0], - indigo:[75,0,130], - khaki:[240,230,140], - lightblue:[173,216,230], - lightcyan:[224,255,255], - lightgreen:[144,238,144], - lightgrey:[211,211,211], - lightpink:[255,182,193], - lightyellow:[255,255,224], - lime:[0,255,0], - magenta:[255,0,255], - maroon:[128,0,0], - navy:[0,0,128], - olive:[128,128,0], - orange:[255,165,0], - pink:[255,192,203], - purple:[128,0,128], - violet:[128,0,128], - red:[255,0,0], - silver:[192,192,192], - white:[255,255,255], - yellow:[255,255,0] - }; -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.canvas.js b/src/legacy/ui/public/flot-charts/jquery.flot.canvas.js deleted file mode 100644 index 29328d5812127..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.canvas.js +++ /dev/null @@ -1,345 +0,0 @@ -/* Flot plugin for drawing all elements of a plot on the canvas. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -Flot normally produces certain elements, like axis labels and the legend, using -HTML elements. This permits greater interactivity and customization, and often -looks better, due to cross-browser canvas text inconsistencies and limitations. - -It can also be desirable to render the plot entirely in canvas, particularly -if the goal is to save it as an image, or if Flot is being used in a context -where the HTML DOM does not exist, as is the case within Node.js. This plugin -switches out Flot's standard drawing operations for canvas-only replacements. - -Currently the plugin supports only axis labels, but it will eventually allow -every element of the plot to be rendered directly to canvas. - -The plugin supports these options: - -{ - canvas: boolean -} - -The "canvas" option controls whether full canvas drawing is enabled, making it -possible to toggle on and off. This is useful when a plot uses HTML text in the -browser, but needs to redraw with canvas text when exporting as an image. - -*/ - -(function($) { - - var options = { - canvas: true - }; - - var render, getTextInfo, addText; - - // Cache the prototype hasOwnProperty for faster access - - var hasOwnProperty = Object.prototype.hasOwnProperty; - - function init(plot, classes) { - - var Canvas = classes.Canvas; - - // We only want to replace the functions once; the second time around - // we would just get our new function back. This whole replacing of - // prototype functions is a disaster, and needs to be changed ASAP. - - if (render == null) { - getTextInfo = Canvas.prototype.getTextInfo, - addText = Canvas.prototype.addText, - render = Canvas.prototype.render; - } - - // Finishes rendering the canvas, including overlaid text - - Canvas.prototype.render = function() { - - if (!plot.getOptions().canvas) { - return render.call(this); - } - - var context = this.context, - cache = this._textCache; - - // For each text layer, render elements marked as active - - context.save(); - context.textBaseline = "middle"; - - for (var layerKey in cache) { - if (hasOwnProperty.call(cache, layerKey)) { - var layerCache = cache[layerKey]; - for (var styleKey in layerCache) { - if (hasOwnProperty.call(layerCache, styleKey)) { - var styleCache = layerCache[styleKey], - updateStyles = true; - for (var key in styleCache) { - if (hasOwnProperty.call(styleCache, key)) { - - var info = styleCache[key], - positions = info.positions, - lines = info.lines; - - // Since every element at this level of the cache have the - // same font and fill styles, we can just change them once - // using the values from the first element. - - if (updateStyles) { - context.fillStyle = info.font.color; - context.font = info.font.definition; - updateStyles = false; - } - - for (var i = 0, position; position = positions[i]; i++) { - if (position.active) { - for (var j = 0, line; line = position.lines[j]; j++) { - context.fillText(lines[j].text, line[0], line[1]); - } - } else { - positions.splice(i--, 1); - } - } - - if (positions.length == 0) { - delete styleCache[key]; - } - } - } - } - } - } - } - - context.restore(); - }; - - // Creates (if necessary) and returns a text info object. - // - // When the canvas option is set, the object looks like this: - // - // { - // width: Width of the text's bounding box. - // height: Height of the text's bounding box. - // positions: Array of positions at which this text is drawn. - // lines: [{ - // height: Height of this line. - // widths: Width of this line. - // text: Text on this line. - // }], - // font: { - // definition: Canvas font property string. - // color: Color of the text. - // }, - // } - // - // The positions array contains objects that look like this: - // - // { - // active: Flag indicating whether the text should be visible. - // lines: Array of [x, y] coordinates at which to draw the line. - // x: X coordinate at which to draw the text. - // y: Y coordinate at which to draw the text. - // } - - Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { - - if (!plot.getOptions().canvas) { - return getTextInfo.call(this, layer, text, font, angle, width); - } - - var textStyle, layerCache, styleCache, info; - - // Cast the value to a string, in case we were given a number - - text = "" + text; - - // If the font is a font-spec object, generate a CSS definition - - if (typeof font === "object") { - textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; - } else { - textStyle = font; - } - - // Retrieve (or create) the cache for the text's layer and styles - - layerCache = this._textCache[layer]; - - if (layerCache == null) { - layerCache = this._textCache[layer] = {}; - } - - styleCache = layerCache[textStyle]; - - if (styleCache == null) { - styleCache = layerCache[textStyle] = {}; - } - - info = styleCache[text]; - - if (info == null) { - - var context = this.context; - - // If the font was provided as CSS, create a div with those - // classes and examine it to generate a canvas font spec. - - if (typeof font !== "object") { - - var element = $("
 
") - .css("position", "absolute") - .addClass(typeof font === "string" ? font : null) - .appendTo(this.getTextLayer(layer)); - - font = { - lineHeight: element.height(), - style: element.css("font-style"), - variant: element.css("font-variant"), - weight: element.css("font-weight"), - family: element.css("font-family"), - color: element.css("color") - }; - - // Setting line-height to 1, without units, sets it equal - // to the font-size, even if the font-size is abstract, - // like 'smaller'. This enables us to read the real size - // via the element's height, working around browsers that - // return the literal 'smaller' value. - - font.size = element.css("line-height", 1).height(); - - element.remove(); - } - - textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px " + font.family; - - // Create a new info object, initializing the dimensions to - // zero so we can count them up line-by-line. - - info = styleCache[text] = { - width: 0, - height: 0, - positions: [], - lines: [], - font: { - definition: textStyle, - color: font.color - } - }; - - context.save(); - context.font = textStyle; - - // Canvas can't handle multi-line strings; break on various - // newlines, including HTML brs, to build a list of lines. - // Note that we could split directly on regexps, but IE < 9 is - // broken; revisit when we drop IE 7/8 support. - - var lines = (text + "").replace(/
|\r\n|\r/g, "\n").split("\n"); - - for (var i = 0; i < lines.length; ++i) { - - var lineText = lines[i], - measured = context.measureText(lineText); - - info.width = Math.max(measured.width, info.width); - info.height += font.lineHeight; - - info.lines.push({ - text: lineText, - width: measured.width, - height: font.lineHeight - }); - } - - context.restore(); - } - - return info; - }; - - // Adds a text string to the canvas text overlay. - - Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { - - if (!plot.getOptions().canvas) { - return addText.call(this, layer, x, y, text, font, angle, width, halign, valign); - } - - var info = this.getTextInfo(layer, text, font, angle, width), - positions = info.positions, - lines = info.lines; - - // Text is drawn with baseline 'middle', which we need to account - // for by adding half a line's height to the y position. - - y += info.height / lines.length / 2; - - // Tweak the initial y-position to match vertical alignment - - if (valign == "middle") { - y = Math.round(y - info.height / 2); - } else if (valign == "bottom") { - y = Math.round(y - info.height); - } else { - y = Math.round(y); - } - - // FIXME: LEGACY BROWSER FIX - // AFFECTS: Opera < 12.00 - - // Offset the y coordinate, since Opera is off pretty - // consistently compared to the other browsers. - - if (!!(window.opera && window.opera.version().split(".")[0] < 12)) { - y -= 2; - } - - // Determine whether this text already exists at this position. - // If so, mark it for inclusion in the next render pass. - - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = true; - return; - } - } - - // If the text doesn't exist at this position, create a new entry - - position = { - active: true, - lines: [], - x: x, - y: y - }; - - positions.push(position); - - // Fill in the x & y positions of each line, adjusting them - // individually for horizontal alignment. - - for (var i = 0, line; line = lines[i]; i++) { - if (halign == "center") { - position.lines.push([Math.round(x - line.width / 2), y]); - } else if (halign == "right") { - position.lines.push([Math.round(x - line.width), y]); - } else { - position.lines.push([Math.round(x), y]); - } - y += line.height; - } - }; - } - - $.plot.plugins.push({ - init: init, - options: options, - name: "canvas", - version: "1.0" - }); - -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.categories.js b/src/legacy/ui/public/flot-charts/jquery.flot.categories.js deleted file mode 100644 index 2f9b257971499..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.categories.js +++ /dev/null @@ -1,190 +0,0 @@ -/* Flot plugin for plotting textual data or categories. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -Consider a dataset like [["February", 34], ["March", 20], ...]. This plugin -allows you to plot such a dataset directly. - -To enable it, you must specify mode: "categories" on the axis with the textual -labels, e.g. - - $.plot("#placeholder", data, { xaxis: { mode: "categories" } }); - -By default, the labels are ordered as they are met in the data series. If you -need a different ordering, you can specify "categories" on the axis options -and list the categories there: - - xaxis: { - mode: "categories", - categories: ["February", "March", "April"] - } - -If you need to customize the distances between the categories, you can specify -"categories" as an object mapping labels to values - - xaxis: { - mode: "categories", - categories: { "February": 1, "March": 3, "April": 4 } - } - -If you don't specify all categories, the remaining categories will be numbered -from the max value plus 1 (with a spacing of 1 between each). - -Internally, the plugin works by transforming the input data through an auto- -generated mapping where the first category becomes 0, the second 1, etc. -Hence, a point like ["February", 34] becomes [0, 34] internally in Flot (this -is visible in hover and click events that return numbers rather than the -category labels). The plugin also overrides the tick generator to spit out the -categories as ticks instead of the values. - -If you need to map a value back to its label, the mapping is always accessible -as "categories" on the axis object, e.g. plot.getAxes().xaxis.categories. - -*/ - -(function ($) { - var options = { - xaxis: { - categories: null - }, - yaxis: { - categories: null - } - }; - - function processRawData(plot, series, data, datapoints) { - // if categories are enabled, we need to disable - // auto-transformation to numbers so the strings are intact - // for later processing - - var xCategories = series.xaxis.options.mode == "categories", - yCategories = series.yaxis.options.mode == "categories"; - - if (!(xCategories || yCategories)) - return; - - var format = datapoints.format; - - if (!format) { - // FIXME: auto-detection should really not be defined here - var s = series; - format = []; - format.push({ x: true, number: true, required: true }); - format.push({ y: true, number: true, required: true }); - - if (s.bars.show || (s.lines.show && s.lines.fill)) { - var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); - format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); - if (s.bars.horizontal) { - delete format[format.length - 1].y; - format[format.length - 1].x = true; - } - } - - datapoints.format = format; - } - - for (var m = 0; m < format.length; ++m) { - if (format[m].x && xCategories) - format[m].number = false; - - if (format[m].y && yCategories) - format[m].number = false; - } - } - - function getNextIndex(categories) { - var index = -1; - - for (var v in categories) - if (categories[v] > index) - index = categories[v]; - - return index + 1; - } - - function categoriesTickGenerator(axis) { - var res = []; - for (var label in axis.categories) { - var v = axis.categories[label]; - if (v >= axis.min && v <= axis.max) - res.push([v, label]); - } - - res.sort(function (a, b) { return a[0] - b[0]; }); - - return res; - } - - function setupCategoriesForAxis(series, axis, datapoints) { - if (series[axis].options.mode != "categories") - return; - - if (!series[axis].categories) { - // parse options - var c = {}, o = series[axis].options.categories || {}; - if ($.isArray(o)) { - for (var i = 0; i < o.length; ++i) - c[o[i]] = i; - } - else { - for (var v in o) - c[v] = o[v]; - } - - series[axis].categories = c; - } - - // fix ticks - if (!series[axis].options.ticks) - series[axis].options.ticks = categoriesTickGenerator; - - transformPointsOnAxis(datapoints, axis, series[axis].categories); - } - - function transformPointsOnAxis(datapoints, axis, categories) { - // go through the points, transforming them - var points = datapoints.points, - ps = datapoints.pointsize, - format = datapoints.format, - formatColumn = axis.charAt(0), - index = getNextIndex(categories); - - for (var i = 0; i < points.length; i += ps) { - if (points[i] == null) - continue; - - for (var m = 0; m < ps; ++m) { - var val = points[i + m]; - - if (val == null || !format[m][formatColumn]) - continue; - - if (!(val in categories)) { - categories[val] = index; - ++index; - } - - points[i + m] = categories[val]; - } - } - } - - function processDatapoints(plot, series, datapoints) { - setupCategoriesForAxis(series, "xaxis", datapoints); - setupCategoriesForAxis(series, "yaxis", datapoints); - } - - function init(plot) { - plot.hooks.processRawData.push(processRawData); - plot.hooks.processDatapoints.push(processDatapoints); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'categories', - version: '1.0' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.crosshair.js b/src/legacy/ui/public/flot-charts/jquery.flot.crosshair.js deleted file mode 100644 index 5111695e3d12c..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.crosshair.js +++ /dev/null @@ -1,176 +0,0 @@ -/* Flot plugin for showing crosshairs when the mouse hovers over the plot. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The plugin supports these options: - - crosshair: { - mode: null or "x" or "y" or "xy" - color: color - lineWidth: number - } - -Set the mode to one of "x", "y" or "xy". The "x" mode enables a vertical -crosshair that lets you trace the values on the x axis, "y" enables a -horizontal crosshair and "xy" enables them both. "color" is the color of the -crosshair (default is "rgba(170, 0, 0, 0.80)"), "lineWidth" is the width of -the drawn lines (default is 1). - -The plugin also adds four public methods: - - - setCrosshair( pos ) - - Set the position of the crosshair. Note that this is cleared if the user - moves the mouse. "pos" is in coordinates of the plot and should be on the - form { x: xpos, y: ypos } (you can use x2/x3/... if you're using multiple - axes), which is coincidentally the same format as what you get from a - "plothover" event. If "pos" is null, the crosshair is cleared. - - - clearCrosshair() - - Clear the crosshair. - - - lockCrosshair(pos) - - Cause the crosshair to lock to the current location, no longer updating if - the user moves the mouse. Optionally supply a position (passed on to - setCrosshair()) to move it to. - - Example usage: - - var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; - $("#graph").bind( "plothover", function ( evt, position, item ) { - if ( item ) { - // Lock the crosshair to the data point being hovered - myFlot.lockCrosshair({ - x: item.datapoint[ 0 ], - y: item.datapoint[ 1 ] - }); - } else { - // Return normal crosshair operation - myFlot.unlockCrosshair(); - } - }); - - - unlockCrosshair() - - Free the crosshair to move again after locking it. -*/ - -(function ($) { - var options = { - crosshair: { - mode: null, // one of null, "x", "y" or "xy", - color: "rgba(170, 0, 0, 0.80)", - lineWidth: 1 - } - }; - - function init(plot) { - // position of crosshair in pixels - var crosshair = { x: -1, y: -1, locked: false }; - - plot.setCrosshair = function setCrosshair(pos) { - if (!pos) - crosshair.x = -1; - else { - var o = plot.p2c(pos); - crosshair.x = Math.max(0, Math.min(o.left, plot.width())); - crosshair.y = Math.max(0, Math.min(o.top, plot.height())); - } - - plot.triggerRedrawOverlay(); - }; - - plot.clearCrosshair = plot.setCrosshair; // passes null for pos - - plot.lockCrosshair = function lockCrosshair(pos) { - if (pos) - plot.setCrosshair(pos); - crosshair.locked = true; - }; - - plot.unlockCrosshair = function unlockCrosshair() { - crosshair.locked = false; - }; - - function onMouseOut(e) { - if (crosshair.locked) - return; - - if (crosshair.x != -1) { - crosshair.x = -1; - plot.triggerRedrawOverlay(); - } - } - - function onMouseMove(e) { - if (crosshair.locked) - return; - - if (plot.getSelection && plot.getSelection()) { - crosshair.x = -1; // hide the crosshair while selecting - return; - } - - var offset = plot.offset(); - crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); - crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); - plot.triggerRedrawOverlay(); - } - - plot.hooks.bindEvents.push(function (plot, eventHolder) { - if (!plot.getOptions().crosshair.mode) - return; - - eventHolder.mouseout(onMouseOut); - eventHolder.mousemove(onMouseMove); - }); - - plot.hooks.drawOverlay.push(function (plot, ctx) { - var c = plot.getOptions().crosshair; - if (!c.mode) - return; - - var plotOffset = plot.getPlotOffset(); - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - if (crosshair.x != -1) { - var adj = plot.getOptions().crosshair.lineWidth % 2 ? 0.5 : 0; - - ctx.strokeStyle = c.color; - ctx.lineWidth = c.lineWidth; - ctx.lineJoin = "round"; - - ctx.beginPath(); - if (c.mode.indexOf("x") != -1) { - var drawX = Math.floor(crosshair.x) + adj; - ctx.moveTo(drawX, 0); - ctx.lineTo(drawX, plot.height()); - } - if (c.mode.indexOf("y") != -1) { - var drawY = Math.floor(crosshair.y) + adj; - ctx.moveTo(0, drawY); - ctx.lineTo(plot.width(), drawY); - } - ctx.stroke(); - } - ctx.restore(); - }); - - plot.hooks.shutdown.push(function (plot, eventHolder) { - eventHolder.unbind("mouseout", onMouseOut); - eventHolder.unbind("mousemove", onMouseMove); - }); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'crosshair', - version: '1.0' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.errorbars.js b/src/legacy/ui/public/flot-charts/jquery.flot.errorbars.js deleted file mode 100644 index 655036e0db846..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.errorbars.js +++ /dev/null @@ -1,353 +0,0 @@ -/* Flot plugin for plotting error bars. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -Error bars are used to show standard deviation and other statistical -properties in a plot. - -* Created by Rui Pereira - rui (dot) pereira (at) gmail (dot) com - -This plugin allows you to plot error-bars over points. Set "errorbars" inside -the points series to the axis name over which there will be error values in -your data array (*even* if you do not intend to plot them later, by setting -"show: null" on xerr/yerr). - -The plugin supports these options: - - series: { - points: { - errorbars: "x" or "y" or "xy", - xerr: { - show: null/false or true, - asymmetric: null/false or true, - upperCap: null or "-" or function, - lowerCap: null or "-" or function, - color: null or color, - radius: null or number - }, - yerr: { same options as xerr } - } - } - -Each data point array is expected to be of the type: - - "x" [ x, y, xerr ] - "y" [ x, y, yerr ] - "xy" [ x, y, xerr, yerr ] - -Where xerr becomes xerr_lower,xerr_upper for the asymmetric error case, and -equivalently for yerr. E.g., a datapoint for the "xy" case with symmetric -error-bars on X and asymmetric on Y would be: - - [ x, y, xerr, yerr_lower, yerr_upper ] - -By default no end caps are drawn. Setting upperCap and/or lowerCap to "-" will -draw a small cap perpendicular to the error bar. They can also be set to a -user-defined drawing function, with (ctx, x, y, radius) as parameters, as e.g.: - - function drawSemiCircle( ctx, x, y, radius ) { - ctx.beginPath(); - ctx.arc( x, y, radius, 0, Math.PI, false ); - ctx.moveTo( x - radius, y ); - ctx.lineTo( x + radius, y ); - ctx.stroke(); - } - -Color and radius both default to the same ones of the points series if not -set. The independent radius parameter on xerr/yerr is useful for the case when -we may want to add error-bars to a line, without showing the interconnecting -points (with radius: 0), and still showing end caps on the error-bars. -shadowSize and lineWidth are derived as well from the points series. - -*/ - -(function ($) { - var options = { - series: { - points: { - errorbars: null, //should be 'x', 'y' or 'xy' - xerr: { err: 'x', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null}, - yerr: { err: 'y', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null} - } - } - }; - - function processRawData(plot, series, data, datapoints){ - if (!series.points.errorbars) - return; - - // x,y values - var format = [ - { x: true, number: true, required: true }, - { y: true, number: true, required: true } - ]; - - var errors = series.points.errorbars; - // error bars - first X then Y - if (errors == 'x' || errors == 'xy') { - // lower / upper error - if (series.points.xerr.asymmetric) { - format.push({ x: true, number: true, required: true }); - format.push({ x: true, number: true, required: true }); - } else - format.push({ x: true, number: true, required: true }); - } - if (errors == 'y' || errors == 'xy') { - // lower / upper error - if (series.points.yerr.asymmetric) { - format.push({ y: true, number: true, required: true }); - format.push({ y: true, number: true, required: true }); - } else - format.push({ y: true, number: true, required: true }); - } - datapoints.format = format; - } - - function parseErrors(series, i){ - - var points = series.datapoints.points; - - // read errors from points array - var exl = null, - exu = null, - eyl = null, - eyu = null; - var xerr = series.points.xerr, - yerr = series.points.yerr; - - var eb = series.points.errorbars; - // error bars - first X - if (eb == 'x' || eb == 'xy') { - if (xerr.asymmetric) { - exl = points[i + 2]; - exu = points[i + 3]; - if (eb == 'xy') - if (yerr.asymmetric){ - eyl = points[i + 4]; - eyu = points[i + 5]; - } else eyl = points[i + 4]; - } else { - exl = points[i + 2]; - if (eb == 'xy') - if (yerr.asymmetric) { - eyl = points[i + 3]; - eyu = points[i + 4]; - } else eyl = points[i + 3]; - } - // only Y - } else if (eb == 'y') - if (yerr.asymmetric) { - eyl = points[i + 2]; - eyu = points[i + 3]; - } else eyl = points[i + 2]; - - // symmetric errors? - if (exu == null) exu = exl; - if (eyu == null) eyu = eyl; - - var errRanges = [exl, exu, eyl, eyu]; - // nullify if not showing - if (!xerr.show){ - errRanges[0] = null; - errRanges[1] = null; - } - if (!yerr.show){ - errRanges[2] = null; - errRanges[3] = null; - } - return errRanges; - } - - function drawSeriesErrors(plot, ctx, s){ - - var points = s.datapoints.points, - ps = s.datapoints.pointsize, - ax = [s.xaxis, s.yaxis], - radius = s.points.radius, - err = [s.points.xerr, s.points.yerr]; - - //sanity check, in case some inverted axis hack is applied to flot - var invertX = false; - if (ax[0].p2c(ax[0].max) < ax[0].p2c(ax[0].min)) { - invertX = true; - var tmp = err[0].lowerCap; - err[0].lowerCap = err[0].upperCap; - err[0].upperCap = tmp; - } - - var invertY = false; - if (ax[1].p2c(ax[1].min) < ax[1].p2c(ax[1].max)) { - invertY = true; - var tmp = err[1].lowerCap; - err[1].lowerCap = err[1].upperCap; - err[1].upperCap = tmp; - } - - for (var i = 0; i < s.datapoints.points.length; i += ps) { - - //parse - var errRanges = parseErrors(s, i); - - //cycle xerr & yerr - for (var e = 0; e < err.length; e++){ - - var minmax = [ax[e].min, ax[e].max]; - - //draw this error? - if (errRanges[e * err.length]){ - - //data coordinates - var x = points[i], - y = points[i + 1]; - - //errorbar ranges - var upper = [x, y][e] + errRanges[e * err.length + 1], - lower = [x, y][e] - errRanges[e * err.length]; - - //points outside of the canvas - if (err[e].err == 'x') - if (y > ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) - continue; - if (err[e].err == 'y') - if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) - continue; - - // prevent errorbars getting out of the canvas - var drawUpper = true, - drawLower = true; - - if (upper > minmax[1]) { - drawUpper = false; - upper = minmax[1]; - } - if (lower < minmax[0]) { - drawLower = false; - lower = minmax[0]; - } - - //sanity check, in case some inverted axis hack is applied to flot - if ((err[e].err == 'x' && invertX) || (err[e].err == 'y' && invertY)) { - //swap coordinates - var tmp = lower; - lower = upper; - upper = tmp; - tmp = drawLower; - drawLower = drawUpper; - drawUpper = tmp; - tmp = minmax[0]; - minmax[0] = minmax[1]; - minmax[1] = tmp; - } - - // convert to pixels - x = ax[0].p2c(x), - y = ax[1].p2c(y), - upper = ax[e].p2c(upper); - lower = ax[e].p2c(lower); - minmax[0] = ax[e].p2c(minmax[0]); - minmax[1] = ax[e].p2c(minmax[1]); - - //same style as points by default - var lw = err[e].lineWidth ? err[e].lineWidth : s.points.lineWidth, - sw = s.points.shadowSize != null ? s.points.shadowSize : s.shadowSize; - - //shadow as for points - if (lw > 0 && sw > 0) { - var w = sw / 2; - ctx.lineWidth = w; - ctx.strokeStyle = "rgba(0,0,0,0.1)"; - drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w/2, minmax); - - ctx.strokeStyle = "rgba(0,0,0,0.2)"; - drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w/2, minmax); - } - - ctx.strokeStyle = err[e].color? err[e].color: s.color; - ctx.lineWidth = lw; - //draw it - drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); - } - } - } - } - - function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){ - - //shadow offset - y += offset; - upper += offset; - lower += offset; - - // error bar - avoid plotting over circles - if (err.err == 'x'){ - if (upper > x + radius) drawPath(ctx, [[upper,y],[Math.max(x + radius,minmax[0]),y]]); - else drawUpper = false; - if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius,minmax[1]),y],[lower,y]] ); - else drawLower = false; - } - else { - if (upper < y - radius) drawPath(ctx, [[x,upper],[x,Math.min(y - radius,minmax[0])]] ); - else drawUpper = false; - if (lower > y + radius) drawPath(ctx, [[x,Math.max(y + radius,minmax[1])],[x,lower]] ); - else drawLower = false; - } - - //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps - //this is a way to get errorbars on lines without visible connecting dots - radius = err.radius != null? err.radius: radius; - - // upper cap - if (drawUpper) { - if (err.upperCap == '-'){ - if (err.err=='x') drawPath(ctx, [[upper,y - radius],[upper,y + radius]] ); - else drawPath(ctx, [[x - radius,upper],[x + radius,upper]] ); - } else if ($.isFunction(err.upperCap)){ - if (err.err=='x') err.upperCap(ctx, upper, y, radius); - else err.upperCap(ctx, x, upper, radius); - } - } - // lower cap - if (drawLower) { - if (err.lowerCap == '-'){ - if (err.err=='x') drawPath(ctx, [[lower,y - radius],[lower,y + radius]] ); - else drawPath(ctx, [[x - radius,lower],[x + radius,lower]] ); - } else if ($.isFunction(err.lowerCap)){ - if (err.err=='x') err.lowerCap(ctx, lower, y, radius); - else err.lowerCap(ctx, x, lower, radius); - } - } - } - - function drawPath(ctx, pts){ - ctx.beginPath(); - ctx.moveTo(pts[0][0], pts[0][1]); - for (var p=1; p < pts.length; p++) - ctx.lineTo(pts[p][0], pts[p][1]); - ctx.stroke(); - } - - function draw(plot, ctx){ - var plotOffset = plot.getPlotOffset(); - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - $.each(plot.getData(), function (i, s) { - if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) - drawSeriesErrors(plot, ctx, s); - }); - ctx.restore(); - } - - function init(plot) { - plot.hooks.processRawData.push(processRawData); - plot.hooks.draw.push(draw); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'errorbars', - version: '1.0' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.fillbetween.js b/src/legacy/ui/public/flot-charts/jquery.flot.fillbetween.js deleted file mode 100644 index 18b15d26db8c9..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.fillbetween.js +++ /dev/null @@ -1,226 +0,0 @@ -/* Flot plugin for computing bottoms for filled line and bar charts. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The case: you've got two series that you want to fill the area between. In Flot -terms, you need to use one as the fill bottom of the other. You can specify the -bottom of each data point as the third coordinate manually, or you can use this -plugin to compute it for you. - -In order to name the other series, you need to give it an id, like this: - - var dataset = [ - { data: [ ... ], id: "foo" } , // use default bottom - { data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom - ]; - - $.plot($("#placeholder"), dataset, { lines: { show: true, fill: true }}); - -As a convenience, if the id given is a number that doesn't appear as an id in -the series, it is interpreted as the index in the array instead (so fillBetween: -0 can also mean the first series). - -Internally, the plugin modifies the datapoints in each series. For line series, -extra data points might be inserted through interpolation. Note that at points -where the bottom line is not defined (due to a null point or start/end of line), -the current line will show a gap too. The algorithm comes from the -jquery.flot.stack.js plugin, possibly some code could be shared. - -*/ - -(function ( $ ) { - - var options = { - series: { - fillBetween: null // or number - } - }; - - function init( plot ) { - - function findBottomSeries( s, allseries ) { - - var i; - - for ( i = 0; i < allseries.length; ++i ) { - if ( allseries[ i ].id === s.fillBetween ) { - return allseries[ i ]; - } - } - - if ( typeof s.fillBetween === "number" ) { - if ( s.fillBetween < 0 || s.fillBetween >= allseries.length ) { - return null; - } - return allseries[ s.fillBetween ]; - } - - return null; - } - - function computeFillBottoms( plot, s, datapoints ) { - - if ( s.fillBetween == null ) { - return; - } - - var other = findBottomSeries( s, plot.getData() ); - - if ( !other ) { - return; - } - - var ps = datapoints.pointsize, - points = datapoints.points, - otherps = other.datapoints.pointsize, - otherpoints = other.datapoints.points, - newpoints = [], - px, py, intery, qx, qy, bottom, - withlines = s.lines.show, - withbottom = ps > 2 && datapoints.format[2].y, - withsteps = withlines && s.lines.steps, - fromgap = true, - i = 0, - j = 0, - l, m; - - while ( true ) { - - if ( i >= points.length ) { - break; - } - - l = newpoints.length; - - if ( points[ i ] == null ) { - - // copy gaps - - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - - i += ps; - - } else if ( j >= otherpoints.length ) { - - // for lines, we can't use the rest of the points - - if ( !withlines ) { - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - } - - i += ps; - - } else if ( otherpoints[ j ] == null ) { - - // oops, got a gap - - for ( m = 0; m < ps; ++m ) { - newpoints.push( null ); - } - - fromgap = true; - j += otherps; - - } else { - - // cases where we actually got two points - - px = points[ i ]; - py = points[ i + 1 ]; - qx = otherpoints[ j ]; - qy = otherpoints[ j + 1 ]; - bottom = 0; - - if ( px === qx ) { - - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - - //newpoints[ l + 1 ] += qy; - bottom = qy; - - i += ps; - j += otherps; - - } else if ( px > qx ) { - - // we got past point below, might need to - // insert interpolated extra point - - if ( withlines && i > 0 && points[ i - ps ] != null ) { - intery = py + ( points[ i - ps + 1 ] - py ) * ( qx - px ) / ( points[ i - ps ] - px ); - newpoints.push( qx ); - newpoints.push( intery ); - for ( m = 2; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - bottom = qy; - } - - j += otherps; - - } else { // px < qx - - // if we come from a gap, we just skip this point - - if ( fromgap && withlines ) { - i += ps; - continue; - } - - for ( m = 0; m < ps; ++m ) { - newpoints.push( points[ i + m ] ); - } - - // we might be able to interpolate a point below, - // this can give us a better y - - if ( withlines && j > 0 && otherpoints[ j - otherps ] != null ) { - bottom = qy + ( otherpoints[ j - otherps + 1 ] - qy ) * ( px - qx ) / ( otherpoints[ j - otherps ] - qx ); - } - - //newpoints[l + 1] += bottom; - - i += ps; - } - - fromgap = false; - - if ( l !== newpoints.length && withbottom ) { - newpoints[ l + 2 ] = bottom; - } - } - - // maintain the line steps invariant - - if ( withsteps && l !== newpoints.length && l > 0 && - newpoints[ l ] !== null && - newpoints[ l ] !== newpoints[ l - ps ] && - newpoints[ l + 1 ] !== newpoints[ l - ps + 1 ] ) { - for (m = 0; m < ps; ++m) { - newpoints[ l + ps + m ] = newpoints[ l + m ]; - } - newpoints[ l + 1 ] = newpoints[ l - ps + 1 ]; - } - } - - datapoints.points = newpoints; - } - - plot.hooks.processDatapoints.push( computeFillBottoms ); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: "fillbetween", - version: "1.0" - }); - -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.image.js b/src/legacy/ui/public/flot-charts/jquery.flot.image.js deleted file mode 100644 index 178f0e69069ef..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.image.js +++ /dev/null @@ -1,241 +0,0 @@ -/* Flot plugin for plotting images. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The data syntax is [ [ image, x1, y1, x2, y2 ], ... ] where (x1, y1) and -(x2, y2) are where you intend the two opposite corners of the image to end up -in the plot. Image must be a fully loaded JavaScript image (you can make one -with new Image()). If the image is not complete, it's skipped when plotting. - -There are two helpers included for retrieving images. The easiest work the way -that you put in URLs instead of images in the data, like this: - - [ "myimage.png", 0, 0, 10, 10 ] - -Then call $.plot.image.loadData( data, options, callback ) where data and -options are the same as you pass in to $.plot. This loads the images, replaces -the URLs in the data with the corresponding images and calls "callback" when -all images are loaded (or failed loading). In the callback, you can then call -$.plot with the data set. See the included example. - -A more low-level helper, $.plot.image.load(urls, callback) is also included. -Given a list of URLs, it calls callback with an object mapping from URL to -Image object when all images are loaded or have failed loading. - -The plugin supports these options: - - series: { - images: { - show: boolean - anchor: "corner" or "center" - alpha: [ 0, 1 ] - } - } - -They can be specified for a specific series: - - $.plot( $("#placeholder"), [{ - data: [ ... ], - images: { ... } - ]) - -Note that because the data format is different from usual data points, you -can't use images with anything else in a specific data series. - -Setting "anchor" to "center" causes the pixels in the image to be anchored at -the corner pixel centers inside of at the pixel corners, effectively letting -half a pixel stick out to each side in the plot. - -A possible future direction could be support for tiling for large images (like -Google Maps). - -*/ - -(function ($) { - var options = { - series: { - images: { - show: false, - alpha: 1, - anchor: "corner" // or "center" - } - } - }; - - $.plot.image = {}; - - $.plot.image.loadDataImages = function (series, options, callback) { - var urls = [], points = []; - - var defaultShow = options.series.images.show; - - $.each(series, function (i, s) { - if (!(defaultShow || s.images.show)) - return; - - if (s.data) - s = s.data; - - $.each(s, function (i, p) { - if (typeof p[0] == "string") { - urls.push(p[0]); - points.push(p); - } - }); - }); - - $.plot.image.load(urls, function (loadedImages) { - $.each(points, function (i, p) { - var url = p[0]; - if (loadedImages[url]) - p[0] = loadedImages[url]; - }); - - callback(); - }); - } - - $.plot.image.load = function (urls, callback) { - var missing = urls.length, loaded = {}; - if (missing == 0) - callback({}); - - $.each(urls, function (i, url) { - var handler = function () { - --missing; - - loaded[url] = this; - - if (missing == 0) - callback(loaded); - }; - - $('').load(handler).error(handler).attr('src', url); - }); - }; - - function drawSeries(plot, ctx, series) { - var plotOffset = plot.getPlotOffset(); - - if (!series.images || !series.images.show) - return; - - var points = series.datapoints.points, - ps = series.datapoints.pointsize; - - for (var i = 0; i < points.length; i += ps) { - var img = points[i], - x1 = points[i + 1], y1 = points[i + 2], - x2 = points[i + 3], y2 = points[i + 4], - xaxis = series.xaxis, yaxis = series.yaxis, - tmp; - - // actually we should check img.complete, but it - // appears to be a somewhat unreliable indicator in - // IE6 (false even after load event) - if (!img || img.width <= 0 || img.height <= 0) - continue; - - if (x1 > x2) { - tmp = x2; - x2 = x1; - x1 = tmp; - } - if (y1 > y2) { - tmp = y2; - y2 = y1; - y1 = tmp; - } - - // if the anchor is at the center of the pixel, expand the - // image by 1/2 pixel in each direction - if (series.images.anchor == "center") { - tmp = 0.5 * (x2-x1) / (img.width - 1); - x1 -= tmp; - x2 += tmp; - tmp = 0.5 * (y2-y1) / (img.height - 1); - y1 -= tmp; - y2 += tmp; - } - - // clip - if (x1 == x2 || y1 == y2 || - x1 >= xaxis.max || x2 <= xaxis.min || - y1 >= yaxis.max || y2 <= yaxis.min) - continue; - - var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; - if (x1 < xaxis.min) { - sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); - x1 = xaxis.min; - } - - if (x2 > xaxis.max) { - sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); - x2 = xaxis.max; - } - - if (y1 < yaxis.min) { - sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); - y1 = yaxis.min; - } - - if (y2 > yaxis.max) { - sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); - y2 = yaxis.max; - } - - x1 = xaxis.p2c(x1); - x2 = xaxis.p2c(x2); - y1 = yaxis.p2c(y1); - y2 = yaxis.p2c(y2); - - // the transformation may have swapped us - if (x1 > x2) { - tmp = x2; - x2 = x1; - x1 = tmp; - } - if (y1 > y2) { - tmp = y2; - y2 = y1; - y1 = tmp; - } - - tmp = ctx.globalAlpha; - ctx.globalAlpha *= series.images.alpha; - ctx.drawImage(img, - sx1, sy1, sx2 - sx1, sy2 - sy1, - x1 + plotOffset.left, y1 + plotOffset.top, - x2 - x1, y2 - y1); - ctx.globalAlpha = tmp; - } - } - - function processRawData(plot, series, data, datapoints) { - if (!series.images.show) - return; - - // format is Image, x1, y1, x2, y2 (opposite corners) - datapoints.format = [ - { required: true }, - { x: true, number: true, required: true }, - { y: true, number: true, required: true }, - { x: true, number: true, required: true }, - { y: true, number: true, required: true } - ]; - } - - function init(plot) { - plot.hooks.processRawData.push(processRawData); - plot.hooks.drawSeries.push(drawSeries); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'image', - version: '1.1' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.js b/src/legacy/ui/public/flot-charts/jquery.flot.js deleted file mode 100644 index 43db1cc3d93db..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.js +++ /dev/null @@ -1,3168 +0,0 @@ -/* JavaScript plotting library for jQuery, version 0.8.3. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -*/ - -// first an inline dependency, jquery.colorhelpers.js, we inline it here -// for convenience - -/* Plugin for jQuery for working with colors. - * - * Version 1.1. - * - * Inspiration from jQuery color animation plugin by John Resig. - * - * Released under the MIT license by Ole Laursen, October 2009. - * - * Examples: - * - * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() - * var c = $.color.extract($("#mydiv"), 'background-color'); - * console.log(c.r, c.g, c.b, c.a); - * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" - * - * Note that .scale() and .add() return the same modified object - * instead of making a new one. - * - * V. 1.1: Fix error handling so e.g. parsing an empty string does - * produce a color rather than just crashing. - */ -(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); - -// the actual Flot code -(function($) { - - // Cache the prototype hasOwnProperty for faster access - - var hasOwnProperty = Object.prototype.hasOwnProperty; - - // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM - // operation produces the same effect as detach, i.e. removing the element - // without touching its jQuery data. - - // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+. - - if (!$.fn.detach) { - $.fn.detach = function() { - return this.each(function() { - if (this.parentNode) { - this.parentNode.removeChild( this ); - } - }); - }; - } - - /////////////////////////////////////////////////////////////////////////// - // The Canvas object is a wrapper around an HTML5 tag. - // - // @constructor - // @param {string} cls List of classes to apply to the canvas. - // @param {element} container Element onto which to append the canvas. - // - // Requiring a container is a little iffy, but unfortunately canvas - // operations don't work unless the canvas is attached to the DOM. - - function Canvas(cls, container) { - - var element = container.children("." + cls)[0]; - - if (element == null) { - - element = document.createElement("canvas"); - element.className = cls; - - $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) - .appendTo(container); - - // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas - - if (!element.getContext) { - if (window.G_vmlCanvasManager) { - element = window.G_vmlCanvasManager.initElement(element); - } else { - throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); - } - } - } - - this.element = element; - - var context = this.context = element.getContext("2d"); - - // Determine the screen's ratio of physical to device-independent - // pixels. This is the ratio between the canvas width that the browser - // advertises and the number of pixels actually present in that space. - - // The iPhone 4, for example, has a device-independent width of 320px, - // but its screen is actually 640px wide. It therefore has a pixel - // ratio of 2, while most normal devices have a ratio of 1. - - var devicePixelRatio = window.devicePixelRatio || 1, - backingStoreRatio = - context.webkitBackingStorePixelRatio || - context.mozBackingStorePixelRatio || - context.msBackingStorePixelRatio || - context.oBackingStorePixelRatio || - context.backingStorePixelRatio || 1; - - this.pixelRatio = devicePixelRatio / backingStoreRatio; - - // Size the canvas to match the internal dimensions of its container - - this.resize(container.width(), container.height()); - - // Collection of HTML div layers for text overlaid onto the canvas - - this.textContainer = null; - this.text = {}; - - // Cache of text fragments and metrics, so we can avoid expensively - // re-calculating them when the plot is re-rendered in a loop. - - this._textCache = {}; - } - - // Resizes the canvas to the given dimensions. - // - // @param {number} width New width of the canvas, in pixels. - // @param {number} width New height of the canvas, in pixels. - - Canvas.prototype.resize = function(width, height) { - - if (width <= 0 || height <= 0) { - throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); - } - - var element = this.element, - context = this.context, - pixelRatio = this.pixelRatio; - - // Resize the canvas, increasing its density based on the display's - // pixel ratio; basically giving it more pixels without increasing the - // size of its element, to take advantage of the fact that retina - // displays have that many more pixels in the same advertised space. - - // Resizing should reset the state (excanvas seems to be buggy though) - - if (this.width != width) { - element.width = width * pixelRatio; - element.style.width = width + "px"; - this.width = width; - } - - if (this.height != height) { - element.height = height * pixelRatio; - element.style.height = height + "px"; - this.height = height; - } - - // Save the context, so we can reset in case we get replotted. The - // restore ensure that we're really back at the initial state, and - // should be safe even if we haven't saved the initial state yet. - - context.restore(); - context.save(); - - // Scale the coordinate space to match the display density; so even though we - // may have twice as many pixels, we still want lines and other drawing to - // appear at the same size; the extra pixels will just make them crisper. - - context.scale(pixelRatio, pixelRatio); - }; - - // Clears the entire canvas area, not including any overlaid HTML text - - Canvas.prototype.clear = function() { - this.context.clearRect(0, 0, this.width, this.height); - }; - - // Finishes rendering the canvas, including managing the text overlay. - - Canvas.prototype.render = function() { - - var cache = this._textCache; - - // For each text layer, add elements marked as active that haven't - // already been rendered, and remove those that are no longer active. - - for (var layerKey in cache) { - if (hasOwnProperty.call(cache, layerKey)) { - - var layer = this.getTextLayer(layerKey), - layerCache = cache[layerKey]; - - layer.hide(); - - for (var styleKey in layerCache) { - if (hasOwnProperty.call(layerCache, styleKey)) { - var styleCache = layerCache[styleKey]; - for (var key in styleCache) { - if (hasOwnProperty.call(styleCache, key)) { - - var positions = styleCache[key].positions; - - for (var i = 0, position; position = positions[i]; i++) { - if (position.active) { - if (!position.rendered) { - layer.append(position.element); - position.rendered = true; - } - } else { - positions.splice(i--, 1); - if (position.rendered) { - position.element.detach(); - } - } - } - - if (positions.length == 0) { - delete styleCache[key]; - } - } - } - } - } - - layer.show(); - } - } - }; - - // Creates (if necessary) and returns the text overlay container. - // - // @param {string} classes String of space-separated CSS classes used to - // uniquely identify the text layer. - // @return {object} The jQuery-wrapped text-layer div. - - Canvas.prototype.getTextLayer = function(classes) { - - var layer = this.text[classes]; - - // Create the text layer if it doesn't exist - - if (layer == null) { - - // Create the text layer container, if it doesn't exist - - if (this.textContainer == null) { - this.textContainer = $("
") - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0, - 'font-size': "smaller", - color: "#545454" - }) - .insertAfter(this.element); - } - - layer = this.text[classes] = $("
") - .addClass(classes) - .css({ - position: "absolute", - top: 0, - left: 0, - bottom: 0, - right: 0 - }) - .appendTo(this.textContainer); - } - - return layer; - }; - - // Creates (if necessary) and returns a text info object. - // - // The object looks like this: - // - // { - // width: Width of the text's wrapper div. - // height: Height of the text's wrapper div. - // element: The jQuery-wrapped HTML div containing the text. - // positions: Array of positions at which this text is drawn. - // } - // - // The positions array contains objects that look like this: - // - // { - // active: Flag indicating whether the text should be visible. - // rendered: Flag indicating whether the text is currently visible. - // element: The jQuery-wrapped HTML div containing the text. - // x: X coordinate at which to draw the text. - // y: Y coordinate at which to draw the text. - // } - // - // Each position after the first receives a clone of the original element. - // - // The idea is that that the width, height, and general 'identity' of the - // text is constant no matter where it is placed; the placements are a - // secondary property. - // - // Canvas maintains a cache of recently-used text info objects; getTextInfo - // either returns the cached element or creates a new entry. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {string} text Text string to retrieve info for. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @return {object} a text info object. - - Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { - - var textStyle, layerCache, styleCache, info; - - // Cast the value to a string, in case we were given a number or such - - text = "" + text; - - // If the font is a font-spec object, generate a CSS font definition - - if (typeof font === "object") { - textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; - } else { - textStyle = font; - } - - // Retrieve (or create) the cache for the text's layer and styles - - layerCache = this._textCache[layer]; - - if (layerCache == null) { - layerCache = this._textCache[layer] = {}; - } - - styleCache = layerCache[textStyle]; - - if (styleCache == null) { - styleCache = layerCache[textStyle] = {}; - } - - info = styleCache[text]; - - // If we can't find a matching element in our cache, create a new one - - if (info == null) { - - var element = $("
").html(text) - .css({ - position: "absolute", - 'max-width': width, - top: -9999 - }) - .appendTo(this.getTextLayer(layer)); - - if (typeof font === "object") { - element.css({ - font: textStyle, - color: font.color - }); - } else if (typeof font === "string") { - element.addClass(font); - } - - info = styleCache[text] = { - width: element.outerWidth(true), - height: element.outerHeight(true), - element: element, - positions: [] - }; - - element.detach(); - } - - return info; - }; - - // Adds a text string to the canvas text overlay. - // - // The text isn't drawn immediately; it is marked as rendering, which will - // result in its addition to the canvas on the next render pass. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number} x X coordinate at which to draw the text. - // @param {number} y Y coordinate at which to draw the text. - // @param {string} text Text string to draw. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which to rotate the text, in degrees. - // Angle is currently unused, it will be implemented in the future. - // @param {number=} width Maximum width of the text before it wraps. - // @param {string=} halign Horizontal alignment of the text; either "left", - // "center" or "right". - // @param {string=} valign Vertical alignment of the text; either "top", - // "middle" or "bottom". - - Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { - - var info = this.getTextInfo(layer, text, font, angle, width), - positions = info.positions; - - // Tweak the div's position to match the text's alignment - - if (halign == "center") { - x -= info.width / 2; - } else if (halign == "right") { - x -= info.width; - } - - if (valign == "middle") { - y -= info.height / 2; - } else if (valign == "bottom") { - y -= info.height; - } - - // Determine whether this text already exists at this position. - // If so, mark it for inclusion in the next render pass. - - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = true; - return; - } - } - - // If the text doesn't exist at this position, create a new entry - - // For the very first position we'll re-use the original element, - // while for subsequent ones we'll clone it. - - position = { - active: true, - rendered: false, - element: positions.length ? info.element.clone() : info.element, - x: x, - y: y - }; - - positions.push(position); - - // Move the element to its final position within the container - - position.element.css({ - top: Math.round(y), - left: Math.round(x), - 'text-align': halign // In case the text wraps - }); - }; - - // Removes one or more text strings from the canvas text overlay. - // - // If no parameters are given, all text within the layer is removed. - // - // Note that the text is not immediately removed; it is simply marked as - // inactive, which will result in its removal on the next render pass. - // This avoids the performance penalty for 'clear and redraw' behavior, - // where we potentially get rid of all text on a layer, but will likely - // add back most or all of it later, as when redrawing axes, for example. - // - // @param {string} layer A string of space-separated CSS classes uniquely - // identifying the layer containing this text. - // @param {number=} x X coordinate of the text. - // @param {number=} y Y coordinate of the text. - // @param {string=} text Text string to remove. - // @param {(string|object)=} font Either a string of space-separated CSS - // classes or a font-spec object, defining the text's font and style. - // @param {number=} angle Angle at which the text is rotated, in degrees. - // Angle is currently unused, it will be implemented in the future. - - Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { - if (text == null) { - var layerCache = this._textCache[layer]; - if (layerCache != null) { - for (var styleKey in layerCache) { - if (hasOwnProperty.call(layerCache, styleKey)) { - var styleCache = layerCache[styleKey]; - for (var key in styleCache) { - if (hasOwnProperty.call(styleCache, key)) { - var positions = styleCache[key].positions; - for (var i = 0, position; position = positions[i]; i++) { - position.active = false; - } - } - } - } - } - } - } else { - var positions = this.getTextInfo(layer, text, font, angle).positions; - for (var i = 0, position; position = positions[i]; i++) { - if (position.x == x && position.y == y) { - position.active = false; - } - } - } - }; - - /////////////////////////////////////////////////////////////////////////// - // The top-level container for the entire plot. - - function Plot(placeholder, data_, options_, plugins) { - // data is on the form: - // [ series1, series2 ... ] - // where series is either just the data as [ [x1, y1], [x2, y2], ... ] - // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } - - var series = [], - options = { - // the color theme used for graphs - colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], - legend: { - show: true, - noColumns: 1, // number of columns in legend table - labelFormatter: null, // fn: string -> string - labelBoxBorderColor: "#ccc", // border color for the little label boxes - container: null, // container (as jQuery object) to put legend in, null means default on top of graph - position: "ne", // position of default legend container within plot - margin: 5, // distance from grid edge to default legend container within plot - backgroundColor: null, // null means auto-detect - backgroundOpacity: 0.85, // set to 0 to avoid background - sorted: null // default to no legend sorting - }, - xaxis: { - show: null, // null = auto-detect, true = always, false = never - position: "bottom", // or "top" - mode: null, // null or "time" - font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } - color: null, // base color, labels, ticks - tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" - transform: null, // null or f: number -> number to transform axis - inverseTransform: null, // if transform is set, this should be the inverse function - min: null, // min. value to show, null means set automatically - max: null, // max. value to show, null means set automatically - autoscaleMargin: null, // margin in % to add if auto-setting min/max - ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks - tickFormatter: null, // fn: number -> string - labelWidth: null, // size of tick labels in pixels - labelHeight: null, - reserveSpace: null, // whether to reserve space even if axis isn't shown - tickLength: null, // size in pixels of ticks, or "full" for whole line - alignTicksWithAxis: null, // axis number or null for no sync - tickDecimals: null, // no. of decimals, null means auto - tickSize: null, // number or [number, "unit"] - minTickSize: null // number or [number, "unit"] - }, - yaxis: { - autoscaleMargin: 0.02, - position: "left" // or "right" - }, - xaxes: [], - yaxes: [], - series: { - points: { - show: false, - radius: 3, - lineWidth: 2, // in pixels - fill: true, - fillColor: "#ffffff", - symbol: "circle" // or callback - }, - lines: { - // we don't put in show: false so we can see - // whether lines were actively disabled - lineWidth: 2, // in pixels - fill: false, - fillColor: null, - steps: false - // Omit 'zero', so we can later default its value to - // match that of the 'fill' option. - }, - bars: { - show: false, - lineWidth: 2, // in pixels - barWidth: 1, // in units of the x axis - fill: true, - fillColor: null, - align: "left", // "left", "right", or "center" - horizontal: false, - zero: true - }, - shadowSize: 3, - highlightColor: null - }, - grid: { - show: true, - aboveData: false, - color: "#545454", // primary color used for outline and labels - backgroundColor: null, // null for transparent, else color - borderColor: null, // set if different from the grid color - tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" - margin: 0, // distance from the canvas edge to the grid - labelMargin: 5, // in pixels - axisMargin: 8, // in pixels - borderWidth: 2, // in pixels - minBorderMargin: null, // in pixels, null means taken from points radius - markings: null, // array of ranges or fn: axes -> array of ranges - markingsColor: "#f4f4f4", - markingsLineWidth: 2, - // interactive stuff - clickable: false, - hoverable: false, - autoHighlight: true, // highlight in case mouse is near - mouseActiveRadius: 10 // how far the mouse can be away to activate an item - }, - interaction: { - redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow - }, - hooks: {} - }, - surface = null, // the canvas for the plot itself - overlay = null, // canvas for interactive stuff on top of plot - eventHolder = null, // jQuery object that events should be bound to - ctx = null, octx = null, - xaxes = [], yaxes = [], - plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, - plotWidth = 0, plotHeight = 0, - hooks = { - processOptions: [], - processRawData: [], - processDatapoints: [], - processOffset: [], - drawBackground: [], - drawSeries: [], - draw: [], - bindEvents: [], - drawOverlay: [], - shutdown: [] - }, - plot = this; - - // public functions - plot.setData = setData; - plot.setupGrid = setupGrid; - plot.draw = draw; - plot.getPlaceholder = function() { return placeholder; }; - plot.getCanvas = function() { return surface.element; }; - plot.getPlotOffset = function() { return plotOffset; }; - plot.width = function () { return plotWidth; }; - plot.height = function () { return plotHeight; }; - plot.offset = function () { - var o = eventHolder.offset(); - o.left += plotOffset.left; - o.top += plotOffset.top; - return o; - }; - plot.getData = function () { return series; }; - plot.getAxes = function () { - var res = {}, i; - $.each(xaxes.concat(yaxes), function (_, axis) { - if (axis) - res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; - }); - return res; - }; - plot.getXAxes = function () { return xaxes; }; - plot.getYAxes = function () { return yaxes; }; - plot.c2p = canvasToAxisCoords; - plot.p2c = axisToCanvasCoords; - plot.getOptions = function () { return options; }; - plot.highlight = highlight; - plot.unhighlight = unhighlight; - plot.triggerRedrawOverlay = triggerRedrawOverlay; - plot.pointOffset = function(point) { - return { - left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), - top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) - }; - }; - plot.shutdown = shutdown; - plot.destroy = function () { - shutdown(); - placeholder.removeData("plot").empty(); - - series = []; - options = null; - surface = null; - overlay = null; - eventHolder = null; - ctx = null; - octx = null; - xaxes = []; - yaxes = []; - hooks = null; - highlights = []; - plot = null; - }; - plot.resize = function () { - var width = placeholder.width(), - height = placeholder.height(); - surface.resize(width, height); - overlay.resize(width, height); - }; - - // public attributes - plot.hooks = hooks; - - // initialize - initPlugins(plot); - parseOptions(options_); - setupCanvases(); - setData(data_); - setupGrid(); - draw(); - bindEvents(); - - - function executeHooks(hook, args) { - args = [plot].concat(args); - for (var i = 0; i < hook.length; ++i) - hook[i].apply(this, args); - } - - function initPlugins() { - - // References to key classes, allowing plugins to modify them - - var classes = { - Canvas: Canvas - }; - - for (var i = 0; i < plugins.length; ++i) { - var p = plugins[i]; - p.init(plot, classes); - if (p.options) - $.extend(true, options, p.options); - } - } - - function parseOptions(opts) { - - $.extend(true, options, opts); - - // $.extend merges arrays, rather than replacing them. When less - // colors are provided than the size of the default palette, we - // end up with those colors plus the remaining defaults, which is - // not expected behavior; avoid it by replacing them here. - - if (opts && opts.colors) { - options.colors = opts.colors; - } - - if (options.xaxis.color == null) - options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - if (options.yaxis.color == null) - options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - - if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility - options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; - if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility - options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; - - if (options.grid.borderColor == null) - options.grid.borderColor = options.grid.color; - if (options.grid.tickColor == null) - options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); - - // Fill in defaults for axis options, including any unspecified - // font-spec fields, if a font-spec was provided. - - // If no x/y axis options were provided, create one of each anyway, - // since the rest of the code assumes that they exist. - - var i, axisOptions, axisCount, - fontSize = placeholder.css("font-size"), - fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13, - fontDefaults = { - style: placeholder.css("font-style"), - size: Math.round(0.8 * fontSizeDefault), - variant: placeholder.css("font-variant"), - weight: placeholder.css("font-weight"), - family: placeholder.css("font-family") - }; - - axisCount = options.xaxes.length || 1; - for (i = 0; i < axisCount; ++i) { - - axisOptions = options.xaxes[i]; - if (axisOptions && !axisOptions.tickColor) { - axisOptions.tickColor = axisOptions.color; - } - - axisOptions = $.extend(true, {}, options.xaxis, axisOptions); - options.xaxes[i] = axisOptions; - - if (axisOptions.font) { - axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); - if (!axisOptions.font.color) { - axisOptions.font.color = axisOptions.color; - } - if (!axisOptions.font.lineHeight) { - axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); - } - } - } - - axisCount = options.yaxes.length || 1; - for (i = 0; i < axisCount; ++i) { - - axisOptions = options.yaxes[i]; - if (axisOptions && !axisOptions.tickColor) { - axisOptions.tickColor = axisOptions.color; - } - - axisOptions = $.extend(true, {}, options.yaxis, axisOptions); - options.yaxes[i] = axisOptions; - - if (axisOptions.font) { - axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); - if (!axisOptions.font.color) { - axisOptions.font.color = axisOptions.color; - } - if (!axisOptions.font.lineHeight) { - axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15); - } - } - } - - // backwards compatibility, to be removed in future - if (options.xaxis.noTicks && options.xaxis.ticks == null) - options.xaxis.ticks = options.xaxis.noTicks; - if (options.yaxis.noTicks && options.yaxis.ticks == null) - options.yaxis.ticks = options.yaxis.noTicks; - if (options.x2axis) { - options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); - options.xaxes[1].position = "top"; - // Override the inherit to allow the axis to auto-scale - if (options.x2axis.min == null) { - options.xaxes[1].min = null; - } - if (options.x2axis.max == null) { - options.xaxes[1].max = null; - } - } - if (options.y2axis) { - options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); - options.yaxes[1].position = "right"; - // Override the inherit to allow the axis to auto-scale - if (options.y2axis.min == null) { - options.yaxes[1].min = null; - } - if (options.y2axis.max == null) { - options.yaxes[1].max = null; - } - } - if (options.grid.coloredAreas) - options.grid.markings = options.grid.coloredAreas; - if (options.grid.coloredAreasColor) - options.grid.markingsColor = options.grid.coloredAreasColor; - if (options.lines) - $.extend(true, options.series.lines, options.lines); - if (options.points) - $.extend(true, options.series.points, options.points); - if (options.bars) - $.extend(true, options.series.bars, options.bars); - if (options.shadowSize != null) - options.series.shadowSize = options.shadowSize; - if (options.highlightColor != null) - options.series.highlightColor = options.highlightColor; - - // save options on axes for future reference - for (i = 0; i < options.xaxes.length; ++i) - getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; - for (i = 0; i < options.yaxes.length; ++i) - getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; - - // add hooks from options - for (var n in hooks) - if (options.hooks[n] && options.hooks[n].length) - hooks[n] = hooks[n].concat(options.hooks[n]); - - executeHooks(hooks.processOptions, [options]); - } - - function setData(d) { - series = parseData(d); - fillInSeriesOptions(); - processData(); - } - - function parseData(d) { - var res = []; - for (var i = 0; i < d.length; ++i) { - var s = $.extend(true, {}, options.series); - - if (d[i].data != null) { - s.data = d[i].data; // move the data instead of deep-copy - delete d[i].data; - - $.extend(true, s, d[i]); - - d[i].data = s.data; - } - else - s.data = d[i]; - res.push(s); - } - - return res; - } - - function axisNumber(obj, coord) { - var a = obj[coord + "axis"]; - if (typeof a == "object") // if we got a real axis, extract number - a = a.n; - if (typeof a != "number") - a = 1; // default to first axis - return a; - } - - function allAxes() { - // return flat array without annoying null entries - return $.grep(xaxes.concat(yaxes), function (a) { return a; }); - } - - function canvasToAxisCoords(pos) { - // return an object with x/y corresponding to all used axes - var res = {}, i, axis; - for (i = 0; i < xaxes.length; ++i) { - axis = xaxes[i]; - if (axis && axis.used) - res["x" + axis.n] = axis.c2p(pos.left); - } - - for (i = 0; i < yaxes.length; ++i) { - axis = yaxes[i]; - if (axis && axis.used) - res["y" + axis.n] = axis.c2p(pos.top); - } - - if (res.x1 !== undefined) - res.x = res.x1; - if (res.y1 !== undefined) - res.y = res.y1; - - return res; - } - - function axisToCanvasCoords(pos) { - // get canvas coords from the first pair of x/y found in pos - var res = {}, i, axis, key; - - for (i = 0; i < xaxes.length; ++i) { - axis = xaxes[i]; - if (axis && axis.used) { - key = "x" + axis.n; - if (pos[key] == null && axis.n == 1) - key = "x"; - - if (pos[key] != null) { - res.left = axis.p2c(pos[key]); - break; - } - } - } - - for (i = 0; i < yaxes.length; ++i) { - axis = yaxes[i]; - if (axis && axis.used) { - key = "y" + axis.n; - if (pos[key] == null && axis.n == 1) - key = "y"; - - if (pos[key] != null) { - res.top = axis.p2c(pos[key]); - break; - } - } - } - - return res; - } - - function getOrCreateAxis(axes, number) { - if (!axes[number - 1]) - axes[number - 1] = { - n: number, // save the number for future reference - direction: axes == xaxes ? "x" : "y", - options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) - }; - - return axes[number - 1]; - } - - function fillInSeriesOptions() { - - var neededColors = series.length, maxIndex = -1, i; - - // Subtract the number of series that already have fixed colors or - // color indexes from the number that we still need to generate. - - for (i = 0; i < series.length; ++i) { - var sc = series[i].color; - if (sc != null) { - neededColors--; - if (typeof sc == "number" && sc > maxIndex) { - maxIndex = sc; - } - } - } - - // If any of the series have fixed color indexes, then we need to - // generate at least as many colors as the highest index. - - if (neededColors <= maxIndex) { - neededColors = maxIndex + 1; - } - - // Generate all the colors, using first the option colors and then - // variations on those colors once they're exhausted. - - var c, colors = [], colorPool = options.colors, - colorPoolSize = colorPool.length, variation = 0; - - for (i = 0; i < neededColors; i++) { - - c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); - - // Each time we exhaust the colors in the pool we adjust - // a scaling factor used to produce more variations on - // those colors. The factor alternates negative/positive - // to produce lighter/darker colors. - - // Reset the variation after every few cycles, or else - // it will end up producing only white or black colors. - - if (i % colorPoolSize == 0 && i) { - if (variation >= 0) { - if (variation < 0.5) { - variation = -variation - 0.2; - } else variation = 0; - } else variation = -variation; - } - - colors[i] = c.scale('rgb', 1 + variation); - } - - // Finalize the series options, filling in their colors - - var colori = 0, s; - for (i = 0; i < series.length; ++i) { - s = series[i]; - - // assign colors - if (s.color == null) { - s.color = colors[colori].toString(); - ++colori; - } - else if (typeof s.color == "number") - s.color = colors[s.color].toString(); - - // turn on lines automatically in case nothing is set - if (s.lines.show == null) { - var v, show = true; - for (v in s) - if (s[v] && s[v].show) { - show = false; - break; - } - if (show) - s.lines.show = true; - } - - // If nothing was provided for lines.zero, default it to match - // lines.fill, since areas by default should extend to zero. - - if (s.lines.zero == null) { - s.lines.zero = !!s.lines.fill; - } - - // setup axes - s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); - s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); - } - } - - function processData() { - var topSentry = Number.POSITIVE_INFINITY, - bottomSentry = Number.NEGATIVE_INFINITY, - fakeInfinity = Number.MAX_VALUE, - i, j, k, m, length, - s, points, ps, x, y, axis, val, f, p, - data, format; - - function updateAxis(axis, min, max) { - if (min < axis.datamin && min != -fakeInfinity) - axis.datamin = min; - if (max > axis.datamax && max != fakeInfinity) - axis.datamax = max; - } - - $.each(allAxes(), function (_, axis) { - // init axis - axis.datamin = topSentry; - axis.datamax = bottomSentry; - axis.used = false; - }); - - for (i = 0; i < series.length; ++i) { - s = series[i]; - s.datapoints = { points: [] }; - - executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); - } - - // first pass: clean and copy data - for (i = 0; i < series.length; ++i) { - s = series[i]; - - data = s.data; - format = s.datapoints.format; - - if (!format) { - format = []; - // find out how to copy - format.push({ x: true, number: true, required: true }); - format.push({ y: true, number: true, required: true }); - - if (s.bars.show || (s.lines.show && s.lines.fill)) { - var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); - format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); - if (s.bars.horizontal) { - delete format[format.length - 1].y; - format[format.length - 1].x = true; - } - } - - s.datapoints.format = format; - } - - if (s.datapoints.pointsize != null) - continue; // already filled in - - s.datapoints.pointsize = format.length; - - ps = s.datapoints.pointsize; - points = s.datapoints.points; - - var insertSteps = s.lines.show && s.lines.steps; - s.xaxis.used = s.yaxis.used = true; - - for (j = k = 0; j < data.length; ++j, k += ps) { - p = data[j]; - - var nullify = p == null; - if (!nullify) { - for (m = 0; m < ps; ++m) { - val = p[m]; - f = format[m]; - - if (f) { - if (f.number && val != null) { - val = +val; // convert to number - if (isNaN(val)) - val = null; - else if (val == Infinity) - val = fakeInfinity; - else if (val == -Infinity) - val = -fakeInfinity; - } - - if (val == null) { - if (f.required) - nullify = true; - - if (f.defaultValue != null) - val = f.defaultValue; - } - } - - points[k + m] = val; - } - } - - if (nullify) { - for (m = 0; m < ps; ++m) { - val = points[k + m]; - if (val != null) { - f = format[m]; - // extract min/max info - if (f.autoscale !== false) { - if (f.x) { - updateAxis(s.xaxis, val, val); - } - if (f.y) { - updateAxis(s.yaxis, val, val); - } - } - } - points[k + m] = null; - } - } - else { - // a little bit of line specific stuff that - // perhaps shouldn't be here, but lacking - // better means... - if (insertSteps && k > 0 - && points[k - ps] != null - && points[k - ps] != points[k] - && points[k - ps + 1] != points[k + 1]) { - // copy the point to make room for a middle point - for (m = 0; m < ps; ++m) - points[k + ps + m] = points[k + m]; - - // middle point has same y - points[k + 1] = points[k - ps + 1]; - - // we've added a point, better reflect that - k += ps; - } - } - } - } - - // give the hooks a chance to run - for (i = 0; i < series.length; ++i) { - s = series[i]; - - executeHooks(hooks.processDatapoints, [ s, s.datapoints]); - } - - // second pass: find datamax/datamin for auto-scaling - for (i = 0; i < series.length; ++i) { - s = series[i]; - points = s.datapoints.points; - ps = s.datapoints.pointsize; - format = s.datapoints.format; - - var xmin = topSentry, ymin = topSentry, - xmax = bottomSentry, ymax = bottomSentry; - - for (j = 0; j < points.length; j += ps) { - if (points[j] == null) - continue; - - for (m = 0; m < ps; ++m) { - val = points[j + m]; - f = format[m]; - if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) - continue; - - if (f.x) { - if (val < xmin) - xmin = val; - if (val > xmax) - xmax = val; - } - if (f.y) { - if (val < ymin) - ymin = val; - if (val > ymax) - ymax = val; - } - } - } - - if (s.bars.show) { - // make sure we got room for the bar on the dancing floor - var delta; - - switch (s.bars.align) { - case "left": - delta = 0; - break; - case "right": - delta = -s.bars.barWidth; - break; - default: - delta = -s.bars.barWidth / 2; - } - - if (s.bars.horizontal) { - ymin += delta; - ymax += delta + s.bars.barWidth; - } - else { - xmin += delta; - xmax += delta + s.bars.barWidth; - } - } - - updateAxis(s.xaxis, xmin, xmax); - updateAxis(s.yaxis, ymin, ymax); - } - - $.each(allAxes(), function (_, axis) { - if (axis.datamin == topSentry) - axis.datamin = null; - if (axis.datamax == bottomSentry) - axis.datamax = null; - }); - } - - function setupCanvases() { - - // Make sure the placeholder is clear of everything except canvases - // from a previous plot in this container that we'll try to re-use. - - placeholder.css("padding", 0) // padding messes up the positioning - .children().filter(function(){ - return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base'); - }).remove(); - - if (placeholder.css("position") == 'static') - placeholder.css("position", "relative"); // for positioning labels and overlay - - surface = new Canvas("flot-base", placeholder); - overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features - - ctx = surface.context; - octx = overlay.context; - - // define which element we're listening for events on - eventHolder = $(overlay.element).unbind(); - - // If we're re-using a plot object, shut down the old one - - var existing = placeholder.data("plot"); - - if (existing) { - existing.shutdown(); - overlay.clear(); - } - - // save in case we get replotted - placeholder.data("plot", plot); - } - - function bindEvents() { - // bind events - if (options.grid.hoverable) { - eventHolder.mousemove(onMouseMove); - - // Use bind, rather than .mouseleave, because we officially - // still support jQuery 1.2.6, which doesn't define a shortcut - // for mouseenter or mouseleave. This was a bug/oversight that - // was fixed somewhere around 1.3.x. We can return to using - // .mouseleave when we drop support for 1.2.6. - - eventHolder.bind("mouseleave", onMouseLeave); - } - - if (options.grid.clickable) - eventHolder.click(onClick); - - executeHooks(hooks.bindEvents, [eventHolder]); - } - - function shutdown() { - if (redrawTimeout) - clearTimeout(redrawTimeout); - - eventHolder.unbind("mousemove", onMouseMove); - eventHolder.unbind("mouseleave", onMouseLeave); - eventHolder.unbind("click", onClick); - - executeHooks(hooks.shutdown, [eventHolder]); - } - - function setTransformationHelpers(axis) { - // set helper functions on the axis, assumes plot area - // has been computed already - - function identity(x) { return x; } - - var s, m, t = axis.options.transform || identity, - it = axis.options.inverseTransform; - - // precompute how much the axis is scaling a point - // in canvas space - if (axis.direction == "x") { - s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); - m = Math.min(t(axis.max), t(axis.min)); - } - else { - s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); - s = -s; - m = Math.max(t(axis.max), t(axis.min)); - } - - // data point to canvas coordinate - if (t == identity) // slight optimization - axis.p2c = function (p) { return (p - m) * s; }; - else - axis.p2c = function (p) { return (t(p) - m) * s; }; - // canvas coordinate to data point - if (!it) - axis.c2p = function (c) { return m + c / s; }; - else - axis.c2p = function (c) { return it(m + c / s); }; - } - - function measureTickLabels(axis) { - - var opts = axis.options, - ticks = axis.ticks || [], - labelWidth = opts.labelWidth || 0, - labelHeight = opts.labelHeight || 0, - maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null), - legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", - layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, - font = opts.font || "flot-tick-label tickLabel"; - - for (var i = 0; i < ticks.length; ++i) { - - var t = ticks[i]; - - if (!t.label) - continue; - - var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); - - labelWidth = Math.max(labelWidth, info.width); - labelHeight = Math.max(labelHeight, info.height); - } - - axis.labelWidth = opts.labelWidth || labelWidth; - axis.labelHeight = opts.labelHeight || labelHeight; - } - - function allocateAxisBoxFirstPhase(axis) { - // find the bounding box of the axis by looking at label - // widths/heights and ticks, make room by diminishing the - // plotOffset; this first phase only looks at one - // dimension per axis, the other dimension depends on the - // other axes so will have to wait - - var lw = axis.labelWidth, - lh = axis.labelHeight, - pos = axis.options.position, - isXAxis = axis.direction === "x", - tickLength = axis.options.tickLength, - axisMargin = options.grid.axisMargin, - padding = options.grid.labelMargin, - innermost = true, - outermost = true, - first = true, - found = false; - - // Determine the axis's position in its direction and on its side - - $.each(isXAxis ? xaxes : yaxes, function(i, a) { - if (a && (a.show || a.reserveSpace)) { - if (a === axis) { - found = true; - } else if (a.options.position === pos) { - if (found) { - outermost = false; - } else { - innermost = false; - } - } - if (!found) { - first = false; - } - } - }); - - // The outermost axis on each side has no margin - - if (outermost) { - axisMargin = 0; - } - - // The ticks for the first axis in each direction stretch across - - if (tickLength == null) { - tickLength = first ? "full" : 5; - } - - if (!isNaN(+tickLength)) - padding += +tickLength; - - if (isXAxis) { - lh += padding; - - if (pos == "bottom") { - plotOffset.bottom += lh + axisMargin; - axis.box = { top: surface.height - plotOffset.bottom, height: lh }; - } - else { - axis.box = { top: plotOffset.top + axisMargin, height: lh }; - plotOffset.top += lh + axisMargin; - } - } - else { - lw += padding; - - if (pos == "left") { - axis.box = { left: plotOffset.left + axisMargin, width: lw }; - plotOffset.left += lw + axisMargin; - } - else { - plotOffset.right += lw + axisMargin; - axis.box = { left: surface.width - plotOffset.right, width: lw }; - } - } - - // save for future reference - axis.position = pos; - axis.tickLength = tickLength; - axis.box.padding = padding; - axis.innermost = innermost; - } - - function allocateAxisBoxSecondPhase(axis) { - // now that all axis boxes have been placed in one - // dimension, we can set the remaining dimension coordinates - if (axis.direction == "x") { - axis.box.left = plotOffset.left - axis.labelWidth / 2; - axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; - } - else { - axis.box.top = plotOffset.top - axis.labelHeight / 2; - axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; - } - } - - function adjustLayoutForThingsStickingOut() { - // possibly adjust plot offset to ensure everything stays - // inside the canvas and isn't clipped off - - var minMargin = options.grid.minBorderMargin, - axis, i; - - // check stuff from the plot (FIXME: this should just read - // a value from the series, otherwise it's impossible to - // customize) - if (minMargin == null) { - minMargin = 0; - for (i = 0; i < series.length; ++i) - minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); - } - - var margins = { - left: minMargin, - right: minMargin, - top: minMargin, - bottom: minMargin - }; - - // check axis labels, note we don't check the actual - // labels but instead use the overall width/height to not - // jump as much around with replots - $.each(allAxes(), function (_, axis) { - if (axis.reserveSpace && axis.ticks && axis.ticks.length) { - if (axis.direction === "x") { - margins.left = Math.max(margins.left, axis.labelWidth / 2); - margins.right = Math.max(margins.right, axis.labelWidth / 2); - } else { - margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2); - margins.top = Math.max(margins.top, axis.labelHeight / 2); - } - } - }); - - plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left)); - plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right)); - plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top)); - plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom)); - } - - function setupGrid() { - var i, axes = allAxes(), showGrid = options.grid.show; - - // Initialize the plot's offset from the edge of the canvas - - for (var a in plotOffset) { - var margin = options.grid.margin || 0; - plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; - } - - executeHooks(hooks.processOffset, [plotOffset]); - - // If the grid is visible, add its border width to the offset - - for (var a in plotOffset) { - if(typeof(options.grid.borderWidth) == "object") { - plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; - } - else { - plotOffset[a] += showGrid ? options.grid.borderWidth : 0; - } - } - - $.each(axes, function (_, axis) { - var axisOpts = axis.options; - axis.show = axisOpts.show == null ? axis.used : axisOpts.show; - axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace; - setRange(axis); - }); - - if (showGrid) { - - var allocatedAxes = $.grep(axes, function (axis) { - return axis.show || axis.reserveSpace; - }); - - $.each(allocatedAxes, function (_, axis) { - // make the ticks - setupTickGeneration(axis); - setTicks(axis); - snapRangeToTicks(axis, axis.ticks); - // find labelWidth/Height for axis - measureTickLabels(axis); - }); - - // with all dimensions calculated, we can compute the - // axis bounding boxes, start from the outside - // (reverse order) - for (i = allocatedAxes.length - 1; i >= 0; --i) - allocateAxisBoxFirstPhase(allocatedAxes[i]); - - // make sure we've got enough space for things that - // might stick out - adjustLayoutForThingsStickingOut(); - - $.each(allocatedAxes, function (_, axis) { - allocateAxisBoxSecondPhase(axis); - }); - } - - plotWidth = surface.width - plotOffset.left - plotOffset.right; - plotHeight = surface.height - plotOffset.bottom - plotOffset.top; - - // now we got the proper plot dimensions, we can compute the scaling - $.each(axes, function (_, axis) { - setTransformationHelpers(axis); - }); - - if (showGrid) { - drawAxisLabels(); - } - - insertLegend(); - } - - function setRange(axis) { - var opts = axis.options, - min = +(opts.min != null ? opts.min : axis.datamin), - max = +(opts.max != null ? opts.max : axis.datamax), - delta = max - min; - - if (delta == 0.0) { - // degenerate case - var widen = max == 0 ? 1 : 0.01; - - if (opts.min == null) - min -= widen; - // always widen max if we couldn't widen min to ensure we - // don't fall into min == max which doesn't work - if (opts.max == null || opts.min != null) - max += widen; - } - else { - // consider autoscaling - var margin = opts.autoscaleMargin; - if (margin != null) { - if (opts.min == null) { - min -= delta * margin; - // make sure we don't go below zero if all values - // are positive - if (min < 0 && axis.datamin != null && axis.datamin >= 0) - min = 0; - } - if (opts.max == null) { - max += delta * margin; - if (max > 0 && axis.datamax != null && axis.datamax <= 0) - max = 0; - } - } - } - axis.min = min; - axis.max = max; - } - - function setupTickGeneration(axis) { - var opts = axis.options; - - // estimate number of ticks - var noTicks; - if (typeof opts.ticks == "number" && opts.ticks > 0) - noTicks = opts.ticks; - else - // heuristic based on the model a*sqrt(x) fitted to - // some data points that seemed reasonable - noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); - - var delta = (axis.max - axis.min) / noTicks, - dec = -Math.floor(Math.log(delta) / Math.LN10), - maxDec = opts.tickDecimals; - - if (maxDec != null && dec > maxDec) { - dec = maxDec; - } - - var magn = Math.pow(10, -dec), - norm = delta / magn, // norm is between 1.0 and 10.0 - size; - - if (norm < 1.5) { - size = 1; - } else if (norm < 3) { - size = 2; - // special case for 2.5, requires an extra decimal - if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { - size = 2.5; - ++dec; - } - } else if (norm < 7.5) { - size = 5; - } else { - size = 10; - } - - size *= magn; - - if (opts.minTickSize != null && size < opts.minTickSize) { - size = opts.minTickSize; - } - - axis.delta = delta; - axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); - axis.tickSize = opts.tickSize || size; - - // Time mode was moved to a plug-in in 0.8, and since so many people use it - // we'll add an especially friendly reminder to make sure they included it. - - if (opts.mode == "time" && !axis.tickGenerator) { - throw new Error("Time mode requires the flot.time plugin."); - } - - // Flot supports base-10 axes; any other mode else is handled by a plug-in, - // like flot.time.js. - - if (!axis.tickGenerator) { - - axis.tickGenerator = function (axis) { - - var ticks = [], - start = floorInBase(axis.min, axis.tickSize), - i = 0, - v = Number.NaN, - prev; - - do { - prev = v; - v = start + i * axis.tickSize; - ticks.push(v); - ++i; - } while (v < axis.max && v != prev); - return ticks; - }; - - axis.tickFormatter = function (value, axis) { - - var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; - var formatted = "" + Math.round(value * factor) / factor; - - // If tickDecimals was specified, ensure that we have exactly that - // much precision; otherwise default to the value's own precision. - - if (axis.tickDecimals != null) { - var decimal = formatted.indexOf("."); - var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; - if (precision < axis.tickDecimals) { - return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); - } - } - - return formatted; - }; - } - - if ($.isFunction(opts.tickFormatter)) - axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; - - if (opts.alignTicksWithAxis != null) { - var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; - if (otherAxis && otherAxis.used && otherAxis != axis) { - // consider snapping min/max to outermost nice ticks - var niceTicks = axis.tickGenerator(axis); - if (niceTicks.length > 0) { - if (opts.min == null) - axis.min = Math.min(axis.min, niceTicks[0]); - if (opts.max == null && niceTicks.length > 1) - axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); - } - - axis.tickGenerator = function (axis) { - // copy ticks, scaled to this axis - var ticks = [], v, i; - for (i = 0; i < otherAxis.ticks.length; ++i) { - v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); - v = axis.min + v * (axis.max - axis.min); - ticks.push(v); - } - return ticks; - }; - - // we might need an extra decimal since forced - // ticks don't necessarily fit naturally - if (!axis.mode && opts.tickDecimals == null) { - var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), - ts = axis.tickGenerator(axis); - - // only proceed if the tick interval rounded - // with an extra decimal doesn't give us a - // zero at end - if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) - axis.tickDecimals = extraDec; - } - } - } - } - - function setTicks(axis) { - var oticks = axis.options.ticks, ticks = []; - if (oticks == null || (typeof oticks == "number" && oticks > 0)) - ticks = axis.tickGenerator(axis); - else if (oticks) { - if ($.isFunction(oticks)) - // generate the ticks - ticks = oticks(axis); - else - ticks = oticks; - } - - // clean up/labelify the supplied ticks, copy them over - var i, v; - axis.ticks = []; - for (i = 0; i < ticks.length; ++i) { - var label = null; - var t = ticks[i]; - if (typeof t == "object") { - v = +t[0]; - if (t.length > 1) - label = t[1]; - } - else - v = +t; - if (label == null) - label = axis.tickFormatter(v, axis); - if (!isNaN(v)) - axis.ticks.push({ v: v, label: label }); - } - } - - function snapRangeToTicks(axis, ticks) { - if (axis.options.autoscaleMargin && ticks.length > 0) { - // snap to ticks - if (axis.options.min == null) - axis.min = Math.min(axis.min, ticks[0].v); - if (axis.options.max == null && ticks.length > 1) - axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); - } - } - - function draw() { - - surface.clear(); - - executeHooks(hooks.drawBackground, [ctx]); - - var grid = options.grid; - - // draw background, if any - if (grid.show && grid.backgroundColor) - drawBackground(); - - if (grid.show && !grid.aboveData) { - drawGrid(); - } - - for (var i = 0; i < series.length; ++i) { - executeHooks(hooks.drawSeries, [ctx, series[i]]); - drawSeries(series[i]); - } - - executeHooks(hooks.draw, [ctx]); - - if (grid.show && grid.aboveData) { - drawGrid(); - } - - surface.render(); - - // A draw implies that either the axes or data have changed, so we - // should probably update the overlay highlights as well. - - triggerRedrawOverlay(); - } - - function extractRange(ranges, coord) { - var axis, from, to, key, axes = allAxes(); - - for (var i = 0; i < axes.length; ++i) { - axis = axes[i]; - if (axis.direction == coord) { - key = coord + axis.n + "axis"; - if (!ranges[key] && axis.n == 1) - key = coord + "axis"; // support x1axis as xaxis - if (ranges[key]) { - from = ranges[key].from; - to = ranges[key].to; - break; - } - } - } - - // backwards-compat stuff - to be removed in future - if (!ranges[key]) { - axis = coord == "x" ? xaxes[0] : yaxes[0]; - from = ranges[coord + "1"]; - to = ranges[coord + "2"]; - } - - // auto-reverse as an added bonus - if (from != null && to != null && from > to) { - var tmp = from; - from = to; - to = tmp; - } - - return { from: from, to: to, axis: axis }; - } - - function drawBackground() { - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); - ctx.fillRect(0, 0, plotWidth, plotHeight); - ctx.restore(); - } - - function drawGrid() { - var i, axes, bw, bc; - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - // draw markings - var markings = options.grid.markings; - if (markings) { - if ($.isFunction(markings)) { - axes = plot.getAxes(); - // xmin etc. is backwards compatibility, to be - // removed in the future - axes.xmin = axes.xaxis.min; - axes.xmax = axes.xaxis.max; - axes.ymin = axes.yaxis.min; - axes.ymax = axes.yaxis.max; - - markings = markings(axes); - } - - for (i = 0; i < markings.length; ++i) { - var m = markings[i], - xrange = extractRange(m, "x"), - yrange = extractRange(m, "y"); - - // fill in missing - if (xrange.from == null) - xrange.from = xrange.axis.min; - if (xrange.to == null) - xrange.to = xrange.axis.max; - if (yrange.from == null) - yrange.from = yrange.axis.min; - if (yrange.to == null) - yrange.to = yrange.axis.max; - - // clip - if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || - yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) - continue; - - xrange.from = Math.max(xrange.from, xrange.axis.min); - xrange.to = Math.min(xrange.to, xrange.axis.max); - yrange.from = Math.max(yrange.from, yrange.axis.min); - yrange.to = Math.min(yrange.to, yrange.axis.max); - - var xequal = xrange.from === xrange.to, - yequal = yrange.from === yrange.to; - - if (xequal && yequal) { - continue; - } - - // then draw - xrange.from = Math.floor(xrange.axis.p2c(xrange.from)); - xrange.to = Math.floor(xrange.axis.p2c(xrange.to)); - yrange.from = Math.floor(yrange.axis.p2c(yrange.from)); - yrange.to = Math.floor(yrange.axis.p2c(yrange.to)); - - if (xequal || yequal) { - var lineWidth = m.lineWidth || options.grid.markingsLineWidth, - subPixel = lineWidth % 2 ? 0.5 : 0; - ctx.beginPath(); - ctx.strokeStyle = m.color || options.grid.markingsColor; - ctx.lineWidth = lineWidth; - if (xequal) { - ctx.moveTo(xrange.to + subPixel, yrange.from); - ctx.lineTo(xrange.to + subPixel, yrange.to); - } else { - ctx.moveTo(xrange.from, yrange.to + subPixel); - ctx.lineTo(xrange.to, yrange.to + subPixel); - } - ctx.stroke(); - } else { - ctx.fillStyle = m.color || options.grid.markingsColor; - ctx.fillRect(xrange.from, yrange.to, - xrange.to - xrange.from, - yrange.from - yrange.to); - } - } - } - - // draw the ticks - axes = allAxes(); - bw = options.grid.borderWidth; - - for (var j = 0; j < axes.length; ++j) { - var axis = axes[j], box = axis.box, - t = axis.tickLength, x, y, xoff, yoff; - if (!axis.show || axis.ticks.length == 0) - continue; - - ctx.lineWidth = 1; - - // find the edges - if (axis.direction == "x") { - x = 0; - if (t == "full") - y = (axis.position == "top" ? 0 : plotHeight); - else - y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); - } - else { - y = 0; - if (t == "full") - x = (axis.position == "left" ? 0 : plotWidth); - else - x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); - } - - // draw tick bar - if (!axis.innermost) { - ctx.strokeStyle = axis.options.color; - ctx.beginPath(); - xoff = yoff = 0; - if (axis.direction == "x") - xoff = plotWidth + 1; - else - yoff = plotHeight + 1; - - if (ctx.lineWidth == 1) { - if (axis.direction == "x") { - y = Math.floor(y) + 0.5; - } else { - x = Math.floor(x) + 0.5; - } - } - - ctx.moveTo(x, y); - ctx.lineTo(x + xoff, y + yoff); - ctx.stroke(); - } - - // draw ticks - - ctx.strokeStyle = axis.options.tickColor; - - ctx.beginPath(); - for (i = 0; i < axis.ticks.length; ++i) { - var v = axis.ticks[i].v; - - xoff = yoff = 0; - - if (isNaN(v) || v < axis.min || v > axis.max - // skip those lying on the axes if we got a border - || (t == "full" - && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) - && (v == axis.min || v == axis.max))) - continue; - - if (axis.direction == "x") { - x = axis.p2c(v); - yoff = t == "full" ? -plotHeight : t; - - if (axis.position == "top") - yoff = -yoff; - } - else { - y = axis.p2c(v); - xoff = t == "full" ? -plotWidth : t; - - if (axis.position == "left") - xoff = -xoff; - } - - if (ctx.lineWidth == 1) { - if (axis.direction == "x") - x = Math.floor(x) + 0.5; - else - y = Math.floor(y) + 0.5; - } - - ctx.moveTo(x, y); - ctx.lineTo(x + xoff, y + yoff); - } - - ctx.stroke(); - } - - - // draw border - if (bw) { - // If either borderWidth or borderColor is an object, then draw the border - // line by line instead of as one rectangle - bc = options.grid.borderColor; - if(typeof bw == "object" || typeof bc == "object") { - if (typeof bw !== "object") { - bw = {top: bw, right: bw, bottom: bw, left: bw}; - } - if (typeof bc !== "object") { - bc = {top: bc, right: bc, bottom: bc, left: bc}; - } - - if (bw.top > 0) { - ctx.strokeStyle = bc.top; - ctx.lineWidth = bw.top; - ctx.beginPath(); - ctx.moveTo(0 - bw.left, 0 - bw.top/2); - ctx.lineTo(plotWidth, 0 - bw.top/2); - ctx.stroke(); - } - - if (bw.right > 0) { - ctx.strokeStyle = bc.right; - ctx.lineWidth = bw.right; - ctx.beginPath(); - ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); - ctx.lineTo(plotWidth + bw.right / 2, plotHeight); - ctx.stroke(); - } - - if (bw.bottom > 0) { - ctx.strokeStyle = bc.bottom; - ctx.lineWidth = bw.bottom; - ctx.beginPath(); - ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); - ctx.lineTo(0, plotHeight + bw.bottom / 2); - ctx.stroke(); - } - - if (bw.left > 0) { - ctx.strokeStyle = bc.left; - ctx.lineWidth = bw.left; - ctx.beginPath(); - ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom); - ctx.lineTo(0- bw.left/2, 0); - ctx.stroke(); - } - } - else { - ctx.lineWidth = bw; - ctx.strokeStyle = options.grid.borderColor; - ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); - } - } - - ctx.restore(); - } - - function drawAxisLabels() { - - $.each(allAxes(), function (_, axis) { - var box = axis.box, - legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", - layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, - font = axis.options.font || "flot-tick-label tickLabel", - tick, x, y, halign, valign; - - // Remove text before checking for axis.show and ticks.length; - // otherwise plugins, like flot-tickrotor, that draw their own - // tick labels will end up with both theirs and the defaults. - - surface.removeText(layer); - - if (!axis.show || axis.ticks.length == 0) - return; - - for (var i = 0; i < axis.ticks.length; ++i) { - - tick = axis.ticks[i]; - if (!tick.label || tick.v < axis.min || tick.v > axis.max) - continue; - - if (axis.direction == "x") { - halign = "center"; - x = plotOffset.left + axis.p2c(tick.v); - if (axis.position == "bottom") { - y = box.top + box.padding; - } else { - y = box.top + box.height - box.padding; - valign = "bottom"; - } - } else { - valign = "middle"; - y = plotOffset.top + axis.p2c(tick.v); - if (axis.position == "left") { - x = box.left + box.width - box.padding; - halign = "right"; - } else { - x = box.left + box.padding; - } - } - - surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); - } - }); - } - - function drawSeries(series) { - if (series.lines.show) - drawSeriesLines(series); - if (series.bars.show) - drawSeriesBars(series); - if (series.points.show) - drawSeriesPoints(series); - } - - function drawSeriesLines(series) { - function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { - var points = datapoints.points, - ps = datapoints.pointsize, - prevx = null, prevy = null; - - ctx.beginPath(); - for (var i = ps; i < points.length; i += ps) { - var x1 = points[i - ps], y1 = points[i - ps + 1], - x2 = points[i], y2 = points[i + 1]; - - if (x1 == null || x2 == null) - continue; - - // clip with ymin - if (y1 <= y2 && y1 < axisy.min) { - if (y2 < axisy.min) - continue; // line segment is outside - // compute new intersection point - x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; - y1 = axisy.min; - } - else if (y2 <= y1 && y2 < axisy.min) { - if (y1 < axisy.min) - continue; - x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; - y2 = axisy.min; - } - - // clip with ymax - if (y1 >= y2 && y1 > axisy.max) { - if (y2 > axisy.max) - continue; - x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; - y1 = axisy.max; - } - else if (y2 >= y1 && y2 > axisy.max) { - if (y1 > axisy.max) - continue; - x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; - y2 = axisy.max; - } - - // clip with xmin - if (x1 <= x2 && x1 < axisx.min) { - if (x2 < axisx.min) - continue; - y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; - x1 = axisx.min; - } - else if (x2 <= x1 && x2 < axisx.min) { - if (x1 < axisx.min) - continue; - y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; - x2 = axisx.min; - } - - // clip with xmax - if (x1 >= x2 && x1 > axisx.max) { - if (x2 > axisx.max) - continue; - y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; - x1 = axisx.max; - } - else if (x2 >= x1 && x2 > axisx.max) { - if (x1 > axisx.max) - continue; - y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; - x2 = axisx.max; - } - - if (x1 != prevx || y1 != prevy) - ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); - - prevx = x2; - prevy = y2; - ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); - } - ctx.stroke(); - } - - function plotLineArea(datapoints, axisx, axisy) { - var points = datapoints.points, - ps = datapoints.pointsize, - bottom = Math.min(Math.max(0, axisy.min), axisy.max), - i = 0, top, areaOpen = false, - ypos = 1, segmentStart = 0, segmentEnd = 0; - - // we process each segment in two turns, first forward - // direction to sketch out top, then once we hit the - // end we go backwards to sketch the bottom - while (true) { - if (ps > 0 && i > points.length + ps) - break; - - i += ps; // ps is negative if going backwards - - var x1 = points[i - ps], - y1 = points[i - ps + ypos], - x2 = points[i], y2 = points[i + ypos]; - - if (areaOpen) { - if (ps > 0 && x1 != null && x2 == null) { - // at turning point - segmentEnd = i; - ps = -ps; - ypos = 2; - continue; - } - - if (ps < 0 && i == segmentStart + ps) { - // done with the reverse sweep - ctx.fill(); - areaOpen = false; - ps = -ps; - ypos = 1; - i = segmentStart = segmentEnd + ps; - continue; - } - } - - if (x1 == null || x2 == null) - continue; - - // clip x values - - // clip with xmin - if (x1 <= x2 && x1 < axisx.min) { - if (x2 < axisx.min) - continue; - y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; - x1 = axisx.min; - } - else if (x2 <= x1 && x2 < axisx.min) { - if (x1 < axisx.min) - continue; - y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; - x2 = axisx.min; - } - - // clip with xmax - if (x1 >= x2 && x1 > axisx.max) { - if (x2 > axisx.max) - continue; - y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; - x1 = axisx.max; - } - else if (x2 >= x1 && x2 > axisx.max) { - if (x1 > axisx.max) - continue; - y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; - x2 = axisx.max; - } - - if (!areaOpen) { - // open area - ctx.beginPath(); - ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); - areaOpen = true; - } - - // now first check the case where both is outside - if (y1 >= axisy.max && y2 >= axisy.max) { - ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); - ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); - continue; - } - else if (y1 <= axisy.min && y2 <= axisy.min) { - ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); - ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); - continue; - } - - // else it's a bit more complicated, there might - // be a flat maxed out rectangle first, then a - // triangular cutout or reverse; to find these - // keep track of the current x values - var x1old = x1, x2old = x2; - - // clip the y values, without shortcutting, we - // go through all cases in turn - - // clip with ymin - if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { - x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; - y1 = axisy.min; - } - else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { - x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; - y2 = axisy.min; - } - - // clip with ymax - if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { - x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; - y1 = axisy.max; - } - else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { - x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; - y2 = axisy.max; - } - - // if the x value was changed we got a rectangle - // to fill - if (x1 != x1old) { - ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); - // it goes to (x1, y1), but we fill that below - } - - // fill triangular section, this sometimes result - // in redundant points if (x1, y1) hasn't changed - // from previous line to, but we just ignore that - ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); - ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); - - // fill the other rectangle if it's there - if (x2 != x2old) { - ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); - ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); - } - } - } - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - ctx.lineJoin = "round"; - - var lw = series.lines.lineWidth, - sw = series.shadowSize; - // FIXME: consider another form of shadow when filling is turned on - if (lw > 0 && sw > 0) { - // draw shadow as a thick and thin line with transparency - ctx.lineWidth = sw; - ctx.strokeStyle = "rgba(0,0,0,0.1)"; - // position shadow at angle from the mid of line - var angle = Math.PI/18; - plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); - ctx.lineWidth = sw/2; - plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); - } - - ctx.lineWidth = lw; - ctx.strokeStyle = series.color; - var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); - if (fillStyle) { - ctx.fillStyle = fillStyle; - plotLineArea(series.datapoints, series.xaxis, series.yaxis); - } - - if (lw > 0) - plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); - ctx.restore(); - } - - function drawSeriesPoints(series) { - function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { - var points = datapoints.points, ps = datapoints.pointsize; - - for (var i = 0; i < points.length; i += ps) { - var x = points[i], y = points[i + 1]; - if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) - continue; - - ctx.beginPath(); - x = axisx.p2c(x); - y = axisy.p2c(y) + offset; - if (symbol == "circle") - ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); - else - symbol(ctx, x, y, radius, shadow); - ctx.closePath(); - - if (fillStyle) { - ctx.fillStyle = fillStyle; - ctx.fill(); - } - ctx.stroke(); - } - } - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - var lw = series.points.lineWidth, - sw = series.shadowSize, - radius = series.points.radius, - symbol = series.points.symbol; - - // If the user sets the line width to 0, we change it to a very - // small value. A line width of 0 seems to force the default of 1. - // Doing the conditional here allows the shadow setting to still be - // optional even with a lineWidth of 0. - - if( lw == 0 ) - lw = 0.0001; - - if (lw > 0 && sw > 0) { - // draw shadow in two steps - var w = sw / 2; - ctx.lineWidth = w; - ctx.strokeStyle = "rgba(0,0,0,0.1)"; - plotPoints(series.datapoints, radius, null, w + w/2, true, - series.xaxis, series.yaxis, symbol); - - ctx.strokeStyle = "rgba(0,0,0,0.2)"; - plotPoints(series.datapoints, radius, null, w/2, true, - series.xaxis, series.yaxis, symbol); - } - - ctx.lineWidth = lw; - ctx.strokeStyle = series.color; - plotPoints(series.datapoints, radius, - getFillStyle(series.points, series.color), 0, false, - series.xaxis, series.yaxis, symbol); - ctx.restore(); - } - - function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { - var left, right, bottom, top, - drawLeft, drawRight, drawTop, drawBottom, - tmp; - - // in horizontal mode, we start the bar from the left - // instead of from the bottom so it appears to be - // horizontal rather than vertical - if (horizontal) { - drawBottom = drawRight = drawTop = true; - drawLeft = false; - left = b; - right = x; - top = y + barLeft; - bottom = y + barRight; - - // account for negative bars - if (right < left) { - tmp = right; - right = left; - left = tmp; - drawLeft = true; - drawRight = false; - } - } - else { - drawLeft = drawRight = drawTop = true; - drawBottom = false; - left = x + barLeft; - right = x + barRight; - bottom = b; - top = y; - - // account for negative bars - if (top < bottom) { - tmp = top; - top = bottom; - bottom = tmp; - drawBottom = true; - drawTop = false; - } - } - - // clip - if (right < axisx.min || left > axisx.max || - top < axisy.min || bottom > axisy.max) - return; - - if (left < axisx.min) { - left = axisx.min; - drawLeft = false; - } - - if (right > axisx.max) { - right = axisx.max; - drawRight = false; - } - - if (bottom < axisy.min) { - bottom = axisy.min; - drawBottom = false; - } - - if (top > axisy.max) { - top = axisy.max; - drawTop = false; - } - - left = axisx.p2c(left); - bottom = axisy.p2c(bottom); - right = axisx.p2c(right); - top = axisy.p2c(top); - - // fill the bar - if (fillStyleCallback) { - c.fillStyle = fillStyleCallback(bottom, top); - c.fillRect(left, top, right - left, bottom - top) - } - - // draw outline - if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { - c.beginPath(); - - // FIXME: inline moveTo is buggy with excanvas - c.moveTo(left, bottom); - if (drawLeft) - c.lineTo(left, top); - else - c.moveTo(left, top); - if (drawTop) - c.lineTo(right, top); - else - c.moveTo(right, top); - if (drawRight) - c.lineTo(right, bottom); - else - c.moveTo(right, bottom); - if (drawBottom) - c.lineTo(left, bottom); - else - c.moveTo(left, bottom); - c.stroke(); - } - } - - function drawSeriesBars(series) { - function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { - var points = datapoints.points, ps = datapoints.pointsize; - - for (var i = 0; i < points.length; i += ps) { - if (points[i] == null) - continue; - drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); - } - } - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - // FIXME: figure out a way to add shadows (for instance along the right edge) - ctx.lineWidth = series.bars.lineWidth; - ctx.strokeStyle = series.color; - - var barLeft; - - switch (series.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -series.bars.barWidth; - break; - default: - barLeft = -series.bars.barWidth / 2; - } - - var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; - plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); - ctx.restore(); - } - - function getFillStyle(filloptions, seriesColor, bottom, top) { - var fill = filloptions.fill; - if (!fill) - return null; - - if (filloptions.fillColor) - return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); - - var c = $.color.parse(seriesColor); - c.a = typeof fill == "number" ? fill : 0.4; - c.normalize(); - return c.toString(); - } - - function insertLegend() { - - if (options.legend.container != null) { - $(options.legend.container).html(""); - } else { - placeholder.find(".legend").remove(); - } - - if (!options.legend.show) { - return; - } - - var fragments = [], entries = [], rowStarted = false, - lf = options.legend.labelFormatter, s, label; - - // Build a list of legend entries, with each having a label and a color - - for (var i = 0; i < series.length; ++i) { - s = series[i]; - if (s.label) { - label = lf ? lf(s.label, s) : s.label; - if (label) { - entries.push({ - label: label, - color: s.color - }); - } - } - } - - // Sort the legend using either the default or a custom comparator - - if (options.legend.sorted) { - if ($.isFunction(options.legend.sorted)) { - entries.sort(options.legend.sorted); - } else if (options.legend.sorted == "reverse") { - entries.reverse(); - } else { - var ascending = options.legend.sorted != "descending"; - entries.sort(function(a, b) { - return a.label == b.label ? 0 : ( - (a.label < b.label) != ascending ? 1 : -1 // Logical XOR - ); - }); - } - } - - // Generate markup for the list of entries, in their final order - - for (var i = 0; i < entries.length; ++i) { - - var entry = entries[i]; - - if (i % options.legend.noColumns == 0) { - if (rowStarted) - fragments.push(''); - fragments.push(''); - rowStarted = true; - } - - fragments.push( - '
' + - '' + entry.label + '' - ); - } - - if (rowStarted) - fragments.push(''); - - if (fragments.length == 0) - return; - - var table = '' + fragments.join("") + '
'; - if (options.legend.container != null) - $(options.legend.container).html(table); - else { - var pos = "", - p = options.legend.position, - m = options.legend.margin; - if (m[0] == null) - m = [m, m]; - if (p.charAt(0) == "n") - pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; - else if (p.charAt(0) == "s") - pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; - if (p.charAt(1) == "e") - pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; - else if (p.charAt(1) == "w") - pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; - var legend = $('
' + table.replace('style="', 'style="position:absolute;' + pos +';') + '
').appendTo(placeholder); - if (options.legend.backgroundOpacity != 0.0) { - // put in the transparent background - // separately to avoid blended labels and - // label boxes - var c = options.legend.backgroundColor; - if (c == null) { - c = options.grid.backgroundColor; - if (c && typeof c == "string") - c = $.color.parse(c); - else - c = $.color.extract(legend, 'background-color'); - c.a = 1; - c = c.toString(); - } - var div = legend.children(); - $('
').prependTo(legend).css('opacity', options.legend.backgroundOpacity); - } - } - } - - - // interactive features - - var highlights = [], - redrawTimeout = null; - - // returns the data item the mouse is over, or null if none is found - function findNearbyItem(mouseX, mouseY, seriesFilter) { - var maxDistance = options.grid.mouseActiveRadius, - smallestDistance = maxDistance * maxDistance + 1, - item = null, foundPoint = false, i, j, ps; - - for (i = series.length - 1; i >= 0; --i) { - if (!seriesFilter(series[i])) - continue; - - var s = series[i], - axisx = s.xaxis, - axisy = s.yaxis, - points = s.datapoints.points, - mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster - my = axisy.c2p(mouseY), - maxx = maxDistance / axisx.scale, - maxy = maxDistance / axisy.scale; - - ps = s.datapoints.pointsize; - // with inverse transforms, we can't use the maxx/maxy - // optimization, sadly - if (axisx.options.inverseTransform) - maxx = Number.MAX_VALUE; - if (axisy.options.inverseTransform) - maxy = Number.MAX_VALUE; - - if (s.lines.show || s.points.show) { - for (j = 0; j < points.length; j += ps) { - var x = points[j], y = points[j + 1]; - if (x == null) - continue; - - // For points and lines, the cursor must be within a - // certain distance to the data point - if (x - mx > maxx || x - mx < -maxx || - y - my > maxy || y - my < -maxy) - continue; - - // We have to calculate distances in pixels, not in - // data units, because the scales of the axes may be different - var dx = Math.abs(axisx.p2c(x) - mouseX), - dy = Math.abs(axisy.p2c(y) - mouseY), - dist = dx * dx + dy * dy; // we save the sqrt - - // use <= to ensure last point takes precedence - // (last generally means on top of) - if (dist < smallestDistance) { - smallestDistance = dist; - item = [i, j / ps]; - } - } - } - - if (s.bars.show && !item) { // no other point can be nearby - - var barLeft, barRight; - - switch (s.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -s.bars.barWidth; - break; - default: - barLeft = -s.bars.barWidth / 2; - } - - barRight = barLeft + s.bars.barWidth; - - for (j = 0; j < points.length; j += ps) { - var x = points[j], y = points[j + 1], b = points[j + 2]; - if (x == null) - continue; - - // for a bar graph, the cursor must be inside the bar - if (series[i].bars.horizontal ? - (mx <= Math.max(b, x) && mx >= Math.min(b, x) && - my >= y + barLeft && my <= y + barRight) : - (mx >= x + barLeft && mx <= x + barRight && - my >= Math.min(b, y) && my <= Math.max(b, y))) - item = [i, j / ps]; - } - } - } - - if (item) { - i = item[0]; - j = item[1]; - ps = series[i].datapoints.pointsize; - - return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), - dataIndex: j, - series: series[i], - seriesIndex: i }; - } - - return null; - } - - function onMouseMove(e) { - if (options.grid.hoverable) - triggerClickHoverEvent("plothover", e, - function (s) { return s["hoverable"] != false; }); - } - - function onMouseLeave(e) { - if (options.grid.hoverable) - triggerClickHoverEvent("plothover", e, - function (s) { return false; }); - } - - function onClick(e) { - triggerClickHoverEvent("plotclick", e, - function (s) { return s["clickable"] != false; }); - } - - // trigger click or hover event (they send the same parameters - // so we share their code) - function triggerClickHoverEvent(eventname, event, seriesFilter) { - var offset = eventHolder.offset(), - canvasX = event.pageX - offset.left - plotOffset.left, - canvasY = event.pageY - offset.top - plotOffset.top, - pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); - - pos.pageX = event.pageX; - pos.pageY = event.pageY; - - var item = findNearbyItem(canvasX, canvasY, seriesFilter); - - if (item) { - // fill in mouse pos for any listeners out there - item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); - item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10); - } - - if (options.grid.autoHighlight) { - // clear auto-highlights - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.auto == eventname && - !(item && h.series == item.series && - h.point[0] == item.datapoint[0] && - h.point[1] == item.datapoint[1])) - unhighlight(h.series, h.point); - } - - if (item) - highlight(item.series, item.datapoint, eventname); - } - - placeholder.trigger(eventname, [ pos, item ]); - } - - function triggerRedrawOverlay() { - var t = options.interaction.redrawOverlayInterval; - if (t == -1) { // skip event queue - drawOverlay(); - return; - } - - if (!redrawTimeout) - redrawTimeout = setTimeout(drawOverlay, t); - } - - function drawOverlay() { - redrawTimeout = null; - - // draw highlights - octx.save(); - overlay.clear(); - octx.translate(plotOffset.left, plotOffset.top); - - var i, hi; - for (i = 0; i < highlights.length; ++i) { - hi = highlights[i]; - - if (hi.series.bars.show) - drawBarHighlight(hi.series, hi.point); - else - drawPointHighlight(hi.series, hi.point); - } - octx.restore(); - - executeHooks(hooks.drawOverlay, [octx]); - } - - function highlight(s, point, auto) { - if (typeof s == "number") - s = series[s]; - - if (typeof point == "number") { - var ps = s.datapoints.pointsize; - point = s.datapoints.points.slice(ps * point, ps * (point + 1)); - } - - var i = indexOfHighlight(s, point); - if (i == -1) { - highlights.push({ series: s, point: point, auto: auto }); - - triggerRedrawOverlay(); - } - else if (!auto) - highlights[i].auto = false; - } - - function unhighlight(s, point) { - if (s == null && point == null) { - highlights = []; - triggerRedrawOverlay(); - return; - } - - if (typeof s == "number") - s = series[s]; - - if (typeof point == "number") { - var ps = s.datapoints.pointsize; - point = s.datapoints.points.slice(ps * point, ps * (point + 1)); - } - - var i = indexOfHighlight(s, point); - if (i != -1) { - highlights.splice(i, 1); - - triggerRedrawOverlay(); - } - } - - function indexOfHighlight(s, p) { - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.series == s && h.point[0] == p[0] - && h.point[1] == p[1]) - return i; - } - return -1; - } - - function drawPointHighlight(series, point) { - var x = point[0], y = point[1], - axisx = series.xaxis, axisy = series.yaxis, - highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); - - if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) - return; - - var pointRadius = series.points.radius + series.points.lineWidth / 2; - octx.lineWidth = pointRadius; - octx.strokeStyle = highlightColor; - var radius = 1.5 * pointRadius; - x = axisx.p2c(x); - y = axisy.p2c(y); - - octx.beginPath(); - if (series.points.symbol == "circle") - octx.arc(x, y, radius, 0, 2 * Math.PI, false); - else - series.points.symbol(octx, x, y, radius, false); - octx.closePath(); - octx.stroke(); - } - - function drawBarHighlight(series, point) { - var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), - fillStyle = highlightColor, - barLeft; - - switch (series.bars.align) { - case "left": - barLeft = 0; - break; - case "right": - barLeft = -series.bars.barWidth; - break; - default: - barLeft = -series.bars.barWidth / 2; - } - - octx.lineWidth = series.bars.lineWidth; - octx.strokeStyle = highlightColor; - - drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, - function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); - } - - function getColorOrGradient(spec, bottom, top, defaultColor) { - if (typeof spec == "string") - return spec; - else { - // assume this is a gradient spec; IE currently only - // supports a simple vertical gradient properly, so that's - // what we support too - var gradient = ctx.createLinearGradient(0, top, 0, bottom); - - for (var i = 0, l = spec.colors.length; i < l; ++i) { - var c = spec.colors[i]; - if (typeof c != "string") { - var co = $.color.parse(defaultColor); - if (c.brightness != null) - co = co.scale('rgb', c.brightness); - if (c.opacity != null) - co.a *= c.opacity; - c = co.toString(); - } - gradient.addColorStop(i / (l - 1), c); - } - - return gradient; - } - } - } - - // Add the plot function to the top level of the jQuery object - - $.plot = function(placeholder, data, options) { - //var t0 = new Date(); - var plot = new Plot($(placeholder), data, options, $.plot.plugins); - //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); - return plot; - }; - - $.plot.version = "0.8.3"; - - $.plot.plugins = []; - - // Also add the plot function as a chainable property - - $.fn.plot = function(data, options) { - return this.each(function() { - $.plot(this, data, options); - }); - }; - - // round to nearby lower multiple of base - function floorInBase(n, base) { - return base * Math.floor(n / base); - } - -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.log.js b/src/legacy/ui/public/flot-charts/jquery.flot.log.js deleted file mode 100644 index e32bf5cf7e817..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.log.js +++ /dev/null @@ -1,163 +0,0 @@ -/* @notice - * - * Pretty handling of logarithmic axes. - * Copyright (c) 2007-2014 IOLA and Ole Laursen. - * Licensed under the MIT license. - * Created by Arne de Laat - * Set axis.mode to "log" and make the axis logarithmic using transform: - * axis: { - * mode: 'log', - * transform: function(v) {v <= 0 ? Math.log(v) / Math.LN10 : null}, - * inverseTransform: function(v) {Math.pow(10, v)} - * } - * The transform filters negative and zero values, because those are - * invalid on logarithmic scales. - * This plugin tries to create good looking logarithmic ticks, using - * unicode superscript characters. If all data to be plotted is between two - * powers of ten then the default flot tick generator and renderer are - * used. Logarithmic ticks are places at powers of ten and at half those - * values if there are not to many ticks already (e.g. [1, 5, 10, 50, 100]). - * For details, see https://github.com/flot/flot/pull/1328 -*/ - -(function($) { - - function log10(value) { - /* Get the Log10 of the value - */ - return Math.log(value) / Math.LN10; - } - - function floorAsLog10(value) { - /* Get power of the first power of 10 below the value - */ - return Math.floor(log10(value)); - } - - function ceilAsLog10(value) { - /* Get power of the first power of 10 above the value - */ - return Math.ceil(log10(value)); - } - - - // round to nearby lower multiple of base - function floorInBase(n, base) { - return base * Math.floor(n / base); - } - - function getUnicodePower(power) { - var superscripts = ["⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"], - result = "", - str_power = "" + power; - for (var i = 0; i < str_power.length; i++) { - if (str_power[i] === "+") { - } - else if (str_power[i] === "-") { - result += "⁻"; - } - else { - result += superscripts[str_power[i]]; - } - } - return result; - } - - function init(plot) { - plot.hooks.processOptions.push(function (plot) { - $.each(plot.getAxes(), function(axisName, axis) { - - var opts = axis.options; - - if (opts.mode === "log") { - - axis.tickGenerator = function (axis) { - - var ticks = [], - end = ceilAsLog10(axis.max), - start = floorAsLog10(axis.min), - tick = Number.NaN, - i = 0; - - if (axis.min === null || axis.min <= 0) { - // Bad minimum, make ticks from 1 (10**0) to max - start = 0; - axis.min = 0.6; - } - - if (end <= start) { - // Start less than end?! - ticks = [1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, - 1e7, 1e8, 1e9]; - } - else if (log10(axis.max) - log10(axis.datamin) < 1) { - // Default flot generator incase no powers of 10 - // are between start and end - var prev; - start = floorInBase(axis.min, axis.tickSize); - do { - prev = tick; - tick = start + i * axis.tickSize; - ticks.push(tick); - ++i; - } while (tick < axis.max && tick !== prev); - } - else { - // Make ticks at each power of ten - for (; i <= (end - start); i++) { - tick = Math.pow(10, start + i); - ticks.push(tick); - } - - var length = ticks.length; - - // If not to many ticks also put a tick between - // the powers of ten - if (end - start < 6) { - for (var j = 1; j < length * 2 - 1; j += 2) { - tick = ticks[j - 1] * 5; - ticks.splice(j, 0, tick); - } - } - } - return ticks; - }; - - axis.tickFormatter = function (value, axis) { - var formatted; - if (log10(axis.max) - log10(axis.datamin) < 1) { - // Default flot formatter - var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; - formatted = "" + Math.round(value * factor) / factor; - if (axis.tickDecimals !== null) { - var decimal = formatted.indexOf("."); - var precision = decimal === -1 ? 0 : formatted.length - decimal - 1; - if (precision < axis.tickDecimals) { - return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); - } - } - } - else { - var multiplier = "", - exponential = parseFloat(value).toExponential(0), - power = getUnicodePower(exponential.slice(2)); - if (exponential[0] !== "1") { - multiplier = exponential[0] + "x"; - } - formatted = multiplier + "10" + power; - } - return formatted; - }; - } - }); - }); - } - - $.plot.plugins.push({ - init: init, - name: "log", - version: "0.9" - }); - -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.navigate.js b/src/legacy/ui/public/flot-charts/jquery.flot.navigate.js deleted file mode 100644 index 13fb7f17d04b2..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.navigate.js +++ /dev/null @@ -1,346 +0,0 @@ -/* Flot plugin for adding the ability to pan and zoom the plot. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The default behaviour is double click and scrollwheel up/down to zoom in, drag -to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and -plot.pan( offset ) so you easily can add custom controls. It also fires -"plotpan" and "plotzoom" events, useful for synchronizing plots. - -The plugin supports these options: - - zoom: { - interactive: false - trigger: "dblclick" // or "click" for single click - amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out) - } - - pan: { - interactive: false - cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer" - frameRate: 20 - } - - xaxis, yaxis, x2axis, y2axis: { - zoomRange: null // or [ number, number ] (min range, max range) or false - panRange: null // or [ number, number ] (min, max) or false - } - -"interactive" enables the built-in drag/click behaviour. If you enable -interactive for pan, then you'll have a basic plot that supports moving -around; the same for zoom. - -"amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to -the current viewport. - -"cursor" is a standard CSS mouse cursor string used for visual feedback to the -user when dragging. - -"frameRate" specifies the maximum number of times per second the plot will -update itself while the user is panning around on it (set to null to disable -intermediate pans, the plot will then not update until the mouse button is -released). - -"zoomRange" is the interval in which zooming can happen, e.g. with zoomRange: -[1, 100] the zoom will never scale the axis so that the difference between min -and max is smaller than 1 or larger than 100. You can set either end to null -to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis -will be disabled. - -"panRange" confines the panning to stay within a range, e.g. with panRange: -[-10, 20] panning stops at -10 in one end and at 20 in the other. Either can -be null, e.g. [-10, null]. If you set panRange to false, panning on that axis -will be disabled. - -Example API usage: - - plot = $.plot(...); - - // zoom default amount in on the pixel ( 10, 20 ) - plot.zoom({ center: { left: 10, top: 20 } }); - - // zoom out again - plot.zoomOut({ center: { left: 10, top: 20 } }); - - // zoom 200% in on the pixel (10, 20) - plot.zoom({ amount: 2, center: { left: 10, top: 20 } }); - - // pan 100 pixels to the left and 20 down - plot.pan({ left: -100, top: 20 }) - -Here, "center" specifies where the center of the zooming should happen. Note -that this is defined in pixel space, not the space of the data points (you can -use the p2c helpers on the axes in Flot to help you convert between these). - -"amount" is the amount to zoom the viewport relative to the current range, so -1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You -can set the default in the options. - -*/ - -// First two dependencies, jquery.event.drag.js and -// jquery.mousewheel.js, we put them inline here to save people the -// effort of downloading them. - -/* -jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) -Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt -*/ -(function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY) max) { - // make sure min < max - var tmp = min; - min = max; - max = tmp; - } - - //Check that we are in panRange - if (pr) { - if (pr[0] != null && min < pr[0]) { - min = pr[0]; - } - if (pr[1] != null && max > pr[1]) { - max = pr[1]; - } - } - - var range = max - min; - if (zr && - ((zr[0] != null && range < zr[0] && amount >1) || - (zr[1] != null && range > zr[1] && amount <1))) - return; - - opts.min = min; - opts.max = max; - }); - - plot.setupGrid(); - plot.draw(); - - if (!args.preventEvent) - plot.getPlaceholder().trigger("plotzoom", [ plot, args ]); - }; - - plot.pan = function (args) { - var delta = { - x: +args.left, - y: +args.top - }; - - if (isNaN(delta.x)) - delta.x = 0; - if (isNaN(delta.y)) - delta.y = 0; - - $.each(plot.getAxes(), function (_, axis) { - var opts = axis.options, - min, max, d = delta[axis.direction]; - - min = axis.c2p(axis.p2c(axis.min) + d), - max = axis.c2p(axis.p2c(axis.max) + d); - - var pr = opts.panRange; - if (pr === false) // no panning on this axis - return; - - if (pr) { - // check whether we hit the wall - if (pr[0] != null && pr[0] > min) { - d = pr[0] - min; - min += d; - max += d; - } - - if (pr[1] != null && pr[1] < max) { - d = pr[1] - max; - min += d; - max += d; - } - } - - opts.min = min; - opts.max = max; - }); - - plot.setupGrid(); - plot.draw(); - - if (!args.preventEvent) - plot.getPlaceholder().trigger("plotpan", [ plot, args ]); - }; - - function shutdown(plot, eventHolder) { - eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); - eventHolder.unbind("mousewheel", onMouseWheel); - eventHolder.unbind("dragstart", onDragStart); - eventHolder.unbind("drag", onDrag); - eventHolder.unbind("dragend", onDragEnd); - if (panTimeout) - clearTimeout(panTimeout); - } - - plot.hooks.bindEvents.push(bindEvents); - plot.hooks.shutdown.push(shutdown); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'navigate', - version: '1.3' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.pie.js b/src/legacy/ui/public/flot-charts/jquery.flot.pie.js deleted file mode 100644 index 06f900bdc950f..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.pie.js +++ /dev/null @@ -1,824 +0,0 @@ -/* Flot plugin for rendering pie charts. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The plugin assumes that each series has a single data value, and that each -value is a positive integer or zero. Negative numbers don't make sense for a -pie chart, and have unpredictable results. The values do NOT need to be -passed in as percentages; the plugin will calculate the total and per-slice -percentages internally. - -* Created by Brian Medendorp - -* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars - -The plugin supports these options: - - series: { - pie: { - show: true/false - radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' - innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect - startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result - tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) - offset: { - top: integer value to move the pie up or down - left: integer value to move the pie left or right, or 'auto' - }, - stroke: { - color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#FFF') - width: integer pixel width of the stroke - }, - label: { - show: true/false, or 'auto' - formatter: a user-defined function that modifies the text/style of the label text - radius: 0-1 for percentage of fullsize, or a specified pixel length - background: { - color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#000') - opacity: 0-1 - }, - threshold: 0-1 for the percentage value at which to hide labels (if they're too small) - }, - combine: { - threshold: 0-1 for the percentage value at which to combine slices (if they're too small) - color: any hexadecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined - label: any text value of what the combined slice should be labeled - } - highlight: { - opacity: 0-1 - } - } - } - -More detail and specific examples can be found in the included HTML file. - -*/ - -import { i18n } from '@kbn/i18n'; - -(function($) { - // Maximum redraw attempts when fitting labels within the plot - - var REDRAW_ATTEMPTS = 10; - - // Factor by which to shrink the pie when fitting labels within the plot - - var REDRAW_SHRINK = 0.95; - - function init(plot) { - - var canvas = null, - target = null, - options = null, - maxRadius = null, - centerLeft = null, - centerTop = null, - processed = false, - ctx = null; - - // interactive variables - - var highlights = []; - - // add hook to determine if pie plugin in enabled, and then perform necessary operations - - plot.hooks.processOptions.push(function(plot, options) { - if (options.series.pie.show) { - - options.grid.show = false; - - // set labels.show - - if (options.series.pie.label.show == "auto") { - if (options.legend.show) { - options.series.pie.label.show = false; - } else { - options.series.pie.label.show = true; - } - } - - // set radius - - if (options.series.pie.radius == "auto") { - if (options.series.pie.label.show) { - options.series.pie.radius = 3/4; - } else { - options.series.pie.radius = 1; - } - } - - // ensure sane tilt - - if (options.series.pie.tilt > 1) { - options.series.pie.tilt = 1; - } else if (options.series.pie.tilt < 0) { - options.series.pie.tilt = 0; - } - } - }); - - plot.hooks.bindEvents.push(function(plot, eventHolder) { - var options = plot.getOptions(); - if (options.series.pie.show) { - if (options.grid.hoverable) { - eventHolder.unbind("mousemove").mousemove(onMouseMove); - } - if (options.grid.clickable) { - eventHolder.unbind("click").click(onClick); - } - } - }); - - plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) { - var options = plot.getOptions(); - if (options.series.pie.show) { - processDatapoints(plot, series, data, datapoints); - } - }); - - plot.hooks.drawOverlay.push(function(plot, octx) { - var options = plot.getOptions(); - if (options.series.pie.show) { - drawOverlay(plot, octx); - } - }); - - plot.hooks.draw.push(function(plot, newCtx) { - var options = plot.getOptions(); - if (options.series.pie.show) { - draw(plot, newCtx); - } - }); - - function processDatapoints(plot, series, datapoints) { - if (!processed) { - processed = true; - canvas = plot.getCanvas(); - target = $(canvas).parent(); - options = plot.getOptions(); - plot.setData(combine(plot.getData())); - } - } - - function combine(data) { - - var total = 0, - combined = 0, - numCombined = 0, - color = options.series.pie.combine.color, - newdata = []; - - // Fix up the raw data from Flot, ensuring the data is numeric - - for (var i = 0; i < data.length; ++i) { - - var value = data[i].data; - - // If the data is an array, we'll assume that it's a standard - // Flot x-y pair, and are concerned only with the second value. - - // Note how we use the original array, rather than creating a - // new one; this is more efficient and preserves any extra data - // that the user may have stored in higher indexes. - - if ($.isArray(value) && value.length == 1) { - value = value[0]; - } - - if ($.isArray(value)) { - // Equivalent to $.isNumeric() but compatible with jQuery < 1.7 - if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) { - value[1] = +value[1]; - } else { - value[1] = 0; - } - } else if (!isNaN(parseFloat(value)) && isFinite(value)) { - value = [1, +value]; - } else { - value = [1, 0]; - } - - data[i].data = [value]; - } - - // Sum up all the slices, so we can calculate percentages for each - - for (var i = 0; i < data.length; ++i) { - total += data[i].data[0][1]; - } - - // Count the number of slices with percentages below the combine - // threshold; if it turns out to be just one, we won't combine. - - for (var i = 0; i < data.length; ++i) { - var value = data[i].data[0][1]; - if (value / total <= options.series.pie.combine.threshold) { - combined += value; - numCombined++; - if (!color) { - color = data[i].color; - } - } - } - - for (var i = 0; i < data.length; ++i) { - var value = data[i].data[0][1]; - if (numCombined < 2 || value / total > options.series.pie.combine.threshold) { - newdata.push( - $.extend(data[i], { /* extend to allow keeping all other original data values - and using them e.g. in labelFormatter. */ - data: [[1, value]], - color: data[i].color, - label: data[i].label, - angle: value * Math.PI * 2 / total, - percent: value / (total / 100) - }) - ); - } - } - - if (numCombined > 1) { - newdata.push({ - data: [[1, combined]], - color: color, - label: options.series.pie.combine.label, - angle: combined * Math.PI * 2 / total, - percent: combined / (total / 100) - }); - } - - return newdata; - } - - function draw(plot, newCtx) { - - if (!target) { - return; // if no series were passed - } - - var canvasWidth = plot.getPlaceholder().width(), - canvasHeight = plot.getPlaceholder().height(), - legendWidth = target.children().filter(".legend").children().width() || 0; - - ctx = newCtx; - - // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE! - - // When combining smaller slices into an 'other' slice, we need to - // add a new series. Since Flot gives plugins no way to modify the - // list of series, the pie plugin uses a hack where the first call - // to processDatapoints results in a call to setData with the new - // list of series, then subsequent processDatapoints do nothing. - - // The plugin-global 'processed' flag is used to control this hack; - // it starts out false, and is set to true after the first call to - // processDatapoints. - - // Unfortunately this turns future setData calls into no-ops; they - // call processDatapoints, the flag is true, and nothing happens. - - // To fix this we'll set the flag back to false here in draw, when - // all series have been processed, so the next sequence of calls to - // processDatapoints once again starts out with a slice-combine. - // This is really a hack; in 0.9 we need to give plugins a proper - // way to modify series before any processing begins. - - processed = false; - - // calculate maximum radius and center point - - maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2; - centerTop = canvasHeight / 2 + options.series.pie.offset.top; - centerLeft = canvasWidth / 2; - - if (options.series.pie.offset.left == "auto") { - if (options.legend.position.match("w")) { - centerLeft += legendWidth / 2; - } else { - centerLeft -= legendWidth / 2; - } - if (centerLeft < maxRadius) { - centerLeft = maxRadius; - } else if (centerLeft > canvasWidth - maxRadius) { - centerLeft = canvasWidth - maxRadius; - } - } else { - centerLeft += options.series.pie.offset.left; - } - - var slices = plot.getData(), - attempts = 0; - - // Keep shrinking the pie's radius until drawPie returns true, - // indicating that all the labels fit, or we try too many times. - - do { - if (attempts > 0) { - maxRadius *= REDRAW_SHRINK; - } - attempts += 1; - clear(); - if (options.series.pie.tilt <= 0.8) { - drawShadow(); - } - } while (!drawPie() && attempts < REDRAW_ATTEMPTS) - - if (attempts >= REDRAW_ATTEMPTS) { - clear(); - const errorMessage = i18n.translate('common.ui.flotCharts.pie.unableToDrawLabelsInsideCanvasErrorMessage', { - defaultMessage: 'Could not draw pie with labels contained inside canvas', - }); - target.prepend(`
${errorMessage}
`); - } - - if (plot.setSeries && plot.insertLegend) { - plot.setSeries(slices); - plot.insertLegend(); - } - - // we're actually done at this point, just defining internal functions at this point - - function clear() { - ctx.clearRect(0, 0, canvasWidth, canvasHeight); - target.children().filter(".pieLabel, .pieLabelBackground").remove(); - } - - function drawShadow() { - - var shadowLeft = options.series.pie.shadow.left; - var shadowTop = options.series.pie.shadow.top; - var edge = 10; - var alpha = options.series.pie.shadow.alpha; - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) { - return; // shadow would be outside canvas, so don't draw it - } - - ctx.save(); - ctx.translate(shadowLeft,shadowTop); - ctx.globalAlpha = alpha; - ctx.fillStyle = "#000"; - - // center and rotate to starting position - - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - - //radius -= edge; - - for (var i = 1; i <= edge; i++) { - ctx.beginPath(); - ctx.arc(0, 0, radius, 0, Math.PI * 2, false); - ctx.fill(); - radius -= i; - } - - ctx.restore(); - } - - function drawPie() { - - var startAngle = Math.PI * options.series.pie.startAngle; - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - // center and rotate to starting position - - ctx.save(); - ctx.translate(centerLeft,centerTop); - ctx.scale(1, options.series.pie.tilt); - //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera - - // draw slices - - ctx.save(); - var currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) { - slices[i].startAngle = currentAngle; - drawSlice(slices[i].angle, slices[i].color, true); - } - ctx.restore(); - - // draw slice outlines - - if (options.series.pie.stroke.width > 0) { - ctx.save(); - ctx.lineWidth = options.series.pie.stroke.width; - currentAngle = startAngle; - for (var i = 0; i < slices.length; ++i) { - drawSlice(slices[i].angle, options.series.pie.stroke.color, false); - } - ctx.restore(); - } - - // draw donut hole - - drawDonutHole(ctx); - - ctx.restore(); - - // Draw the labels, returning true if they fit within the plot - - if (options.series.pie.label.show) { - return drawLabels(); - } else return true; - - function drawSlice(angle, color, fill) { - - if (angle <= 0 || isNaN(angle)) { - return; - } - - if (fill) { - ctx.fillStyle = color; - } else { - ctx.strokeStyle = color; - ctx.lineJoin = "round"; - } - - ctx.beginPath(); - if (Math.abs(angle - Math.PI * 2) > 0.000000001) { - ctx.moveTo(0, 0); // Center of the pie - } - - //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera - ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false); - ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false); - ctx.closePath(); - //ctx.rotate(angle); // This doesn't work properly in Opera - currentAngle += angle; - - if (fill) { - ctx.fill(); - } else { - ctx.stroke(); - } - } - - function drawLabels() { - - var currentAngle = startAngle; - var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius; - - for (var i = 0; i < slices.length; ++i) { - if (slices[i].percent >= options.series.pie.label.threshold * 100) { - if (!drawLabel(slices[i], currentAngle, i)) { - return false; - } - } - currentAngle += slices[i].angle; - } - - return true; - - function drawLabel(slice, startAngle, index) { - - if (slice.data[0][1] == 0) { - return true; - } - - // format label text - - var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; - - if (lf) { - text = lf(slice.label, slice); - } else { - text = slice.label; - } - - if (plf) { - text = plf(text, slice); - } - - var halfAngle = ((startAngle + slice.angle) + startAngle) / 2; - var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); - var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; - - var html = "" + text + ""; - target.append(html); - - var label = target.children("#pieLabel" + index); - var labelTop = (y - label.height() / 2); - var labelLeft = (x - label.width() / 2); - - label.css("top", labelTop); - label.css("left", labelLeft); - - // check to make sure that the label is not outside the canvas - - if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) { - return false; - } - - if (options.series.pie.label.background.opacity != 0) { - - // put in the transparent background separately to avoid blended labels and label boxes - - var c = options.series.pie.label.background.color; - - if (c == null) { - c = slice.color; - } - - var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;"; - $("
") - .css("opacity", options.series.pie.label.background.opacity) - .insertBefore(label); - } - - return true; - } // end individual label function - } // end drawLabels function - } // end drawPie function - } // end draw function - - // Placed here because it needs to be accessed from multiple locations - - function drawDonutHole(layer) { - if (options.series.pie.innerRadius > 0) { - - // subtract the center - - layer.save(); - var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; - layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color - layer.beginPath(); - layer.fillStyle = options.series.pie.stroke.color; - layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); - layer.fill(); - layer.closePath(); - layer.restore(); - - // add inner stroke - - layer.save(); - layer.beginPath(); - layer.strokeStyle = options.series.pie.stroke.color; - layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false); - layer.stroke(); - layer.closePath(); - layer.restore(); - - // TODO: add extra shadow inside hole (with a mask) if the pie is tilted. - } - } - - //-- Additional Interactive related functions -- - - function isPointInPoly(poly, pt) { - for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) - ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) - && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) - && (c = !c); - return c; - } - - function findNearbySlice(mouseX, mouseY) { - - var slices = plot.getData(), - options = plot.getOptions(), - radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius, - x, y; - - for (var i = 0; i < slices.length; ++i) { - - var s = slices[i]; - - if (s.pie.show) { - - ctx.save(); - ctx.beginPath(); - ctx.moveTo(0, 0); // Center of the pie - //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. - ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false); - ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false); - ctx.closePath(); - x = mouseX - centerLeft; - y = mouseY - centerTop; - - if (ctx.isPointInPath) { - if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) { - ctx.restore(); - return { - datapoint: [s.percent, s.data], - dataIndex: 0, - series: s, - seriesIndex: i - }; - } - } else { - - // excanvas for IE doesn;t support isPointInPath, this is a workaround. - - var p1X = radius * Math.cos(s.startAngle), - p1Y = radius * Math.sin(s.startAngle), - p2X = radius * Math.cos(s.startAngle + s.angle / 4), - p2Y = radius * Math.sin(s.startAngle + s.angle / 4), - p3X = radius * Math.cos(s.startAngle + s.angle / 2), - p3Y = radius * Math.sin(s.startAngle + s.angle / 2), - p4X = radius * Math.cos(s.startAngle + s.angle / 1.5), - p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5), - p5X = radius * Math.cos(s.startAngle + s.angle), - p5Y = radius * Math.sin(s.startAngle + s.angle), - arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]], - arrPoint = [x, y]; - - // TODO: perhaps do some mathematical trickery here with the Y-coordinate to compensate for pie tilt? - - if (isPointInPoly(arrPoly, arrPoint)) { - ctx.restore(); - return { - datapoint: [s.percent, s.data], - dataIndex: 0, - series: s, - seriesIndex: i - }; - } - } - - ctx.restore(); - } - } - - return null; - } - - function onMouseMove(e) { - triggerClickHoverEvent("plothover", e); - } - - function onClick(e) { - triggerClickHoverEvent("plotclick", e); - } - - // trigger click or hover event (they send the same parameters so we share their code) - - function triggerClickHoverEvent(eventname, e) { - - var offset = plot.offset(); - var canvasX = parseInt(e.pageX - offset.left); - var canvasY = parseInt(e.pageY - offset.top); - var item = findNearbySlice(canvasX, canvasY); - - if (options.grid.autoHighlight) { - - // clear auto-highlights - - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.auto == eventname && !(item && h.series == item.series)) { - unhighlight(h.series); - } - } - } - - // highlight the slice - - if (item) { - highlight(item.series, eventname); - } - - // trigger any hover bind events - - var pos = { pageX: e.pageX, pageY: e.pageY }; - target.trigger(eventname, [pos, item]); - } - - function highlight(s, auto) { - //if (typeof s == "number") { - // s = series[s]; - //} - - var i = indexOfHighlight(s); - - if (i == -1) { - highlights.push({ series: s, auto: auto }); - plot.triggerRedrawOverlay(); - } else if (!auto) { - highlights[i].auto = false; - } - } - - function unhighlight(s) { - if (s == null) { - highlights = []; - plot.triggerRedrawOverlay(); - } - - //if (typeof s == "number") { - // s = series[s]; - //} - - var i = indexOfHighlight(s); - - if (i != -1) { - highlights.splice(i, 1); - plot.triggerRedrawOverlay(); - } - } - - function indexOfHighlight(s) { - for (var i = 0; i < highlights.length; ++i) { - var h = highlights[i]; - if (h.series == s) - return i; - } - return -1; - } - - function drawOverlay(plot, octx) { - - var options = plot.getOptions(); - - var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; - - octx.save(); - octx.translate(centerLeft, centerTop); - octx.scale(1, options.series.pie.tilt); - - for (var i = 0; i < highlights.length; ++i) { - drawHighlight(highlights[i].series); - } - - drawDonutHole(octx); - - octx.restore(); - - function drawHighlight(series) { - - if (series.angle <= 0 || isNaN(series.angle)) { - return; - } - - //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); - octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor - octx.beginPath(); - if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) { - octx.moveTo(0, 0); // Center of the pie - } - octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false); - octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false); - octx.closePath(); - octx.fill(); - } - } - } // end init (plugin body) - - // define pie specific options and their default values - - var options = { - series: { - pie: { - show: false, - radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) - innerRadius: 0, /* for donut */ - startAngle: 3/2, - tilt: 1, - shadow: { - left: 5, // shadow left offset - top: 15, // shadow top offset - alpha: 0.02 // shadow alpha - }, - offset: { - top: 0, - left: "auto" - }, - stroke: { - color: "#fff", - width: 1 - }, - label: { - show: "auto", - formatter: function(label, slice) { - return "
" + label + "
" + Math.round(slice.percent) + "%
"; - }, // formatter function - radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) - background: { - color: null, - opacity: 0 - }, - threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) - }, - combine: { - threshold: -1, // percentage at which to combine little slices into one larger slice - color: null, // color to give the new slice (auto-generated if null) - label: "Other" // label to give the new slice - }, - highlight: { - //color: "#fff", // will add this functionality once parseColor is available - opacity: 0.5 - } - } - } - }; - - $.plot.plugins.push({ - init: init, - options: options, - name: "pie", - version: "1.1" - }); - -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.resize.js b/src/legacy/ui/public/flot-charts/jquery.flot.resize.js deleted file mode 100644 index 8a626dda0addb..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.resize.js +++ /dev/null @@ -1,59 +0,0 @@ -/* Flot plugin for automatically redrawing plots as the placeholder resizes. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -It works by listening for changes on the placeholder div (through the jQuery -resize event plugin) - if the size changes, it will redraw the plot. - -There are no options. If you need to disable the plugin for some plots, you -can just fix the size of their placeholders. - -*/ - -/* Inline dependency: - * jQuery resize event - v1.1 - 3/14/2010 - * http://benalman.com/projects/jquery-resize-plugin/ - * - * Copyright (c) 2010 "Cowboy" Ben Alman - * Dual licensed under the MIT and GPL licenses. - * http://benalman.com/about/license/ - */ -(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this); - -(function ($) { - var options = { }; // no options - - function init(plot) { - function onResize() { - var placeholder = plot.getPlaceholder(); - - // somebody might have hidden us and we can't plot - // when we don't have the dimensions - if (placeholder.width() == 0 || placeholder.height() == 0) - return; - - plot.resize(); - plot.setupGrid(); - plot.draw(); - } - - function bindEvents(plot, eventHolder) { - plot.getPlaceholder().resize(onResize); - } - - function shutdown(plot, eventHolder) { - plot.getPlaceholder().unbind("resize", onResize); - } - - plot.hooks.bindEvents.push(bindEvents); - plot.hooks.shutdown.push(shutdown); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'resize', - version: '1.0' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.selection.js b/src/legacy/ui/public/flot-charts/jquery.flot.selection.js deleted file mode 100644 index c8707b30f4e6f..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.selection.js +++ /dev/null @@ -1,360 +0,0 @@ -/* Flot plugin for selecting regions of a plot. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The plugin supports these options: - -selection: { - mode: null or "x" or "y" or "xy", - color: color, - shape: "round" or "miter" or "bevel", - minSize: number of pixels -} - -Selection support is enabled by setting the mode to one of "x", "y" or "xy". -In "x" mode, the user will only be able to specify the x range, similarly for -"y" mode. For "xy", the selection becomes a rectangle where both ranges can be -specified. "color" is color of the selection (if you need to change the color -later on, you can get to it with plot.getOptions().selection.color). "shape" -is the shape of the corners of the selection. - -"minSize" is the minimum size a selection can be in pixels. This value can -be customized to determine the smallest size a selection can be and still -have the selection rectangle be displayed. When customizing this value, the -fact that it refers to pixels, not axis units must be taken into account. -Thus, for example, if there is a bar graph in time mode with BarWidth set to 1 -minute, setting "minSize" to 1 will not make the minimum selection size 1 -minute, but rather 1 pixel. Note also that setting "minSize" to 0 will prevent -"plotunselected" events from being fired when the user clicks the mouse without -dragging. - -When selection support is enabled, a "plotselected" event will be emitted on -the DOM element you passed into the plot function. The event handler gets a -parameter with the ranges selected on the axes, like this: - - placeholder.bind( "plotselected", function( event, ranges ) { - alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) - // similar for yaxis - with multiple axes, the extra ones are in - // x2axis, x3axis, ... - }); - -The "plotselected" event is only fired when the user has finished making the -selection. A "plotselecting" event is fired during the process with the same -parameters as the "plotselected" event, in case you want to know what's -happening while it's happening, - -A "plotunselected" event with no arguments is emitted when the user clicks the -mouse to remove the selection. As stated above, setting "minSize" to 0 will -destroy this behavior. - -The plugin also adds the following methods to the plot object: - -- setSelection( ranges, preventEvent ) - - Set the selection rectangle. The passed in ranges is on the same form as - returned in the "plotselected" event. If the selection mode is "x", you - should put in either an xaxis range, if the mode is "y" you need to put in - an yaxis range and both xaxis and yaxis if the selection mode is "xy", like - this: - - setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); - - setSelection will trigger the "plotselected" event when called. If you don't - want that to happen, e.g. if you're inside a "plotselected" handler, pass - true as the second parameter. If you are using multiple axes, you can - specify the ranges on any of those, e.g. as x2axis/x3axis/... instead of - xaxis, the plugin picks the first one it sees. - -- clearSelection( preventEvent ) - - Clear the selection rectangle. Pass in true to avoid getting a - "plotunselected" event. - -- getSelection() - - Returns the current selection in the same format as the "plotselected" - event. If there's currently no selection, the function returns null. - -*/ - -(function ($) { - function init(plot) { - var selection = { - first: { x: -1, y: -1}, second: { x: -1, y: -1}, - show: false, - active: false - }; - - // FIXME: The drag handling implemented here should be - // abstracted out, there's some similar code from a library in - // the navigation plugin, this should be massaged a bit to fit - // the Flot cases here better and reused. Doing this would - // make this plugin much slimmer. - var savedhandlers = {}; - - var mouseUpHandler = null; - - function onMouseMove(e) { - if (selection.active) { - updateSelection(e); - - plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); - } - } - - function onMouseDown(e) { - if (e.which != 1) // only accept left-click - return; - - // cancel out any text selections - document.body.focus(); - - // prevent text selection and drag in old-school browsers - if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { - savedhandlers.onselectstart = document.onselectstart; - document.onselectstart = function () { return false; }; - } - if (document.ondrag !== undefined && savedhandlers.ondrag == null) { - savedhandlers.ondrag = document.ondrag; - document.ondrag = function () { return false; }; - } - - setSelectionPos(selection.first, e); - - selection.active = true; - - // this is a bit silly, but we have to use a closure to be - // able to whack the same handler again - mouseUpHandler = function (e) { onMouseUp(e); }; - - $(document).one("mouseup", mouseUpHandler); - } - - function onMouseUp(e) { - mouseUpHandler = null; - - // revert drag stuff for old-school browsers - if (document.onselectstart !== undefined) - document.onselectstart = savedhandlers.onselectstart; - if (document.ondrag !== undefined) - document.ondrag = savedhandlers.ondrag; - - // no more dragging - selection.active = false; - updateSelection(e); - - if (selectionIsSane()) - triggerSelectedEvent(); - else { - // this counts as a clear - plot.getPlaceholder().trigger("plotunselected", [ ]); - plot.getPlaceholder().trigger("plotselecting", [ null ]); - } - - return false; - } - - function getSelection() { - if (!selectionIsSane()) - return null; - - if (!selection.show) return null; - - var r = {}, c1 = selection.first, c2 = selection.second; - $.each(plot.getAxes(), function (name, axis) { - if (axis.used) { - var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); - r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; - } - }); - return r; - } - - function triggerSelectedEvent() { - var r = getSelection(); - - plot.getPlaceholder().trigger("plotselected", [ r ]); - - // backwards-compat stuff, to be removed in future - if (r.xaxis && r.yaxis) - plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); - } - - function clamp(min, value, max) { - return value < min ? min: (value > max ? max: value); - } - - function setSelectionPos(pos, e) { - var o = plot.getOptions(); - var offset = plot.getPlaceholder().offset(); - var plotOffset = plot.getPlotOffset(); - pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); - pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); - - if (o.selection.mode == "y") - pos.x = pos == selection.first ? 0 : plot.width(); - - if (o.selection.mode == "x") - pos.y = pos == selection.first ? 0 : plot.height(); - } - - function updateSelection(pos) { - if (pos.pageX == null) - return; - - setSelectionPos(selection.second, pos); - if (selectionIsSane()) { - selection.show = true; - plot.triggerRedrawOverlay(); - } - else - clearSelection(true); - } - - function clearSelection(preventEvent) { - if (selection.show) { - selection.show = false; - plot.triggerRedrawOverlay(); - if (!preventEvent) - plot.getPlaceholder().trigger("plotunselected", [ ]); - } - } - - // function taken from markings support in Flot - function extractRange(ranges, coord) { - var axis, from, to, key, axes = plot.getAxes(); - - for (var k in axes) { - axis = axes[k]; - if (axis.direction == coord) { - key = coord + axis.n + "axis"; - if (!ranges[key] && axis.n == 1) - key = coord + "axis"; // support x1axis as xaxis - if (ranges[key]) { - from = ranges[key].from; - to = ranges[key].to; - break; - } - } - } - - // backwards-compat stuff - to be removed in future - if (!ranges[key]) { - axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; - from = ranges[coord + "1"]; - to = ranges[coord + "2"]; - } - - // auto-reverse as an added bonus - if (from != null && to != null && from > to) { - var tmp = from; - from = to; - to = tmp; - } - - return { from: from, to: to, axis: axis }; - } - - function setSelection(ranges, preventEvent) { - var axis, range, o = plot.getOptions(); - - if (o.selection.mode == "y") { - selection.first.x = 0; - selection.second.x = plot.width(); - } - else { - range = extractRange(ranges, "x"); - - selection.first.x = range.axis.p2c(range.from); - selection.second.x = range.axis.p2c(range.to); - } - - if (o.selection.mode == "x") { - selection.first.y = 0; - selection.second.y = plot.height(); - } - else { - range = extractRange(ranges, "y"); - - selection.first.y = range.axis.p2c(range.from); - selection.second.y = range.axis.p2c(range.to); - } - - selection.show = true; - plot.triggerRedrawOverlay(); - if (!preventEvent && selectionIsSane()) - triggerSelectedEvent(); - } - - function selectionIsSane() { - var minSize = plot.getOptions().selection.minSize; - return Math.abs(selection.second.x - selection.first.x) >= minSize && - Math.abs(selection.second.y - selection.first.y) >= minSize; - } - - plot.clearSelection = clearSelection; - plot.setSelection = setSelection; - plot.getSelection = getSelection; - - plot.hooks.bindEvents.push(function(plot, eventHolder) { - var o = plot.getOptions(); - if (o.selection.mode != null) { - eventHolder.mousemove(onMouseMove); - eventHolder.mousedown(onMouseDown); - } - }); - - - plot.hooks.drawOverlay.push(function (plot, ctx) { - // draw selection - if (selection.show && selectionIsSane()) { - var plotOffset = plot.getPlotOffset(); - var o = plot.getOptions(); - - ctx.save(); - ctx.translate(plotOffset.left, plotOffset.top); - - var c = $.color.parse(o.selection.color); - - ctx.strokeStyle = c.scale('a', 0.8).toString(); - ctx.lineWidth = 1; - ctx.lineJoin = o.selection.shape; - ctx.fillStyle = c.scale('a', 0.4).toString(); - - var x = Math.min(selection.first.x, selection.second.x) + 0.5, - y = Math.min(selection.first.y, selection.second.y) + 0.5, - w = Math.abs(selection.second.x - selection.first.x) - 1, - h = Math.abs(selection.second.y - selection.first.y) - 1; - - ctx.fillRect(x, y, w, h); - ctx.strokeRect(x, y, w, h); - - ctx.restore(); - } - }); - - plot.hooks.shutdown.push(function (plot, eventHolder) { - eventHolder.unbind("mousemove", onMouseMove); - eventHolder.unbind("mousedown", onMouseDown); - - if (mouseUpHandler) - $(document).unbind("mouseup", mouseUpHandler); - }); - - } - - $.plot.plugins.push({ - init: init, - options: { - selection: { - mode: null, // one of null, "x", "y" or "xy" - color: "#e8cfac", - shape: "round", // one of "round", "miter", or "bevel" - minSize: 5 // minimum number of pixels - } - }, - name: 'selection', - version: '1.1' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.stack.js b/src/legacy/ui/public/flot-charts/jquery.flot.stack.js deleted file mode 100644 index 0d91c0f3c0160..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.stack.js +++ /dev/null @@ -1,188 +0,0 @@ -/* Flot plugin for stacking data sets rather than overlaying them. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The plugin assumes the data is sorted on x (or y if stacking horizontally). -For line charts, it is assumed that if a line has an undefined gap (from a -null point), then the line above it should have the same gap - insert zeros -instead of "null" if you want another behaviour. This also holds for the start -and end of the chart. Note that stacking a mix of positive and negative values -in most instances doesn't make sense (so it looks weird). - -Two or more series are stacked when their "stack" attribute is set to the same -key (which can be any number or string or just "true"). To specify the default -stack, you can set the stack option like this: - - series: { - stack: null/false, true, or a key (number/string) - } - -You can also specify it for a single series, like this: - - $.plot( $("#placeholder"), [{ - data: [ ... ], - stack: true - }]) - -The stacking order is determined by the order of the data series in the array -(later series end up on top of the previous). - -Internally, the plugin modifies the datapoints in each series, adding an -offset to the y value. For line series, extra data points are inserted through -interpolation. If there's a second y value, it's also adjusted (e.g for bar -charts or filled areas). - -*/ - -(function ($) { - var options = { - series: { stack: null } // or number/string - }; - - function init(plot) { - function findMatchingSeries(s, allseries) { - var res = null; - for (var i = 0; i < allseries.length; ++i) { - if (s == allseries[i]) - break; - - if (allseries[i].stack == s.stack) - res = allseries[i]; - } - - return res; - } - - function stackData(plot, s, datapoints) { - if (s.stack == null || s.stack === false) - return; - - var other = findMatchingSeries(s, plot.getData()); - if (!other) - return; - - var ps = datapoints.pointsize, - points = datapoints.points, - otherps = other.datapoints.pointsize, - otherpoints = other.datapoints.points, - newpoints = [], - px, py, intery, qx, qy, bottom, - withlines = s.lines.show, - horizontal = s.bars.horizontal, - withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), - withsteps = withlines && s.lines.steps, - fromgap = true, - keyOffset = horizontal ? 1 : 0, - accumulateOffset = horizontal ? 0 : 1, - i = 0, j = 0, l, m; - - while (true) { - if (i >= points.length) - break; - - l = newpoints.length; - - if (points[i] == null) { - // copy gaps - for (m = 0; m < ps; ++m) - newpoints.push(points[i + m]); - i += ps; - } - else if (j >= otherpoints.length) { - // for lines, we can't use the rest of the points - if (!withlines) { - for (m = 0; m < ps; ++m) - newpoints.push(points[i + m]); - } - i += ps; - } - else if (otherpoints[j] == null) { - // oops, got a gap - for (m = 0; m < ps; ++m) - newpoints.push(null); - fromgap = true; - j += otherps; - } - else { - // cases where we actually got two points - px = points[i + keyOffset]; - py = points[i + accumulateOffset]; - qx = otherpoints[j + keyOffset]; - qy = otherpoints[j + accumulateOffset]; - bottom = 0; - - if (px == qx) { - for (m = 0; m < ps; ++m) - newpoints.push(points[i + m]); - - newpoints[l + accumulateOffset] += qy; - bottom = qy; - - i += ps; - j += otherps; - } - else if (px > qx) { - // we got past point below, might need to - // insert interpolated extra point - if (withlines && i > 0 && points[i - ps] != null) { - intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); - newpoints.push(qx); - newpoints.push(intery + qy); - for (m = 2; m < ps; ++m) - newpoints.push(points[i + m]); - bottom = qy; - } - - j += otherps; - } - else { // px < qx - if (fromgap && withlines) { - // if we come from a gap, we just skip this point - i += ps; - continue; - } - - for (m = 0; m < ps; ++m) - newpoints.push(points[i + m]); - - // we might be able to interpolate a point below, - // this can give us a better y - if (withlines && j > 0 && otherpoints[j - otherps] != null) - bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); - - newpoints[l + accumulateOffset] += bottom; - - i += ps; - } - - fromgap = false; - - if (l != newpoints.length && withbottom) - newpoints[l + 2] += bottom; - } - - // maintain the line steps invariant - if (withsteps && l != newpoints.length && l > 0 - && newpoints[l] != null - && newpoints[l] != newpoints[l - ps] - && newpoints[l + 1] != newpoints[l - ps + 1]) { - for (m = 0; m < ps; ++m) - newpoints[l + ps + m] = newpoints[l + m]; - newpoints[l + 1] = newpoints[l - ps + 1]; - } - } - - datapoints.points = newpoints; - } - - plot.hooks.processDatapoints.push(stackData); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'stack', - version: '1.2' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.symbol.js b/src/legacy/ui/public/flot-charts/jquery.flot.symbol.js deleted file mode 100644 index 79f634971b6fa..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.symbol.js +++ /dev/null @@ -1,71 +0,0 @@ -/* Flot plugin that adds some extra symbols for plotting points. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The symbols are accessed as strings through the standard symbol options: - - series: { - points: { - symbol: "square" // or "diamond", "triangle", "cross" - } - } - -*/ - -(function ($) { - function processRawData(plot, series, datapoints) { - // we normalize the area of each symbol so it is approximately the - // same as a circle of the given radius - - var handlers = { - square: function (ctx, x, y, radius, shadow) { - // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 - var size = radius * Math.sqrt(Math.PI) / 2; - ctx.rect(x - size, y - size, size + size, size + size); - }, - diamond: function (ctx, x, y, radius, shadow) { - // pi * r^2 = 2s^2 => s = r * sqrt(pi/2) - var size = radius * Math.sqrt(Math.PI / 2); - ctx.moveTo(x - size, y); - ctx.lineTo(x, y - size); - ctx.lineTo(x + size, y); - ctx.lineTo(x, y + size); - ctx.lineTo(x - size, y); - }, - triangle: function (ctx, x, y, radius, shadow) { - // pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) - var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); - var height = size * Math.sin(Math.PI / 3); - ctx.moveTo(x - size/2, y + height/2); - ctx.lineTo(x + size/2, y + height/2); - if (!shadow) { - ctx.lineTo(x, y - height/2); - ctx.lineTo(x - size/2, y + height/2); - } - }, - cross: function (ctx, x, y, radius, shadow) { - // pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 - var size = radius * Math.sqrt(Math.PI) / 2; - ctx.moveTo(x - size, y - size); - ctx.lineTo(x + size, y + size); - ctx.moveTo(x - size, y + size); - ctx.lineTo(x + size, y - size); - } - }; - - var s = series.points.symbol; - if (handlers[s]) - series.points.symbol = handlers[s]; - } - - function init(plot) { - plot.hooks.processDatapoints.push(processRawData); - } - - $.plot.plugins.push({ - init: init, - name: 'symbols', - version: '1.0' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.threshold.js b/src/legacy/ui/public/flot-charts/jquery.flot.threshold.js deleted file mode 100644 index 8c99c401d87e5..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.threshold.js +++ /dev/null @@ -1,142 +0,0 @@ -/* Flot plugin for thresholding data. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -The plugin supports these options: - - series: { - threshold: { - below: number - color: colorspec - } - } - -It can also be applied to a single series, like this: - - $.plot( $("#placeholder"), [{ - data: [ ... ], - threshold: { ... } - }]) - -An array can be passed for multiple thresholding, like this: - - threshold: [{ - below: number1 - color: color1 - },{ - below: number2 - color: color2 - }] - -These multiple threshold objects can be passed in any order since they are -sorted by the processing function. - -The data points below "below" are drawn with the specified color. This makes -it easy to mark points below 0, e.g. for budget data. - -Internally, the plugin works by splitting the data into two series, above and -below the threshold. The extra series below the threshold will have its label -cleared and the special "originSeries" attribute set to the original series. -You may need to check for this in hover events. - -*/ - -(function ($) { - var options = { - series: { threshold: null } // or { below: number, color: color spec} - }; - - function init(plot) { - function thresholdData(plot, s, datapoints, below, color) { - var ps = datapoints.pointsize, i, x, y, p, prevp, - thresholded = $.extend({}, s); // note: shallow copy - - thresholded.datapoints = { points: [], pointsize: ps, format: datapoints.format }; - thresholded.label = null; - thresholded.color = color; - thresholded.threshold = null; - thresholded.originSeries = s; - thresholded.data = []; - - var origpoints = datapoints.points, - addCrossingPoints = s.lines.show; - - var threspoints = []; - var newpoints = []; - var m; - - for (i = 0; i < origpoints.length; i += ps) { - x = origpoints[i]; - y = origpoints[i + 1]; - - prevp = p; - if (y < below) - p = threspoints; - else - p = newpoints; - - if (addCrossingPoints && prevp != p && x != null - && i > 0 && origpoints[i - ps] != null) { - var interx = x + (below - y) * (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]); - prevp.push(interx); - prevp.push(below); - for (m = 2; m < ps; ++m) - prevp.push(origpoints[i + m]); - - p.push(null); // start new segment - p.push(null); - for (m = 2; m < ps; ++m) - p.push(origpoints[i + m]); - p.push(interx); - p.push(below); - for (m = 2; m < ps; ++m) - p.push(origpoints[i + m]); - } - - p.push(x); - p.push(y); - for (m = 2; m < ps; ++m) - p.push(origpoints[i + m]); - } - - datapoints.points = newpoints; - thresholded.datapoints.points = threspoints; - - if (thresholded.datapoints.points.length > 0) { - var origIndex = $.inArray(s, plot.getData()); - // Insert newly-generated series right after original one (to prevent it from becoming top-most) - plot.getData().splice(origIndex + 1, 0, thresholded); - } - - // FIXME: there are probably some edge cases left in bars - } - - function processThresholds(plot, s, datapoints) { - if (!s.threshold) - return; - - if (s.threshold instanceof Array) { - s.threshold.sort(function(a, b) { - return a.below - b.below; - }); - - $(s.threshold).each(function(i, th) { - thresholdData(plot, s, datapoints, th.below, th.color); - }); - } - else { - thresholdData(plot, s, datapoints, s.threshold.below, s.threshold.color); - } - } - - plot.hooks.processDatapoints.push(processThresholds); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'threshold', - version: '1.2' - }); -})(jQuery); diff --git a/src/legacy/ui/public/flot-charts/jquery.flot.time.js b/src/legacy/ui/public/flot-charts/jquery.flot.time.js deleted file mode 100644 index 7612a03302764..0000000000000 --- a/src/legacy/ui/public/flot-charts/jquery.flot.time.js +++ /dev/null @@ -1,473 +0,0 @@ -/* Pretty handling of time axes. - -Copyright (c) 2007-2014 IOLA and Ole Laursen. -Licensed under the MIT license. - -Set axis.mode to "time" to enable. See the section "Time series data" in -API.txt for details. - -*/ - -import { i18n } from '@kbn/i18n'; - -(function($) { - - var options = { - xaxis: { - timezone: null, // "browser" for local to the client or timezone for timezone-js - timeformat: null, // format string to use - twelveHourClock: false, // 12 or 24 time in time mode - monthNames: null // list of names of months - } - }; - - // round to nearby lower multiple of base - - function floorInBase(n, base) { - return base * Math.floor(n / base); - } - - // Returns a string with the date d formatted according to fmt. - // A subset of the Open Group's strftime format is supported. - - function formatDate(d, fmt, monthNames, dayNames) { - - if (typeof d.strftime == "function") { - return d.strftime(fmt); - } - - var leftPad = function(n, pad) { - n = "" + n; - pad = "" + (pad == null ? "0" : pad); - return n.length == 1 ? pad + n : n; - }; - - var r = []; - var escape = false; - var hours = d.getHours(); - var isAM = hours < 12; - - if (monthNames == null) { - monthNames = [ - i18n.translate('common.ui.flotCharts.janLabel', { - defaultMessage: 'Jan', - }), i18n.translate('common.ui.flotCharts.febLabel', { - defaultMessage: 'Feb', - }), i18n.translate('common.ui.flotCharts.marLabel', { - defaultMessage: 'Mar', - }), i18n.translate('common.ui.flotCharts.aprLabel', { - defaultMessage: 'Apr', - }), i18n.translate('common.ui.flotCharts.mayLabel', { - defaultMessage: 'May', - }), i18n.translate('common.ui.flotCharts.junLabel', { - defaultMessage: 'Jun', - }), i18n.translate('common.ui.flotCharts.julLabel', { - defaultMessage: 'Jul', - }), i18n.translate('common.ui.flotCharts.augLabel', { - defaultMessage: 'Aug', - }), i18n.translate('common.ui.flotCharts.sepLabel', { - defaultMessage: 'Sep', - }), i18n.translate('common.ui.flotCharts.octLabel', { - defaultMessage: 'Oct', - }), i18n.translate('common.ui.flotCharts.novLabel', { - defaultMessage: 'Nov', - }), i18n.translate('common.ui.flotCharts.decLabel', { - defaultMessage: 'Dec', - })]; - } - - if (dayNames == null) { - dayNames = [i18n.translate('common.ui.flotCharts.sunLabel', { - defaultMessage: 'Sun', - }), i18n.translate('common.ui.flotCharts.monLabel', { - defaultMessage: 'Mon', - }), i18n.translate('common.ui.flotCharts.tueLabel', { - defaultMessage: 'Tue', - }), i18n.translate('common.ui.flotCharts.wedLabel', { - defaultMessage: 'Wed', - }), i18n.translate('common.ui.flotCharts.thuLabel', { - defaultMessage: 'Thu', - }), i18n.translate('common.ui.flotCharts.friLabel', { - defaultMessage: 'Fri', - }), i18n.translate('common.ui.flotCharts.satLabel', { - defaultMessage: 'Sat', - })]; - } - - var hours12; - - if (hours > 12) { - hours12 = hours - 12; - } else if (hours == 0) { - hours12 = 12; - } else { - hours12 = hours; - } - - for (var i = 0; i < fmt.length; ++i) { - - var c = fmt.charAt(i); - - if (escape) { - switch (c) { - case 'a': c = "" + dayNames[d.getDay()]; break; - case 'b': c = "" + monthNames[d.getMonth()]; break; - case 'd': c = leftPad(d.getDate()); break; - case 'e': c = leftPad(d.getDate(), " "); break; - case 'h': // For back-compat with 0.7; remove in 1.0 - case 'H': c = leftPad(hours); break; - case 'I': c = leftPad(hours12); break; - case 'l': c = leftPad(hours12, " "); break; - case 'm': c = leftPad(d.getMonth() + 1); break; - case 'M': c = leftPad(d.getMinutes()); break; - // quarters not in Open Group's strftime specification - case 'q': - c = "" + (Math.floor(d.getMonth() / 3) + 1); break; - case 'S': c = leftPad(d.getSeconds()); break; - case 'y': c = leftPad(d.getFullYear() % 100); break; - case 'Y': c = "" + d.getFullYear(); break; - case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; - case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; - case 'w': c = "" + d.getDay(); break; - } - r.push(c); - escape = false; - } else { - if (c == "%") { - escape = true; - } else { - r.push(c); - } - } - } - - return r.join(""); - } - - // To have a consistent view of time-based data independent of which time - // zone the client happens to be in we need a date-like object independent - // of time zones. This is done through a wrapper that only calls the UTC - // versions of the accessor methods. - - function makeUtcWrapper(d) { - - function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) { - sourceObj[sourceMethod] = function() { - return targetObj[targetMethod].apply(targetObj, arguments); - }; - }; - - var utc = { - date: d - }; - - // support strftime, if found - - if (d.strftime != undefined) { - addProxyMethod(utc, "strftime", d, "strftime"); - } - - addProxyMethod(utc, "getTime", d, "getTime"); - addProxyMethod(utc, "setTime", d, "setTime"); - - var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"]; - - for (var p = 0; p < props.length; p++) { - addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]); - addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]); - } - - return utc; - }; - - // select time zone strategy. This returns a date-like object tied to the - // desired timezone - - function dateGenerator(ts, opts) { - if (opts.timezone == "browser") { - return new Date(ts); - } else if (!opts.timezone || opts.timezone == "utc") { - return makeUtcWrapper(new Date(ts)); - } else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") { - var d = new timezoneJS.Date(); - // timezone-js is fickle, so be sure to set the time zone before - // setting the time. - d.setTimezone(opts.timezone); - d.setTime(ts); - return d; - } else { - return makeUtcWrapper(new Date(ts)); - } - } - - // map of app. size of time units in milliseconds - - var timeUnitSize = { - "second": 1000, - "minute": 60 * 1000, - "hour": 60 * 60 * 1000, - "day": 24 * 60 * 60 * 1000, - "month": 30 * 24 * 60 * 60 * 1000, - "quarter": 3 * 30 * 24 * 60 * 60 * 1000, - "year": 365.2425 * 24 * 60 * 60 * 1000 - }; - - // the allowed tick sizes, after 1 year we use - // an integer algorithm - - var baseSpec = [ - [1, "second"], [2, "second"], [5, "second"], [10, "second"], - [30, "second"], - [1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], - [30, "minute"], - [1, "hour"], [2, "hour"], [4, "hour"], - [8, "hour"], [12, "hour"], - [1, "day"], [2, "day"], [3, "day"], - [0.25, "month"], [0.5, "month"], [1, "month"], - [2, "month"] - ]; - - // we don't know which variant(s) we'll need yet, but generating both is - // cheap - - var specMonths = baseSpec.concat([[3, "month"], [6, "month"], - [1, "year"]]); - var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"], - [1, "year"]]); - - function init(plot) { - plot.hooks.processOptions.push(function (plot, options) { - $.each(plot.getAxes(), function(axisName, axis) { - - var opts = axis.options; - - if (opts.mode == "time") { - axis.tickGenerator = function(axis) { - - var ticks = []; - var d = dateGenerator(axis.min, opts); - var minSize = 0; - - // make quarter use a possibility if quarters are - // mentioned in either of these options - - var spec = (opts.tickSize && opts.tickSize[1] === - "quarter") || - (opts.minTickSize && opts.minTickSize[1] === - "quarter") ? specQuarters : specMonths; - - if (opts.minTickSize != null) { - if (typeof opts.tickSize == "number") { - minSize = opts.tickSize; - } else { - minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; - } - } - - for (var i = 0; i < spec.length - 1; ++i) { - if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]] - + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 - && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) { - break; - } - } - - var size = spec[i][0]; - var unit = spec[i][1]; - - // special-case the possibility of several years - - if (unit == "year") { - - // if given a minTickSize in years, just use it, - // ensuring that it's an integer - - if (opts.minTickSize != null && opts.minTickSize[1] == "year") { - size = Math.floor(opts.minTickSize[0]); - } else { - - var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10)); - var norm = (axis.delta / timeUnitSize.year) / magn; - - if (norm < 1.5) { - size = 1; - } else if (norm < 3) { - size = 2; - } else if (norm < 7.5) { - size = 5; - } else { - size = 10; - } - - size *= magn; - } - - // minimum size for years is 1 - - if (size < 1) { - size = 1; - } - } - - axis.tickSize = opts.tickSize || [size, unit]; - var tickSize = axis.tickSize[0]; - unit = axis.tickSize[1]; - - var step = tickSize * timeUnitSize[unit]; - - if (unit == "second") { - d.setSeconds(floorInBase(d.getSeconds(), tickSize)); - } else if (unit == "minute") { - d.setMinutes(floorInBase(d.getMinutes(), tickSize)); - } else if (unit == "hour") { - d.setHours(floorInBase(d.getHours(), tickSize)); - } else if (unit == "month") { - d.setMonth(floorInBase(d.getMonth(), tickSize)); - } else if (unit == "quarter") { - d.setMonth(3 * floorInBase(d.getMonth() / 3, - tickSize)); - } else if (unit == "year") { - d.setFullYear(floorInBase(d.getFullYear(), tickSize)); - } - - // reset smaller components - - d.setMilliseconds(0); - - if (step >= timeUnitSize.minute) { - d.setSeconds(0); - } - if (step >= timeUnitSize.hour) { - d.setMinutes(0); - } - if (step >= timeUnitSize.day) { - d.setHours(0); - } - if (step >= timeUnitSize.day * 4) { - d.setDate(1); - } - if (step >= timeUnitSize.month * 2) { - d.setMonth(floorInBase(d.getMonth(), 3)); - } - if (step >= timeUnitSize.quarter * 2) { - d.setMonth(floorInBase(d.getMonth(), 6)); - } - if (step >= timeUnitSize.year) { - d.setMonth(0); - } - - var carry = 0; - var v = Number.NaN; - var prev; - - do { - - prev = v; - v = d.getTime(); - ticks.push(v); - - if (unit == "month" || unit == "quarter") { - if (tickSize < 1) { - - // a bit complicated - we'll divide the - // month/quarter up but we need to take - // care of fractions so we don't end up in - // the middle of a day - - d.setDate(1); - var start = d.getTime(); - d.setMonth(d.getMonth() + - (unit == "quarter" ? 3 : 1)); - var end = d.getTime(); - d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); - carry = d.getHours(); - d.setHours(0); - } else { - d.setMonth(d.getMonth() + - tickSize * (unit == "quarter" ? 3 : 1)); - } - } else if (unit == "year") { - d.setFullYear(d.getFullYear() + tickSize); - } else { - d.setTime(v + step); - } - } while (v < axis.max && v != prev); - - return ticks; - }; - - axis.tickFormatter = function (v, axis) { - - var d = dateGenerator(v, axis.options); - - // first check global format - - if (opts.timeformat != null) { - return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames); - } - - // possibly use quarters if quarters are mentioned in - // any of these places - - var useQuarters = (axis.options.tickSize && - axis.options.tickSize[1] == "quarter") || - (axis.options.minTickSize && - axis.options.minTickSize[1] == "quarter"); - - var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; - var span = axis.max - axis.min; - var suffix = (opts.twelveHourClock) ? " %p" : ""; - var hourCode = (opts.twelveHourClock) ? "%I" : "%H"; - var fmt; - - if (t < timeUnitSize.minute) { - fmt = hourCode + ":%M:%S" + suffix; - } else if (t < timeUnitSize.day) { - if (span < 2 * timeUnitSize.day) { - fmt = hourCode + ":%M" + suffix; - } else { - fmt = "%b %d " + hourCode + ":%M" + suffix; - } - } else if (t < timeUnitSize.month) { - fmt = "%b %d"; - } else if ((useQuarters && t < timeUnitSize.quarter) || - (!useQuarters && t < timeUnitSize.year)) { - if (span < timeUnitSize.year) { - fmt = "%b"; - } else { - fmt = "%b %Y"; - } - } else if (useQuarters && t < timeUnitSize.year) { - if (span < timeUnitSize.year) { - fmt = "Q%q"; - } else { - fmt = "Q%q %Y"; - } - } else { - fmt = "%Y"; - } - - var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames); - - return rt; - }; - } - }); - }); - } - - $.plot.plugins.push({ - init: init, - options: options, - name: 'time', - version: '1.0' - }); - - // Time-axis support used to be in Flot core, which exposed the - // formatDate function on the plot object. Various plugins depend - // on the function, so we need to re-expose it here. - - $.plot.formatDate = formatDate; - $.plot.dateGenerator = dateGenerator; - -})(jQuery); diff --git a/src/legacy/ui/public/i18n/__snapshots__/index.test.tsx.snap b/src/legacy/ui/public/i18n/__snapshots__/index.test.tsx.snap deleted file mode 100644 index fd6a0a07ba39c..0000000000000 --- a/src/legacy/ui/public/i18n/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,10 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`ui/i18n renders children and forwards properties 1`] = ` -
- Context: - - Child: some prop:100500 - -
-`; diff --git a/src/legacy/ui/public/i18n/index.test.tsx b/src/legacy/ui/public/i18n/index.test.tsx deleted file mode 100644 index be8ab4cf8d696..0000000000000 --- a/src/legacy/ui/public/i18n/index.test.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { render } from 'enzyme'; -import PropTypes from 'prop-types'; -import React from 'react'; - -jest.mock('angular-sanitize', () => {}); -jest.mock('ui/new_platform', () => ({ - npStart: { - core: { - i18n: { Context: ({ children }: any) =>
Context: {children}
}, - }, - }, -})); - -import { wrapInI18nContext } from '.'; - -describe('ui/i18n', () => { - test('renders children and forwards properties', () => { - const mockPropTypes = { - stringProp: PropTypes.string.isRequired, - numberProp: PropTypes.number, - }; - - const WrappedComponent = wrapInI18nContext( - class extends React.PureComponent<{ [P in keyof typeof mockPropTypes]: unknown }> { - public static propTypes = mockPropTypes; - - public render() { - return ( - - Child: {this.props.stringProp}:{this.props.numberProp} - - ); - } - } - ); - - expect(WrappedComponent.propTypes).toBe(mockPropTypes); - expect( - render() - ).toMatchSnapshot(); - }); -}); diff --git a/src/legacy/ui/public/i18n/index.tsx b/src/legacy/ui/public/i18n/index.tsx deleted file mode 100644 index 290e82a1334b9..0000000000000 --- a/src/legacy/ui/public/i18n/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -// required for `ngSanitize` angular module -import 'angular-sanitize'; - -import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular'; -// @ts-ignore -import { uiModules } from 'ui/modules'; -import { npStart } from 'ui/new_platform'; - -export const I18nContext = npStart.core.i18n.Context; - -export function wrapInI18nContext

(ComponentToWrap: React.ComponentType

) { - const ContextWrapper: React.FC

= (props) => { - return ( - - - - ); - }; - - // Original propTypes from the wrapped component should be re-exposed - // since it will be used by reactDirective Angular service - // that will rely on propTypes to watch attributes with these names - ContextWrapper.propTypes = ComponentToWrap.propTypes; - - return ContextWrapper; -} - -uiModules - .get('i18n', ['ngSanitize']) - .provider('i18n', I18nProvider) - .filter('i18n', i18nFilter) - .directive('i18nId', i18nDirective); diff --git a/src/legacy/ui/public/indexed_array/__tests__/indexed_array.js b/src/legacy/ui/public/indexed_array/__tests__/indexed_array.js deleted file mode 100644 index df96a58a6e99f..0000000000000 --- a/src/legacy/ui/public/indexed_array/__tests__/indexed_array.js +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import expect from '@kbn/expect'; -import { IndexedArray } from '..'; - -// this is generally a data-structure that IndexedArray is good for managing -const users = [ - { name: 'John', id: 6, username: 'beast', group: 'admins' }, - { name: 'Anon', id: 0, username: 'shhhh', group: 'secret' }, - { name: 'Fern', id: 42, username: 'kitty', group: 'editor' }, - { name: 'Mary', id: 55, username: 'sheep', group: 'editor' }, -]; - -// this is how we used to accomplish this, before IndexedArray -users.byName = _.keyBy(users, 'name'); -users.byUsername = _.keyBy(users, 'username'); -users.byGroup = _.groupBy(users, 'group'); -users.inIdOrder = _.sortBy(users, 'id'); - -// then things started becoming unruly... so IndexedArray! - -describe('IndexedArray', function () { - describe('Basics', function () { - let reg; - - beforeEach(function () { - reg = new IndexedArray(); - }); - - it('Extends Array', function () { - expect(reg).to.be.a(Array); - }); - - it('fails basic lodash check', function () { - expect(Array.isArray(reg)).to.be(false); - }); - - it('clones to an object', function () { - expect(_.isObject(_.clone(reg))).to.be(true); - expect(Array.isArray(_.clone(reg))).to.be(false); - }); - }); - - describe('Indexing', function () { - it('provides the initial set', function () { - const reg = new IndexedArray({ - initialSet: [1, 2, 3], - }); - - expect(reg).to.have.length(3); - - reg.forEach(function (v, i) { - expect(v).to.eql(i + 1); - }); - }); - - it('indexes the initial set', function () { - const reg = new IndexedArray({ - index: ['username'], - initialSet: users, - }); - - expect(reg).to.have.property('byUsername'); - expect(reg.byUsername).to.eql(users.byUsername); - }); - - it('updates indices after values are added', function () { - // split up the user list, and add it in chunks - const firstUser = users.slice(0, 1).pop(); - const otherUsers = users.slice(1); - - // start off with all but the first - const reg = new IndexedArray({ - group: ['group'], - order: ['id'], - initialSet: otherUsers, - }); - - // add the first - reg.push(firstUser); - - // end up with the same structure that is in the users fixture - expect(Object.keys(reg.byGroup).length).to.be(Object.keys(users.byGroup).length); - for (const group of Object.keys(reg.byGroup)) { - expect(reg.byGroup[group].toJSON()).to.eql(users.byGroup[group]); - } - - expect(reg.inIdOrder).to.eql(users.inIdOrder); - }); - - it('updates indices after values are removed', function () { - // start off with all - const reg = new IndexedArray({ - group: ['group'], - order: ['id'], - initialSet: users, - }); - - // remove the last - reg.pop(); - - const expectedCount = users.length - 1; - // indexed lists should be updated - expect(reg).to.have.length(expectedCount); - - const sumOfGroups = _.reduce( - reg.byGroup, - function (note, group) { - return note + group.length; - }, - 0 - ); - expect(sumOfGroups).to.eql(expectedCount); - }); - - it('removes items based on a predicate', function () { - const reg = new IndexedArray({ - group: ['group'], - order: ['id'], - initialSet: users, - }); - - reg.remove({ name: 'John' }); - - expect(_.isEqual(reg.raw, reg.slice(0))).to.be(true); - expect(reg.length).to.be(3); - expect(reg[0].name).to.be('Anon'); - }); - - it('updates indices after values are re-ordered', function () { - const rawUsers = users.slice(0); - - // collect and shuffle the ids available - let ids = []; - _.times(rawUsers.length, function (i) { - ids.push(i); - }); - ids = _.shuffle(ids); - - // move something here - const toI = ids.shift(); - // from here - const fromI = ids.shift(); - // do the move - const move = function (arr) { - arr.splice(toI, 0, arr.splice(fromI, 1)[0]); - }; - - const reg = new IndexedArray({ - index: ['username'], - initialSet: rawUsers, - }); - - const index = reg.byUsername; - - move(reg); - - expect(reg.byUsername).to.eql(index); - expect(reg.byUsername).to.not.be(index); - }); - }); - - describe('Ordering', function () { - it('ordering is case insensitive', function () { - const reg = new IndexedArray({ - index: ['title'], - order: ['title'], - initialSet: [{ title: 'APM' }, { title: 'Advanced Settings' }], - }); - - const ordered = reg.inTitleOrder; - expect(ordered[0].title).to.be('Advanced Settings'); - expect(ordered[1].title).to.be('APM'); - }); - - it('ordering handles numbers', function () { - const reg = new IndexedArray({ - index: ['id'], - order: ['id'], - initialSet: users, - }); - - const ordered = reg.inIdOrder; - expect(ordered[0].id).to.be(0); - expect(ordered[1].id).to.be(6); - expect(ordered[2].id).to.be(42); - expect(ordered[3].id).to.be(55); - }); - }); -}); diff --git a/src/legacy/ui/public/indexed_array/__tests__/inflector.js b/src/legacy/ui/public/indexed_array/__tests__/inflector.js deleted file mode 100644 index 49ac79094e501..0000000000000 --- a/src/legacy/ui/public/indexed_array/__tests__/inflector.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { inflector } from '../inflector'; -import expect from '@kbn/expect'; - -describe('IndexedArray Inflector', function () { - it('returns a function', function () { - const getter = inflector(); - expect(getter).to.be.a('function'); - }); - - describe('fn', function () { - it('prepends a prefix', function () { - const inflect = inflector('my'); - - expect(inflect('Family')).to.be('myFamily'); - expect(inflect('family')).to.be('myFamily'); - expect(inflect('fAmIlY')).to.be('myFAmIlY'); - }); - - it('adds both a prefix and suffix', function () { - const inflect = inflector('foo', 'Bar'); - - expect(inflect('box')).to.be('fooBoxBar'); - expect(inflect('box.car.MAX')).to.be('fooBoxCarMaxBar'); - expect(inflect('BaZzY')).to.be('fooBaZzYBar'); - }); - - it('ignores prefix if it is already at the end of the inflected string', function () { - const inflect = inflector('foo', 'Bar'); - expect(inflect('fooBox')).to.be('fooBoxBar'); - expect(inflect('FooBox')).to.be('FooBoxBar'); - }); - - it('ignores postfix if it is already at the end of the inflected string', function () { - const inflect = inflector('foo', 'Bar'); - expect(inflect('bar')).to.be('fooBar'); - expect(inflect('showBoxBar')).to.be('fooShowBoxBar'); - }); - - it('works with "name"', function () { - const inflect = inflector('in', 'Order'); - expect(inflect('name')).to.be('inNameOrder'); - }); - }); -}); diff --git a/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts b/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts deleted file mode 100644 index fc4ca8469382a..0000000000000 --- a/src/legacy/ui/public/indexed_array/helpers/organize_by.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { groupBy } from 'lodash'; -import { organizeBy } from './organize_by'; - -describe('organizeBy', () => { - test('it works', () => { - const col = [ - { - name: 'one', - roles: ['user', 'admin', 'owner'], - }, - { - name: 'two', - roles: ['user'], - }, - { - name: 'three', - roles: ['user'], - }, - { - name: 'four', - roles: ['user', 'admin'], - }, - ]; - - const resp = organizeBy(col, 'roles'); - expect(resp).toHaveProperty('user'); - expect(resp.user.length).toBe(4); - - expect(resp).toHaveProperty('admin'); - expect(resp.admin.length).toBe(2); - - expect(resp).toHaveProperty('owner'); - expect(resp.owner.length).toBe(1); - }); - - test('behaves just like groupBy in normal scenarios', () => { - const col = [{ name: 'one' }, { name: 'two' }, { name: 'three' }, { name: 'four' }]; - - const orgs = organizeBy(col, 'name'); - const groups = groupBy(col, 'name'); - - expect(orgs).toEqual(groups); - }); -}); diff --git a/src/legacy/ui/public/indexed_array/helpers/organize_by.ts b/src/legacy/ui/public/indexed_array/helpers/organize_by.ts deleted file mode 100644 index e923767c892cd..0000000000000 --- a/src/legacy/ui/public/indexed_array/helpers/organize_by.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { each, isFunction } from 'lodash'; - -/** - * Like _.groupBy, but allows specifying multiple groups for a - * single object. - * - * organizeBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') - * // Object {1: Array[2], 2: Array[1], 3: Array[1], 4: Array[1]} - * - * _.groupBy([{ a: [1, 2, 3] }, { b: true, a: [1, 4] }], 'a') - * // Object {'1,2,3': Array[1], '1,4': Array[1]} - * - * @param {array} collection - the list of values to organize - * @param {Function} callback - either a property name, or a callback. - * @return {object} - */ -export function organizeBy(collection: object[], callback: ((obj: object) => string) | string) { - const buckets: { [key: string]: object[] } = {}; - - function add(key: string, obj: object) { - if (!buckets[key]) { - buckets[key] = []; - } - buckets[key].push(obj); - } - - each(collection, (obj: Record) => { - const keys = isFunction(callback) ? callback(obj) : obj[callback]; - - if (!Array.isArray(keys)) { - add(keys, obj); - return; - } - - let length = keys.length; - while (length-- > 0) { - add(keys[length], obj); - } - }); - - return buckets; -} diff --git a/src/legacy/ui/public/indexed_array/index.d.ts b/src/legacy/ui/public/indexed_array/index.d.ts deleted file mode 100644 index 21c0a818731ac..0000000000000 --- a/src/legacy/ui/public/indexed_array/index.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { ListIterator } from 'lodash'; - -interface IndexedArrayConfig { - index?: string[]; - group?: string[]; - order?: string[]; - initialSet?: T[]; - immutable?: boolean; -} - -declare class IndexedArray extends Array { - public immutable: boolean; - public raw: T[]; - - // These may not actually be present, as they are dynamically defined - public inOrder: T[]; - public byType: Record; - public byName: Record; - - constructor(config: IndexedArrayConfig); - - public remove(predicate: ListIterator): T[]; - - public toJSON(): T[]; -} diff --git a/src/legacy/ui/public/indexed_array/index.js b/src/legacy/ui/public/indexed_array/index.js deleted file mode 100644 index 6a42961c9e680..0000000000000 --- a/src/legacy/ui/public/indexed_array/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { IndexedArray } from './indexed_array'; diff --git a/src/legacy/ui/public/indexed_array/indexed_array.js b/src/legacy/ui/public/indexed_array/indexed_array.js deleted file mode 100644 index b9a427b8da7ad..0000000000000 --- a/src/legacy/ui/public/indexed_array/indexed_array.js +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { inflector } from './inflector'; -import { organizeBy } from './helpers/organize_by'; - -const pathGetter = _(_.get).rearg(1, 0).ary(2); -const inflectIndex = inflector('by'); -const inflectOrder = inflector('in', 'Order'); - -const CLEAR_CACHE = {}; -const OPT_NAMES = ['index', 'group', 'order', 'initialSet', 'immutable']; - -/** - * Generic extension of Array class, which will index (and reindex) the - * objects it contains based on their properties. - * - * @param {Object} config describes the properties of this registry object - * @param {Array} [config.index] a list of props/paths that should be used to index the docs. - * @param {Array} [config.group] a list of keys/paths to group docs by. - * @param {Array} [config.order] a list of keys/paths to order the keys by. - * @param {Array} [config.initialSet] the initial dataset the IndexedArray should contain. - * @param {boolean} [config.immutable] a flag that hints to people reading the implementation that this IndexedArray - * should not be modified - */ - -export class IndexedArray { - static OPT_NAMES = OPT_NAMES; - - constructor(config) { - config = _.pick(config || {}, OPT_NAMES); - - // use defineProperty so that value can't be changed - Object.defineProperty(this, 'raw', { value: [] }); - - this._indexNames = _.union( - this._setupIndex(config.group, inflectIndex, organizeByIndexedArray(config)), - this._setupIndex(config.index, inflectIndex, _.keyBy), - this._setupIndex(config.order, inflectOrder, (raw, pluckValue) => { - return [...raw].sort((itemA, itemB) => { - const a = pluckValue(itemA); - const b = pluckValue(itemB); - if (typeof a === 'number' && typeof b === 'number') { - return a - b; - } - return String(a).toLowerCase().localeCompare(String(b).toLowerCase()); - }); - }) - ); - - if (config.initialSet) { - this.push.apply(this, config.initialSet); - } - - Object.defineProperty(this, 'immutable', { value: !!config.immutable }); - } - - /** - * Remove items from this based on a predicate - * @param {Function|Object|string} predicate - the predicate used to decide what is removed - * @return {array} - the removed data - */ - remove(predicate) { - this._assertMutable('remove'); - const out = _.remove(this, predicate); - _.remove(this.raw, predicate); - this._clearIndices(); - return out; - } - - /** - * provide a hook for the JSON serializer - * @return {array} - a plain, vanilla array with our same data - */ - toJSON() { - return this.raw; - } - - // wrappers for mutable Array methods - copyWithin(...args) { - return this._mutation('copyWithin', args); - } - fill(...args) { - return this._mutation('fill', args); - } - pop(...args) { - return this._mutation('pop', args); - } - push(...args) { - return this._mutation('push', args); - } - reverse(...args) { - return this._mutation('reverse', args); - } - shift(...args) { - return this._mutation('shift', args); - } - sort(...args) { - return this._mutation('sort', args); - } - splice(...args) { - return this._mutation('splice', args); - } - unshift(...args) { - return this._mutation('unshift', args); - } - - /** - * If this instance of IndexedArray is not mutable, throw an error - * @private - * @param {String} methodName - user facing method name, for error message - * @return {undefined} - */ - _assertMutable(methodName) { - if (this.immutable) { - throw new Error(`${methodName}() is not allowed on immutable IndexedArray instances`); - } - } - - /** - * Execute some mutable method from the Array prototype - * on the IndexedArray and this.raw - * - * @private - * @param {string} methodName - * @param {Array} args - * @return {any} - */ - _mutation(methodName, args) { - this._assertMutable(methodName); - super[methodName].apply(this, args); - this._clearIndices(); - return super[methodName].apply(this.raw, args); - } - - /** - * Create indices for a group of object properties. getters and setters are used to - * read and control the indices. - * @private - * @param {string[]} props - the properties that should be used to index docs - * @param {function} inflect - a function that will be called with a property name, and - * creates the public property at which the index will be exposed - * @param {function} op - the function that will be used to create the indices, it is passed - * the raw representation of the registry, and a getter for reading the - * right prop - * - * @returns {string[]} - the public keys of all indices created - */ - _setupIndex(props, inflect, op) { - // shortcut for empty props - if (!props || props.length === 0) return; - - return props.map((prop) => { - const indexName = inflect(prop); - const getIndexValueFromItem = pathGetter.partial(prop).value(); - let cache; - - Object.defineProperty(this, indexName, { - enumerable: false, - configurable: false, - - set: (val) => { - // can't set any value other than the CLEAR_CACHE constant - if (val === CLEAR_CACHE) { - cache = false; - } else { - throw new TypeError(indexName + ' can not be set, it is a computed index of values'); - } - }, - get: () => { - if (!cache) { - cache = op(this.raw, getIndexValueFromItem); - } - - return cache; - }, - }); - - return indexName; - }); - } - - /** - * Clear cached index/group/order caches so they will be recreated - * on next access - * @private - * @return {undefined} - */ - _clearIndices() { - this._indexNames.forEach((name) => { - this[name] = CLEAR_CACHE; - }); - } -} - -// using traditional `extends Array` syntax doesn't work with babel -// See https://babeljs.io/docs/usage/caveats/ -Object.setPrototypeOf(IndexedArray.prototype, Array.prototype); - -// Similar to `organizeBy` but returns IndexedArrays instead of normal Arrays. -function organizeByIndexedArray(config) { - return (...args) => { - const grouped = organizeBy(...args); - - return _.reduce( - grouped, - (acc, value, group) => { - acc[group] = new IndexedArray({ - ...config, - initialSet: value, - }); - - return acc; - }, - {} - ); - }; -} diff --git a/src/legacy/ui/public/indexed_array/inflector.js b/src/legacy/ui/public/indexed_array/inflector.js deleted file mode 100644 index e034146f5f62f..0000000000000 --- a/src/legacy/ui/public/indexed_array/inflector.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -function upFirst(str, total) { - return str.charAt(0).toUpperCase() + (total ? str.substr(1).toLowerCase() : str.substr(1)); -} - -function startsWith(str, test) { - return str.substr(0, test.length).toLowerCase() === test.toLowerCase(); -} - -function endsWith(str, test) { - const tooShort = str.length < test.length; - if (tooShort) return; - - return str.substr(str.length - test.length).toLowerCase() === test.toLowerCase(); -} - -export function inflector(prefix, postfix) { - return function inflect(key) { - let inflected; - - if (key.indexOf('.') !== -1) { - inflected = key - .split('.') - .map(function (step, i) { - return i === 0 ? step : upFirst(step, true); - }) - .join(''); - } else { - inflected = key; - } - - if (prefix && !startsWith(key, prefix)) { - inflected = prefix + upFirst(inflected); - } - - if (postfix && !endsWith(key, postfix)) { - inflected = inflected + postfix; - } - - return inflected; - }; -} diff --git a/src/legacy/ui/public/kfetch/__mocks__/index.ts b/src/legacy/ui/public/kfetch/__mocks__/index.ts deleted file mode 100644 index 1a128e2b85260..0000000000000 --- a/src/legacy/ui/public/kfetch/__mocks__/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export const kfetch = () => Promise.resolve(); diff --git a/src/legacy/ui/public/kfetch/_import_objects.ndjson b/src/legacy/ui/public/kfetch/_import_objects.ndjson deleted file mode 100644 index 3511fb44cdfb2..0000000000000 --- a/src/legacy/ui/public/kfetch/_import_objects.ndjson +++ /dev/null @@ -1 +0,0 @@ -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Log Agents","uiStateJSON":"{}","visState":"{\"title\":\"Log Agents\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{\"text\":\"agent.raw: Descending\"}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"agent.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}"},"id":"082f1d60-a2e7-11e7-bb30-233be9be6a15","migrationVersion":{"visualization":"7.0.0"},"references":[{"id":"f1e4c910-a2e6-11e7-bb30-233be9be6a15","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","version":1} diff --git a/src/legacy/ui/public/kfetch/index.ts b/src/legacy/ui/public/kfetch/index.ts deleted file mode 100644 index 105df171ad370..0000000000000 --- a/src/legacy/ui/public/kfetch/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { npSetup } from 'ui/new_platform'; -import { createKfetch, KFetchKibanaOptions, KFetchOptions } from './kfetch'; -export { addInterceptor, KFetchOptions, KFetchQuery } from './kfetch'; - -const kfetchInstance = createKfetch(npSetup.core.http); - -export const kfetch = (options: KFetchOptions, kfetchOptions?: KFetchKibanaOptions) => { - return kfetchInstance(options, kfetchOptions); -}; diff --git a/src/legacy/ui/public/kfetch/kfetch.test.mocks.ts b/src/legacy/ui/public/kfetch/kfetch.test.mocks.ts deleted file mode 100644 index ea066b3623f13..0000000000000 --- a/src/legacy/ui/public/kfetch/kfetch.test.mocks.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { setup } from '../../../../test_utils/public/http_test_setup'; - -jest.doMock('ui/new_platform', () => ({ - npSetup: { - core: setup(), - }, -})); diff --git a/src/legacy/ui/public/kfetch/kfetch.test.ts b/src/legacy/ui/public/kfetch/kfetch.test.ts deleted file mode 100644 index c45a142d54e9b..0000000000000 --- a/src/legacy/ui/public/kfetch/kfetch.test.ts +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// @ts-ignore -import fetchMock from 'fetch-mock/es5/client'; -import './kfetch.test.mocks'; -import { readFileSync } from 'fs'; -import { join } from 'path'; -import { addInterceptor, kfetch, KFetchOptions } from '.'; -import { Interceptor, resetInterceptors, withDefaultOptions } from './kfetch'; -import { KFetchError } from './kfetch_error'; - -describe('kfetch', () => { - afterEach(() => { - fetchMock.restore(); - resetInterceptors(); - }); - - it('should use supplied request method', async () => { - fetchMock.post('*', {}); - await kfetch({ pathname: '/my/path', method: 'POST' }); - expect(fetchMock.lastOptions()!.method).toBe('POST'); - }); - - it('should use supplied Content-Type', async () => { - fetchMock.get('*', {}); - await kfetch({ pathname: '/my/path', headers: { 'Content-Type': 'CustomContentType' } }); - expect(fetchMock.lastOptions()!.headers).toMatchObject({ - 'content-type': 'CustomContentType', - }); - }); - - it('should use supplied pathname and querystring', async () => { - fetchMock.get('*', {}); - await kfetch({ pathname: '/my/path', query: { a: 'b' } }); - expect(fetchMock.lastUrl()).toBe('http://localhost/myBase/my/path?a=b'); - }); - - it('should use supplied headers', async () => { - fetchMock.get('*', {}); - await kfetch({ - pathname: '/my/path', - headers: { myHeader: 'foo' }, - }); - - expect(fetchMock.lastOptions()!.headers).toEqual({ - 'content-type': 'application/json', - 'kbn-version': 'kibanaVersion', - myheader: 'foo', - }); - }); - - it('should return response', async () => { - fetchMock.get('*', { foo: 'bar' }); - const res = await kfetch({ pathname: '/my/path' }); - expect(res).toEqual({ foo: 'bar' }); - }); - - it('should prepend url with basepath by default', async () => { - fetchMock.get('*', {}); - await kfetch({ pathname: '/my/path' }); - expect(fetchMock.lastUrl()).toBe('http://localhost/myBase/my/path'); - }); - - it('should not prepend url with basepath when disabled', async () => { - fetchMock.get('*', {}); - await kfetch({ pathname: '/my/path' }, { prependBasePath: false }); - expect(fetchMock.lastUrl()).toBe('/my/path'); - }); - - it('should make request with defaults', async () => { - fetchMock.get('*', {}); - await kfetch({ pathname: '/my/path' }); - - expect(fetchMock.lastCall()!.request.credentials).toBe('same-origin'); - expect(fetchMock.lastOptions()!).toMatchObject({ - method: 'GET', - headers: { - 'content-type': 'application/json', - 'kbn-version': 'kibanaVersion', - }, - }); - }); - - it('should make requests for NDJSON content', async () => { - const content = readFileSync(join(__dirname, '_import_objects.ndjson'), { encoding: 'utf-8' }); - - fetchMock.post('*', { - body: content, - headers: { 'Content-Type': 'application/ndjson' }, - }); - - const data = await kfetch({ - method: 'POST', - pathname: '/my/path', - body: content, - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); - - expect(data).toBeInstanceOf(Blob); - - const ndjson = await new Response(data).text(); - - expect(ndjson).toEqual(content); - }); - - it('should reject on network error', async () => { - expect.assertions(1); - fetchMock.get('*', { status: 500 }); - - try { - await kfetch({ pathname: '/my/path' }); - } catch (e) { - expect(e.message).toBe('Internal Server Error'); - } - }); - - describe('when throwing response error (KFetchError)', () => { - let error: KFetchError; - beforeEach(async () => { - fetchMock.get('*', { status: 404, body: { foo: 'bar' } }); - try { - await kfetch({ pathname: '/my/path' }); - } catch (e) { - error = e; - } - }); - - it('should contain error message', () => { - expect(error.message).toBe('Not Found'); - }); - - it('should return response body', () => { - expect(error.body).toEqual({ foo: 'bar' }); - }); - - it('should contain response properties', () => { - expect(error.res.status).toBe(404); - expect(error.res.url).toBe('http://localhost/myBase/my/path'); - }); - }); - - describe('when all interceptor resolves', () => { - let resp: any; - let interceptorCalls: string[]; - - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - - interceptorCalls = mockInterceptorCalls([{}, {}, {}]); - resp = await kfetch({ pathname: '/my/path' }); - }); - - it('should call interceptors in correct order', () => { - expect(interceptorCalls).toEqual([ - 'Request #3', - 'Request #2', - 'Request #1', - 'Response #1', - 'Response #2', - 'Response #3', - ]); - }); - - it('should make request', () => { - expect(fetchMock.called()).toBe(true); - }); - - it('should return response', () => { - expect(resp).toEqual({ foo: 'bar' }); - }); - }); - - describe('when a request interceptor throws; and the next requestError interceptor resolves', () => { - let resp: any; - let interceptorCalls: string[]; - - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - - interceptorCalls = mockInterceptorCalls([ - { requestError: () => ({ pathname: '/my/path' } as KFetchOptions) }, - { request: () => Promise.reject(new Error('Error in request')) }, - {}, - ]); - - resp = await kfetch({ pathname: '/my/path' }); - }); - - it('should call interceptors in correct order', () => { - expect(interceptorCalls).toEqual([ - 'Request #3', - 'Request #2', - 'RequestError #1', - 'Response #1', - 'Response #2', - 'Response #3', - ]); - }); - - it('should make request', () => { - expect(fetchMock.called()).toBe(true); - }); - - it('should return response', () => { - expect(resp).toEqual({ foo: 'bar' }); - }); - }); - - describe('when a request interceptor throws', () => { - let error: Error; - let interceptorCalls: string[]; - - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - - interceptorCalls = mockInterceptorCalls([ - {}, - { request: () => Promise.reject(new Error('Error in request')) }, - {}, - ]); - - try { - await kfetch({ pathname: '/my/path' }); - } catch (e) { - error = e; - } - }); - - it('should call interceptors in correct order', () => { - expect(interceptorCalls).toEqual([ - 'Request #3', - 'Request #2', - 'RequestError #1', - 'ResponseError #1', - 'ResponseError #2', - 'ResponseError #3', - ]); - }); - - it('should not make request', () => { - expect(fetchMock.called()).toBe(false); - }); - - it('should throw error', () => { - expect(error.message).toEqual('Error in request'); - }); - }); - - describe('when a response interceptor throws', () => { - let error: Error; - let interceptorCalls: string[]; - - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - - interceptorCalls = mockInterceptorCalls([ - { response: () => Promise.reject(new Error('Error in response')) }, - {}, - {}, - ]); - - try { - await kfetch({ pathname: '/my/path' }); - } catch (e) { - error = e; - } - }); - - it('should call in correct order', () => { - expect(interceptorCalls).toEqual([ - 'Request #3', - 'Request #2', - 'Request #1', - 'Response #1', - 'ResponseError #2', - 'ResponseError #3', - ]); - }); - - it('should make request', () => { - expect(fetchMock.called()).toBe(true); - }); - - it('should throw error', () => { - expect(error.message).toEqual('Error in response'); - }); - }); - - describe('when request interceptor throws; and a responseError interceptor resolves', () => { - let resp: any; - let interceptorCalls: string[]; - - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - - interceptorCalls = mockInterceptorCalls([ - {}, - { - request: () => { - throw new Error('My request error'); - }, - responseError: () => { - return { custom: 'response' }; - }, - }, - {}, - ]); - - resp = await kfetch({ pathname: '/my/path' }); - }); - - it('should call in correct order', () => { - expect(interceptorCalls).toEqual([ - 'Request #3', - 'Request #2', - 'RequestError #1', - 'ResponseError #1', - 'ResponseError #2', - 'Response #3', - ]); - }); - - it('should not make request', () => { - expect(fetchMock.called()).toBe(false); - }); - - it('should resolve', () => { - expect(resp).toEqual({ custom: 'response' }); - }); - }); - - describe('when interceptors return synchronously', () => { - let resp: any; - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - addInterceptor({ - request: (config) => ({ - ...config, - pathname: '/my/intercepted-route', - }), - response: (res) => ({ - ...res, - addedByResponseInterceptor: true, - }), - }); - - resp = await kfetch({ pathname: '/my/path' }); - }); - - it('should modify request', () => { - expect(fetchMock.lastUrl()).toContain('/my/intercepted-route'); - expect(fetchMock.lastOptions()!).toMatchObject({ - method: 'GET', - }); - }); - - it('should modify response', () => { - expect(resp).toEqual({ - addedByResponseInterceptor: true, - foo: 'bar', - }); - }); - }); - - describe('when interceptors return promise', () => { - let resp: any; - beforeEach(async () => { - fetchMock.get('*', { foo: 'bar' }); - addInterceptor({ - request: (config) => - Promise.resolve({ - ...config, - pathname: '/my/intercepted-route', - }), - response: (res) => - Promise.resolve({ - ...res, - addedByResponseInterceptor: true, - }), - }); - - resp = await kfetch({ pathname: '/my/path' }); - }); - - it('should modify request', () => { - expect(fetchMock.lastUrl()).toContain('/my/intercepted-route'); - expect(fetchMock.lastOptions()!).toMatchObject({ - method: 'GET', - }); - }); - - it('should modify response', () => { - expect(resp).toEqual({ - addedByResponseInterceptor: true, - foo: 'bar', - }); - }); - }); -}); - -function mockInterceptorCalls(interceptors: Interceptor[]) { - const interceptorCalls: string[] = []; - interceptors.forEach((interceptor, i) => { - addInterceptor({ - request: (config) => { - interceptorCalls.push(`Request #${i + 1}`); - - if (interceptor.request) { - return interceptor.request(config); - } - - return config; - }, - requestError: (e) => { - interceptorCalls.push(`RequestError #${i + 1}`); - if (interceptor.requestError) { - return interceptor.requestError(e); - } - - throw e; - }, - response: (res) => { - interceptorCalls.push(`Response #${i + 1}`); - - if (interceptor.response) { - return interceptor.response(res); - } - - return res; - }, - responseError: (e) => { - interceptorCalls.push(`ResponseError #${i + 1}`); - - if (interceptor.responseError) { - return interceptor.responseError(e); - } - - throw e; - }, - }); - }); - - return interceptorCalls; -} - -describe('withDefaultOptions', () => { - it('should remove undefined query params', () => { - const { query } = withDefaultOptions({ - pathname: '/withDefaultOptions', - query: { - foo: 'bar', - param1: (undefined as any) as string, - param2: (null as any) as string, - param3: '', - }, - }); - expect(query).toEqual({ foo: 'bar', param2: null, param3: '' }); - }); - - it('should add default options', () => { - expect(withDefaultOptions({ pathname: '/addDefaultOptions' })).toEqual({ - pathname: '/addDefaultOptions', - credentials: 'same-origin', - headers: { 'Content-Type': 'application/json' }, - method: 'GET', - }); - }); -}); diff --git a/src/legacy/ui/public/kfetch/kfetch.ts b/src/legacy/ui/public/kfetch/kfetch.ts deleted file mode 100644 index 4eb7149931575..0000000000000 --- a/src/legacy/ui/public/kfetch/kfetch.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { merge } from 'lodash'; -// @ts-ignore not really worth typing -import { KFetchError } from './kfetch_error'; - -import { HttpSetup } from '../../../../core/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { HttpRequestInit } from '../../../../core/public/http/types'; - -export interface KFetchQuery { - [key: string]: string | number | boolean | undefined; -} - -export interface KFetchOptions extends HttpRequestInit { - pathname: string; - query?: KFetchQuery; - asSystemRequest?: boolean; -} - -export interface KFetchKibanaOptions { - prependBasePath?: boolean; -} - -export interface Interceptor { - request?: (config: KFetchOptions) => Promise | KFetchOptions; - requestError?: (e: any) => Promise | KFetchOptions; - response?: (res: any) => any; - responseError?: (e: any) => any; -} - -const interceptors: Interceptor[] = []; -export const resetInterceptors = () => (interceptors.length = 0); -export const addInterceptor = (interceptor: Interceptor) => interceptors.push(interceptor); - -export function createKfetch(http: HttpSetup) { - return function kfetch( - options: KFetchOptions, - { prependBasePath = true }: KFetchKibanaOptions = {} - ) { - return responseInterceptors( - requestInterceptors(withDefaultOptions(options)) - .then(({ pathname, ...restOptions }) => - http.fetch(pathname, { ...restOptions, prependBasePath }) - ) - .catch((err) => { - throw new KFetchError(err.response || { statusText: err.message }, err.body); - }) - ); - }; -} - -// Request/response interceptors are called in opposite orders. -// Request hooks start from the newest interceptor and end with the oldest. -function requestInterceptors(config: KFetchOptions): Promise { - return interceptors.reduceRight((acc, interceptor) => { - return acc.then(interceptor.request, interceptor.requestError); - }, Promise.resolve(config)); -} - -// Response hooks start from the oldest interceptor and end with the newest. -function responseInterceptors(responsePromise: Promise) { - return interceptors.reduce((acc, interceptor) => { - return acc.then(interceptor.response, interceptor.responseError); - }, responsePromise); -} - -export function withDefaultOptions(options?: KFetchOptions): KFetchOptions { - const withDefaults = merge( - { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - }, - }, - options - ) as KFetchOptions; - - if ( - options && - options.headers && - 'Content-Type' in options.headers && - options.headers['Content-Type'] === undefined - ) { - // TS thinks headers could be undefined here, but that isn't possible because - // of the merge above. - // @ts-ignore - withDefaults.headers['Content-Type'] = undefined; - } - - return withDefaults; -} diff --git a/src/legacy/ui/public/kfetch/kfetch_error.ts b/src/legacy/ui/public/kfetch/kfetch_error.ts deleted file mode 100644 index f351959e624b8..0000000000000 --- a/src/legacy/ui/public/kfetch/kfetch_error.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export class KFetchError extends Error { - constructor(public readonly res: Response, public readonly body?: any) { - super(res.statusText); - - // captureStackTrace is only available in the V8 engine, so any browser using - // a different JS engine won't have access to this method. - if (Error.captureStackTrace) { - Error.captureStackTrace(this, KFetchError); - } - } -} diff --git a/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js b/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js deleted file mode 100644 index efcfb77997265..0000000000000 --- a/src/legacy/ui/public/legacy_compat/__tests__/xsrf.js +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import $ from 'jquery'; -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import ngMock from 'ng_mock'; - -import { $setupXsrfRequestInterceptor } from '../../../../../plugins/kibana_legacy/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { version } from '../../../../../core/server/utils/package_json'; - -const xsrfHeader = 'kbn-version'; - -describe('chrome xsrf apis', function () { - const sandbox = sinon.createSandbox(); - - afterEach(function () { - sandbox.restore(); - }); - - describe('jQuery support', function () { - it('adds a global jQuery prefilter', function () { - sandbox.stub($, 'ajaxPrefilter'); - $setupXsrfRequestInterceptor(version); - expect($.ajaxPrefilter.callCount).to.be(1); - }); - - describe('jQuery prefilter', function () { - let prefilter; - - beforeEach(function () { - sandbox.stub($, 'ajaxPrefilter'); - $setupXsrfRequestInterceptor(version); - prefilter = $.ajaxPrefilter.args[0][0]; - }); - - it(`sets the ${xsrfHeader} header`, function () { - const setHeader = sinon.stub(); - prefilter({}, {}, { setRequestHeader: setHeader }); - - expect(setHeader.callCount).to.be(1); - expect(setHeader.args[0]).to.eql([xsrfHeader, version]); - }); - - it('can be canceled by setting the kbnXsrfToken option', function () { - const setHeader = sinon.stub(); - prefilter({ kbnXsrfToken: false }, {}, { setRequestHeader: setHeader }); - expect(setHeader.callCount).to.be(0); - }); - }); - - describe('Angular support', function () { - let $http; - let $httpBackend; - - beforeEach(function () { - sandbox.stub($, 'ajaxPrefilter'); - ngMock.module($setupXsrfRequestInterceptor(version)); - }); - - beforeEach( - ngMock.inject(function ($injector) { - $http = $injector.get('$http'); - $httpBackend = $injector.get('$httpBackend'); - - $httpBackend.when('POST', '/api/test').respond('ok'); - }) - ); - - afterEach(function () { - $httpBackend.verifyNoOutstandingExpectation(); - $httpBackend.verifyNoOutstandingRequest(); - }); - - it(`injects a ${xsrfHeader} header on every request`, function () { - $httpBackend - .expectPOST('/api/test', undefined, function (headers) { - return headers[xsrfHeader] === version; - }) - .respond(200, ''); - - $http.post('/api/test'); - $httpBackend.flush(); - }); - - it('skips requests with the kbnXsrfToken set falsy', function () { - $httpBackend - .expectPOST('/api/test', undefined, function (headers) { - return !(xsrfHeader in headers); - }) - .respond(200, ''); - - $http({ - method: 'POST', - url: '/api/test', - kbnXsrfToken: 0, - }); - - $http({ - method: 'POST', - url: '/api/test', - kbnXsrfToken: '', - }); - - $http({ - method: 'POST', - url: '/api/test', - kbnXsrfToken: false, - }); - - $httpBackend.flush(); - }); - - it('treats the kbnXsrfToken option as boolean-y', function () { - const customToken = `custom:${version}`; - $httpBackend - .expectPOST('/api/test', undefined, function (headers) { - return headers[xsrfHeader] === version; - }) - .respond(200, ''); - - $http({ - method: 'POST', - url: '/api/test', - kbnXsrfToken: customToken, - }); - - $httpBackend.flush(); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/legacy_compat/index.ts b/src/legacy/ui/public/legacy_compat/index.ts deleted file mode 100644 index 2067fa6489304..0000000000000 --- a/src/legacy/ui/public/legacy_compat/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { configureAppAngularModule } from '../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/ui/public/metadata.ts b/src/legacy/ui/public/metadata.ts deleted file mode 100644 index fade0f0d8629a..0000000000000 --- a/src/legacy/ui/public/metadata.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { npSetup } from 'ui/new_platform'; - -export const metadata: { - branch: string; - version: string; -} = npSetup.core.injectedMetadata.getLegacyMetadata(); diff --git a/src/legacy/ui/public/modules.js b/src/legacy/ui/public/modules.js deleted file mode 100644 index bb1c8aead1c34..0000000000000 --- a/src/legacy/ui/public/modules.js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import _ from 'lodash'; -/** - * This module is used by Kibana to create and reuse angular modules. Angular modules - * can only be created once and need to have their dependencies at creation. This is - * hard/impossible to do in require.js since all of the dependencies for a module are - * loaded before it is. - * - * Here is an example: - * - * In the scenario below, require.js would load directive.js first because it is a - * dependency of app.js. This would cause the call to `angular.module('app')` to - * execute before the module is actually created. This causes angular to throw an - * error. This effect is magnified when app.js links off to many different modules. - * - * This is normally solved by creating unique modules per file, listed as the 1st - * alternate solution below. Unfortunately this solution would have required that - * we replicate our require statements. - * - * app.js - * ``` - * angular.module('app', ['ui.bootstrap']) - * .controller('AppController', function () { ... }); - * - * require('./directive'); - * ``` - * - * directive.js - * ``` - * angular.module('app') - * .directive('someDirective', function () { ... }); - * ``` - * - * Before taking this approach we saw three possible solutions: - * 1. replicate our js modules in angular modules/use a different module per file - * 2. create a single module outside of our js modules and share it - * 3. use a helper lib to dynamically create modules as needed. - * - * We decided to go with #3 - * - * This ends up working by creating a list of modules that the code base creates by - * calling `modules.get(name)` with different names, and then before bootstrapping - * the application kibana uses `modules.link()` to set the dependencies of the "kibana" - * module to include every defined module. This guarantees that kibana can always find - * any angular dependency defined in the kibana code base. This **also** means that - * Private modules are able to find any dependency, since they are injected using the - * "kibana" module's injector. - * - */ -const existingModules = {}; -const links = []; - -/** - * Take an angular module and extends the dependencies for that module to include all of the modules - * created using `ui/modules` - * - * @param {AngularModule} module - the module to extend - * @return {undefined} - */ -export function link(module) { - // as modules are defined they will be set as requirements for this app - links.push(module); - - // merge in the existing modules - module.requires = _.union(module.requires, _.keys(existingModules)); -} - -/** - * The primary means of interacting with `ui/modules`. Returns an angular module. If the module already - * exists the existing version will be returned. `dependencies` are either set as or merged into the - * modules total dependencies. - * - * This is in contrast to the `angular.module(name, [dependencies])` function which will only - * create a module if the `dependencies` list is passed and get an existing module if no dependencies - * are passed. This requires knowing the order that your files will load, which we can't guarantee. - * - * @param {string} moduleName - the unique name for this module - * @param {array[string]} [requires=[]] - the other modules this module requires - * @return {AngularModule} - */ -export function get(moduleName, requires) { - let module = existingModules[moduleName]; - - if (module === void 0) { - // create the module - module = existingModules[moduleName] = angular.module(moduleName, []); - - module.close = _.partial(close, moduleName); - - // ensure that it is required by linked modules - _.each(links, function (app) { - if (!~app.requires.indexOf(moduleName)) app.requires.push(moduleName); - }); - } - - if (requires) { - // update requires list with possibly new requirements - module.requires = _.union(module.requires, requires); - } - - return module; -} - -export function close(moduleName) { - const module = existingModules[moduleName]; - - // already closed - if (!module) return; - - // if the module is currently linked, unlink it - const i = links.indexOf(module); - if (i > -1) links.splice(i, 1); - - // remove from linked modules list of required modules - _.each(links, function (app) { - _.pull(app.requires, moduleName); - }); - - // remove module from existingModules - delete existingModules[moduleName]; -} - -export const uiModules = { link, get, close }; diff --git a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts b/src/legacy/ui/public/new_platform/__mocks__/helpers.ts deleted file mode 100644 index 35aa8e4830428..0000000000000 --- a/src/legacy/ui/public/new_platform/__mocks__/helpers.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { coreMock } from '../../../../../core/public/mocks'; -import { dataPluginMock } from '../../../../../plugins/data/public/mocks'; -import { embeddablePluginMock } from '../../../../../plugins/embeddable/public/mocks'; -import { navigationPluginMock } from '../../../../../plugins/navigation/public/mocks'; -import { expressionsPluginMock } from '../../../../../plugins/expressions/public/mocks'; -import { inspectorPluginMock } from '../../../../../plugins/inspector/public/mocks'; -import { uiActionsPluginMock } from '../../../../../plugins/ui_actions/public/mocks'; -import { managementPluginMock } from '../../../../../plugins/management/public/mocks'; -import { usageCollectionPluginMock } from '../../../../../plugins/usage_collection/public/mocks'; -import { kibanaLegacyPluginMock } from '../../../../../plugins/kibana_legacy/public/mocks'; -import { chartPluginMock } from '../../../../../plugins/charts/public/mocks'; -import { advancedSettingsMock } from '../../../../../plugins/advanced_settings/public/mocks'; -import { savedObjectsManagementPluginMock } from '../../../../../plugins/saved_objects_management/public/mocks'; -import { visualizationsPluginMock } from '../../../../../plugins/visualizations/public/mocks'; -import { discoverPluginMock } from '../../../../../plugins/discover/public/mocks'; - -export const pluginsMock = { - createSetup: () => ({ - data: dataPluginMock.createSetupContract(), - charts: chartPluginMock.createSetupContract(), - navigation: navigationPluginMock.createSetupContract(), - embeddable: embeddablePluginMock.createSetupContract(), - inspector: inspectorPluginMock.createSetupContract(), - expressions: expressionsPluginMock.createSetupContract(), - uiActions: uiActionsPluginMock.createSetupContract(), - usageCollection: usageCollectionPluginMock.createSetupContract(), - advancedSettings: advancedSettingsMock.createSetupContract(), - visualizations: visualizationsPluginMock.createSetupContract(), - kibanaLegacy: kibanaLegacyPluginMock.createSetupContract(), - savedObjectsManagement: savedObjectsManagementPluginMock.createSetupContract(), - discover: discoverPluginMock.createSetupContract(), - }), - createStart: () => ({ - data: dataPluginMock.createStartContract(), - charts: chartPluginMock.createStartContract(), - navigation: navigationPluginMock.createStartContract(), - embeddable: embeddablePluginMock.createStartContract(), - inspector: inspectorPluginMock.createStartContract(), - expressions: expressionsPluginMock.createStartContract(), - uiActions: uiActionsPluginMock.createStartContract(), - management: managementPluginMock.createStartContract(), - advancedSettings: advancedSettingsMock.createStartContract(), - visualizations: visualizationsPluginMock.createStartContract(), - kibanaLegacy: kibanaLegacyPluginMock.createStartContract(), - savedObjectsManagement: savedObjectsManagementPluginMock.createStartContract(), - discover: discoverPluginMock.createStartContract(), - }), -}; - -export const createUiNewPlatformMock = () => { - const mock = { - npSetup: { - core: coreMock.createSetup(), - plugins: pluginsMock.createSetup(), - }, - npStart: { - core: coreMock.createStart(), - plugins: pluginsMock.createStart(), - }, - }; - return mock; -}; diff --git a/src/legacy/ui/public/new_platform/__mocks__/index.ts b/src/legacy/ui/public/new_platform/__mocks__/index.ts deleted file mode 100644 index d469960ddd7b7..0000000000000 --- a/src/legacy/ui/public/new_platform/__mocks__/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { createUiNewPlatformMock } from './helpers'; - -const { npSetup, npStart } = createUiNewPlatformMock(); -export { npSetup, npStart }; diff --git a/src/legacy/ui/public/new_platform/index.ts b/src/legacy/ui/public/new_platform/index.ts deleted file mode 100644 index ca5890854f3aa..0000000000000 --- a/src/legacy/ui/public/new_platform/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export { __setup__, __start__, npSetup, npStart } from './new_platform'; diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js deleted file mode 100644 index b8d48b784dba7..0000000000000 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import sinon from 'sinon'; -import { getFieldFormatsRegistry } from '../../../../test_utils/public/stub_field_formats'; -import { METRIC_TYPE } from '@kbn/analytics'; -import { setSetupServices, setStartServices } from './set_services'; -import { - AggTypesRegistry, - getAggTypes, - AggConfigs, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../src/plugins/data/common/search/aggs'; -import { ComponentRegistry } from '../../../../../src/plugins/advanced_settings/public/'; -import { UI_SETTINGS } from '../../../../../src/plugins/data/public/'; -import { - CSV_SEPARATOR_SETTING, - CSV_QUOTE_VALUES_SETTING, -} from '../../../../../src/plugins/share/public'; - -const mockObservable = () => { - return { - subscribe: () => {}, - pipe: () => { - return { - subscribe: () => {}, - }; - }, - }; -}; - -const mockComponent = () => { - return null; -}; - -let refreshInterval = undefined; -let isTimeRangeSelectorEnabled = true; -let isAutoRefreshSelectorEnabled = true; - -export const mockUiSettings = { - get: (item, defaultValue) => { - const defaultValues = { - dateFormat: 'MMM D, YYYY @ HH:mm:ss.SSS', - 'dateFormat:tz': 'UTC', - [UI_SETTINGS.SHORT_DOTS_ENABLE]: true, - [UI_SETTINGS.COURIER_IGNORE_FILTER_IF_FIELD_NOT_IN_INDEX]: true, - [UI_SETTINGS.QUERY_ALLOW_LEADING_WILDCARDS]: true, - [UI_SETTINGS.QUERY_STRING_OPTIONS]: {}, - [UI_SETTINGS.FORMAT_CURRENCY_DEFAULT_PATTERN]: '($0,0.[00])', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_PATTERN]: '0,0.[000]', - [UI_SETTINGS.FORMAT_PERCENT_DEFAULT_PATTERN]: '0,0.[000]%', - [UI_SETTINGS.FORMAT_NUMBER_DEFAULT_LOCALE]: 'en', - [UI_SETTINGS.FORMAT_DEFAULT_TYPE_MAP]: {}, - [CSV_SEPARATOR_SETTING]: ',', - [CSV_QUOTE_VALUES_SETTING]: true, - [UI_SETTINGS.SEARCH_QUERY_LANGUAGE]: 'kuery', - 'state:storeInSessionStorage': false, - }; - - return defaultValues[item] || defaultValue; - }, - getUpdate$: () => ({ - subscribe: sinon.fake(), - }), - isDefault: sinon.fake(), -}; - -const mockCoreSetup = { - chrome: {}, - http: { - basePath: { - get: sinon.fake.returns(''), - }, - }, - injectedMetadata: {}, - uiSettings: mockUiSettings, -}; - -const mockCoreStart = { - application: { - capabilities: {}, - }, - chrome: { - overlays: { - openModal: sinon.fake(), - }, - }, - http: { - basePath: { - get: sinon.fake.returns(''), - }, - }, - notifications: { - toasts: {}, - }, - i18n: {}, - overlays: {}, - savedObjects: { - client: {}, - }, - uiSettings: mockUiSettings, -}; - -const querySetup = { - state$: mockObservable(), - filterManager: { - getFetches$: sinon.fake(), - getFilters: sinon.fake(), - getAppFilters: sinon.fake(), - getGlobalFilters: sinon.fake(), - removeFilter: sinon.fake(), - addFilters: sinon.fake(), - setFilters: sinon.fake(), - removeAll: sinon.fake(), - getUpdates$: mockObservable, - }, - timefilter: { - timefilter: { - getFetch$: mockObservable, - getAutoRefreshFetch$: mockObservable, - getEnabledUpdated$: mockObservable, - getTimeUpdate$: mockObservable, - getRefreshIntervalUpdate$: mockObservable, - isTimeRangeSelectorEnabled: () => { - return isTimeRangeSelectorEnabled; - }, - isAutoRefreshSelectorEnabled: () => { - return isAutoRefreshSelectorEnabled; - }, - disableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = false; - }, - enableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = true; - }, - getRefreshInterval: () => { - return refreshInterval; - }, - setRefreshInterval: (interval) => { - refreshInterval = interval; - }, - enableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = true; - }, - disableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = false; - }, - getTime: sinon.fake(), - setTime: sinon.fake(), - getActiveBounds: sinon.fake(), - getBounds: sinon.fake(), - calculateBounds: sinon.fake(), - createFilter: sinon.fake(), - }, - history: sinon.fake(), - }, - savedQueries: { - saveQuery: sinon.fake(), - getAllSavedQueries: sinon.fake(), - findSavedQueries: sinon.fake(), - getSavedQuery: sinon.fake(), - deleteSavedQuery: sinon.fake(), - getSavedQueryCount: sinon.fake(), - }, -}; - -const mockAggTypesRegistry = () => { - const registry = new AggTypesRegistry(); - const registrySetup = registry.setup(); - const aggTypes = getAggTypes({ - calculateBounds: sinon.fake(), - getConfig: sinon.fake(), - getFieldFormatsStart: () => ({ - deserialize: sinon.fake(), - getDefaultInstance: sinon.fake(), - }), - isDefaultTimezone: () => true, - }); - aggTypes.buckets.forEach((type) => registrySetup.registerBucket(type)); - aggTypes.metrics.forEach((type) => registrySetup.registerMetric(type)); - - return registry; -}; - -const aggTypesRegistry = mockAggTypesRegistry(); - -export const npSetup = { - core: mockCoreSetup, - plugins: { - advancedSettings: { - component: { - register: sinon.fake(), - componentType: ComponentRegistry.componentType, - }, - }, - usageCollection: { - allowTrackUserAgent: sinon.fake(), - reportUiStats: sinon.fake(), - METRIC_TYPE, - }, - embeddable: { - registerEmbeddableFactory: sinon.fake(), - }, - expressions: { - registerFunction: sinon.fake(), - registerRenderer: sinon.fake(), - registerType: sinon.fake(), - }, - data: { - autocomplete: { - addProvider: sinon.fake(), - getProvider: sinon.fake(), - }, - query: querySetup, - search: { - aggs: { - types: aggTypesRegistry.setup(), - }, - __LEGACY: { - esClient: { - search: sinon.fake(), - msearch: sinon.fake(), - }, - }, - }, - fieldFormats: getFieldFormatsRegistry(mockCoreSetup), - }, - share: { - register: () => {}, - urlGenerators: { - registerUrlGenerator: () => {}, - }, - }, - devTools: { - register: () => {}, - }, - kibanaLegacy: { - registerLegacyApp: () => {}, - forwardApp: () => {}, - config: { - defaultAppId: 'home', - }, - }, - inspector: { - registerView: () => undefined, - __LEGACY: { - views: { - register: () => undefined, - }, - }, - }, - uiActions: { - attachAction: sinon.fake(), - registerAction: sinon.fake(), - registerTrigger: sinon.fake(), - }, - home: { - featureCatalogue: { - register: sinon.fake(), - }, - environment: { - update: sinon.fake(), - }, - config: { - disableWelcomeScreen: false, - }, - tutorials: { - setVariable: sinon.fake(), - }, - }, - charts: { - theme: { - chartsTheme$: mockObservable, - useChartsTheme: sinon.fake(), - }, - colors: { - seedColors: ['white', 'black'], - }, - }, - management: { - sections: { - getSection: () => ({ - registerApp: sinon.fake(), - }), - }, - }, - indexPatternManagement: { - list: { addListConfig: sinon.fake() }, - creation: { addCreationConfig: sinon.fake() }, - }, - discover: { - docViews: { - addDocView: sinon.fake(), - setAngularInjectorGetter: sinon.fake(), - }, - }, - visTypeVega: { - config: sinon.fake(), - }, - visualizations: { - createBaseVisualization: sinon.fake(), - createReactVisualization: sinon.fake(), - registerAlias: sinon.fake(), - hideTypes: sinon.fake(), - }, - - mapsLegacy: { - serviceSettings: sinon.fake(), - getPrecision: sinon.fake(), - getZoomPrecision: sinon.fake(), - }, - }, -}; - -export const npStart = { - core: mockCoreStart, - plugins: { - management: { - legacy: { - getSection: () => ({ - register: sinon.fake(), - deregister: sinon.fake(), - hasItem: sinon.fake(), - }), - }, - sections: { - getSection: () => ({ - registerApp: sinon.fake(), - }), - }, - }, - indexPatternManagement: { - list: { - getType: sinon.fake(), - getIndexPatternCreationOptions: sinon.fake(), - }, - creation: { - getIndexPatternTags: sinon.fake(), - getFieldInfo: sinon.fake(), - areScriptedFieldsEnabled: sinon.fake(), - }, - }, - embeddable: { - getEmbeddableFactory: sinon.fake(), - getEmbeddableFactories: sinon.fake(), - registerEmbeddableFactory: sinon.fake(), - }, - expressions: { - registerFunction: sinon.fake(), - registerRenderer: sinon.fake(), - registerType: sinon.fake(), - }, - kibanaLegacy: { - getForwards: () => [], - loadFontAwesome: () => {}, - config: { - defaultAppId: 'home', - }, - dashboardConfig: { - turnHideWriteControlsOn: sinon.fake(), - getHideWriteControls: sinon.fake(), - }, - }, - dashboard: { - getSavedDashboardLoader: sinon.fake(), - }, - data: { - actions: { - createFiltersFromValueClickAction: Promise.resolve(['yes']), - createFiltersFromRangeSelectAction: sinon.fake(), - }, - autocomplete: { - getProvider: sinon.fake(), - }, - getSuggestions: sinon.fake(), - indexPatterns: { - get: sinon.spy((indexPatternId) => - Promise.resolve({ - id: indexPatternId, - isTimeNanosBased: () => false, - popularizeField: () => {}, - }) - ), - }, - ui: { - IndexPatternSelect: mockComponent, - SearchBar: mockComponent, - }, - query: { - filterManager: { - getFetches$: sinon.fake(), - getFilters: sinon.fake(), - getAppFilters: sinon.fake(), - getGlobalFilters: sinon.fake(), - removeFilter: sinon.fake(), - addFilters: sinon.fake(), - setFilters: sinon.fake(), - removeAll: sinon.fake(), - getUpdates$: mockObservable, - }, - timefilter: { - timefilter: { - getFetch$: mockObservable, - getAutoRefreshFetch$: mockObservable, - getEnabledUpdated$: mockObservable, - getTimeUpdate$: mockObservable, - getRefreshIntervalUpdate$: mockObservable, - isTimeRangeSelectorEnabled: () => { - return isTimeRangeSelectorEnabled; - }, - isAutoRefreshSelectorEnabled: () => { - return isAutoRefreshSelectorEnabled; - }, - disableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = false; - }, - enableAutoRefreshSelector: () => { - isAutoRefreshSelectorEnabled = true; - }, - getRefreshInterval: () => { - return refreshInterval; - }, - setRefreshInterval: (interval) => { - refreshInterval = interval; - }, - enableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = true; - }, - disableTimeRangeSelector: () => { - isTimeRangeSelectorEnabled = false; - }, - getTime: sinon.fake(), - setTime: sinon.fake(), - getActiveBounds: sinon.fake(), - getBounds: sinon.fake(), - calculateBounds: sinon.fake(), - createFilter: sinon.fake(), - }, - history: sinon.fake(), - }, - }, - search: { - aggs: { - calculateAutoTimeExpression: sinon.fake(), - createAggConfigs: (indexPattern, configStates = []) => { - return new AggConfigs(indexPattern, configStates, { - typesRegistry: aggTypesRegistry.start(), - fieldFormats: getFieldFormatsRegistry(mockCoreStart), - }); - }, - types: aggTypesRegistry.start(), - }, - __LEGACY: { - esClient: { - search: sinon.fake(), - msearch: sinon.fake(), - }, - }, - }, - fieldFormats: getFieldFormatsRegistry(mockCoreStart), - }, - share: { - toggleShareContextMenu: () => {}, - }, - inspector: { - isAvailable: () => false, - open: () => ({ - onClose: Promise.resolve(undefined), - close: () => Promise.resolve(undefined), - }), - }, - uiActions: { - attachAction: sinon.fake(), - registerAction: sinon.fake(), - registerTrigger: sinon.fake(), - detachAction: sinon.fake(), - executeTriggerActions: sinon.fake(), - getTrigger: sinon.fake(), - getTriggerActions: sinon.fake(), - getTriggerCompatibleActions: sinon.fake(), - }, - visualizations: { - get: sinon.fake(), - all: sinon.fake(), - getAliases: sinon.fake(), - savedVisualizationsLoader: {}, - showNewVisModal: sinon.fake(), - createVis: sinon.fake(), - convertFromSerializedVis: sinon.fake(), - convertToSerializedVis: sinon.fake(), - }, - navigation: { - ui: { - TopNavMenu: mockComponent, - }, - }, - charts: { - theme: { - chartsTheme$: mockObservable, - useChartsTheme: sinon.fake(), - }, - }, - discover: { - docViews: { - DocViewer: () => null, - }, - savedSearchLoader: {}, - }, - }, -}; - -export function __setup__(coreSetup) { - npSetup.core = coreSetup; - - // no-op application register calls (this is overwritten to - // bootstrap an LP plugin outside of tests) - npSetup.core.application.register = () => {}; - - npSetup.core.uiSettings.get = mockUiSettings.get; - - // Services that need to be set in the legacy platform since the legacy data - // & vis plugins which previously provided them have been removed. - setSetupServices(npSetup); -} - -export function __start__(coreStart) { - npStart.core = coreStart; - - npStart.core.uiSettings.get = mockUiSettings.get; - - // Services that need to be set in the legacy platform since the legacy data - // & vis plugins which previously provided them have been removed. - setStartServices(npStart); -} diff --git a/src/legacy/ui/public/new_platform/new_platform.test.mocks.ts b/src/legacy/ui/public/new_platform/new_platform.test.mocks.ts deleted file mode 100644 index f44efe17ef8ee..0000000000000 --- a/src/legacy/ui/public/new_platform/new_platform.test.mocks.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { scopedHistoryMock } from '../../../../core/public/mocks'; - -export const setRootControllerMock = jest.fn(); - -jest.doMock('ui/chrome', () => ({ - setRootController: setRootControllerMock, -})); - -export const historyMock = scopedHistoryMock.create(); -jest.doMock('../../../../core/public', () => ({ - ScopedHistory: jest.fn(() => historyMock), -})); diff --git a/src/legacy/ui/public/new_platform/new_platform.test.ts b/src/legacy/ui/public/new_platform/new_platform.test.ts deleted file mode 100644 index d515c348ca440..0000000000000 --- a/src/legacy/ui/public/new_platform/new_platform.test.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -jest.mock('history'); - -import { setRootControllerMock, historyMock } from './new_platform.test.mocks'; -import { legacyAppRegister, __reset__, __setup__, __start__ } from './new_platform'; -import { coreMock } from '../../../../core/public/mocks'; -import { AppMount } from '../../../../core/public'; - -describe('ui/new_platform', () => { - describe('legacyAppRegister', () => { - beforeEach(() => { - setRootControllerMock.mockReset(); - __reset__(); - __setup__(coreMock.createSetup({ basePath: '/test/base/path' }) as any, {} as any); - }); - - const registerApp = () => { - const unmountMock = jest.fn(); - const mountMock = jest.fn, Parameters>(() => unmountMock); - legacyAppRegister({ - id: 'test', - title: 'Test', - mount: mountMock, - }); - return { mountMock, unmountMock }; - }; - - test('sets ui/chrome root controller', () => { - registerApp(); - expect(setRootControllerMock).toHaveBeenCalledWith('test', expect.any(Function)); - }); - - test('throws if called more than once', () => { - registerApp(); - expect(registerApp).toThrowErrorMatchingInlineSnapshot( - `"core.application.register may only be called once for legacy plugins."` - ); - }); - - test('controller calls app.mount when invoked', () => { - const { mountMock } = registerApp(); - const controller = setRootControllerMock.mock.calls[0][1]; - const scopeMock = { $on: jest.fn() }; - const elementMock = [document.createElement('div')]; - - controller(scopeMock, elementMock); - expect(mountMock).toHaveBeenCalledWith({ - element: expect.any(HTMLElement), - appBasePath: '/test/base/path/app/test', - onAppLeave: expect.any(Function), - history: historyMock, - }); - }); - - test('app is mounted in new div inside containing element', () => { - const { mountMock } = registerApp(); - const controller = setRootControllerMock.mock.calls[0][1]; - const scopeMock = { $on: jest.fn() }; - const elementMock = [document.createElement('div')]; - - controller(scopeMock, elementMock); - - const { element } = mountMock.mock.calls[0][0]; - expect(element.parentElement).toEqual(elementMock[0]); - }); - - test('controller calls deprecated context app.mount when invoked', () => { - const unmountMock = jest.fn(); - // Two arguments changes how this is called. - const mountMock = jest.fn((context, params) => unmountMock); - legacyAppRegister({ - id: 'test', - title: 'Test', - mount: mountMock, - }); - const controller = setRootControllerMock.mock.calls[0][1]; - const scopeMock = { $on: jest.fn() }; - const elementMock = [document.createElement('div')]; - - controller(scopeMock, elementMock); - expect(mountMock).toHaveBeenCalledWith(expect.any(Object), { - element: expect.any(HTMLElement), - appBasePath: '/test/base/path/app/test', - onAppLeave: expect.any(Function), - history: historyMock, - }); - }); - - test('controller calls unmount when $scope.$destroy', async () => { - const { unmountMock } = registerApp(); - const controller = setRootControllerMock.mock.calls[0][1]; - const scopeMock = { $on: jest.fn() }; - const elementMock = [document.createElement('div')]; - - controller(scopeMock, elementMock); - // Flush promise queue. Must be done this way because the controller cannot return a Promise without breaking - // angular. - await new Promise((resolve) => setTimeout(resolve, 1)); - - const [event, eventHandler] = scopeMock.$on.mock.calls[0]; - expect(event).toEqual('$destroy'); - eventHandler(); - expect(unmountMock).toHaveBeenCalled(); - }); - }); -}); diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts deleted file mode 100644 index 37787ffbde4fc..0000000000000 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IScope } from 'angular'; - -import { UiActionsStart, UiActionsSetup } from 'src/plugins/ui_actions/public'; -import { EmbeddableStart, EmbeddableSetup } from 'src/plugins/embeddable/public'; -import { createBrowserHistory } from 'history'; -import { VisTypeXyPluginSetup } from 'src/plugins/vis_type_xy/public'; -import { DashboardStart } from '../../../../plugins/dashboard/public'; -import { setSetupServices, setStartServices } from './set_services'; -import { - LegacyCoreSetup, - LegacyCoreStart, - App, - AppMountDeprecated, - ScopedHistory, -} from '../../../../core/public'; -import { Plugin as DataPlugin } from '../../../../plugins/data/public'; -import { Plugin as ExpressionsPlugin } from '../../../../plugins/expressions/public'; -import { - Setup as InspectorSetup, - Start as InspectorStart, -} from '../../../../plugins/inspector/public'; -import { ChartsPluginSetup, ChartsPluginStart } from '../../../../plugins/charts/public'; -import { DevToolsSetup } from '../../../../plugins/dev_tools/public'; -import { KibanaLegacySetup, KibanaLegacyStart } from '../../../../plugins/kibana_legacy/public'; -import { HomePublicPluginSetup } from '../../../../plugins/home/public'; -import { SharePluginSetup, SharePluginStart } from '../../../../plugins/share/public'; -import { - AdvancedSettingsSetup, - AdvancedSettingsStart, -} from '../../../../plugins/advanced_settings/public'; -import { ManagementSetup, ManagementStart } from '../../../../plugins/management/public'; -import { - IndexPatternManagementSetup, - IndexPatternManagementStart, -} from '../../../../plugins/index_pattern_management/public'; -import { BfetchPublicSetup, BfetchPublicStart } from '../../../../plugins/bfetch/public'; -import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; -import { TelemetryPluginSetup, TelemetryPluginStart } from '../../../../plugins/telemetry/public'; -import { - NavigationPublicPluginSetup, - NavigationPublicPluginStart, -} from '../../../../plugins/navigation/public'; -import { DiscoverSetup, DiscoverStart } from '../../../../plugins/discover/public'; -import { - SavedObjectsManagementPluginSetup, - SavedObjectsManagementPluginStart, -} from '../../../../plugins/saved_objects_management/public'; -import { - VisualizationsSetup, - VisualizationsStart, -} from '../../../../plugins/visualizations/public'; -import { VisTypeTimelionPluginStart } from '../../../../plugins/vis_type_timelion/public'; -import { MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public'; - -export interface PluginsSetup { - bfetch: BfetchPublicSetup; - charts: ChartsPluginSetup; - data: ReturnType; - embeddable: EmbeddableSetup; - expressions: ReturnType; - home: HomePublicPluginSetup; - inspector: InspectorSetup; - uiActions: UiActionsSetup; - navigation: NavigationPublicPluginSetup; - devTools: DevToolsSetup; - kibanaLegacy: KibanaLegacySetup; - share: SharePluginSetup; - usageCollection: UsageCollectionSetup; - advancedSettings: AdvancedSettingsSetup; - management: ManagementSetup; - discover: DiscoverSetup; - visualizations: VisualizationsSetup; - telemetry?: TelemetryPluginSetup; - savedObjectsManagement: SavedObjectsManagementPluginSetup; - mapsLegacy: MapsLegacyPluginSetup; - indexPatternManagement: IndexPatternManagementSetup; - visTypeXy?: VisTypeXyPluginSetup; -} - -export interface PluginsStart { - bfetch: BfetchPublicStart; - charts: ChartsPluginStart; - data: ReturnType; - embeddable: EmbeddableStart; - expressions: ReturnType; - inspector: InspectorStart; - uiActions: UiActionsStart; - navigation: NavigationPublicPluginStart; - kibanaLegacy: KibanaLegacyStart; - share: SharePluginStart; - management: ManagementStart; - advancedSettings: AdvancedSettingsStart; - discover: DiscoverStart; - visualizations: VisualizationsStart; - telemetry?: TelemetryPluginStart; - dashboard: DashboardStart; - savedObjectsManagement: SavedObjectsManagementPluginStart; - visTypeTimelion: VisTypeTimelionPluginStart; - indexPatternManagement: IndexPatternManagementStart; -} - -export const npSetup = { - core: (null as unknown) as LegacyCoreSetup, - plugins: {} as PluginsSetup, -}; - -export const npStart = { - core: (null as unknown) as LegacyCoreStart, - plugins: {} as PluginsStart, -}; - -/** - * Only used by unit tests - * @internal - */ -export function __reset__() { - npSetup.core = (null as unknown) as LegacyCoreSetup; - npSetup.plugins = {} as any; - npStart.core = (null as unknown) as LegacyCoreStart; - npStart.plugins = {} as any; - legacyAppRegistered = false; -} - -export function __setup__(coreSetup: LegacyCoreSetup, plugins: PluginsSetup) { - npSetup.core = coreSetup; - npSetup.plugins = plugins; - - // Setup compatibility layer for AppService in legacy platform - npSetup.core.application.register = legacyAppRegister; - - // Services that need to be set in the legacy platform since the legacy data - // & vis plugins which previously provided them have been removed. - setSetupServices(npSetup); -} - -export function __start__(coreStart: LegacyCoreStart, plugins: PluginsStart) { - npStart.core = coreStart; - npStart.plugins = plugins; - - // Services that need to be set in the legacy platform since the legacy data - // & vis plugins which previously provided them have been removed. - setStartServices(npStart); -} - -/** Flag used to ensure `legacyAppRegister` is only called once. */ -let legacyAppRegistered = false; - -/** - * Exported for testing only. Use `npSetup.core.application.register` in legacy apps. - * @internal - */ -export const legacyAppRegister = (app: App) => { - if (legacyAppRegistered) { - throw new Error(`core.application.register may only be called once for legacy plugins.`); - } - legacyAppRegistered = true; - - // eslint-disable-next-line @typescript-eslint/no-var-requires - require('ui/chrome').setRootController(app.id, ($scope: IScope, $element: JQLite) => { - const element = document.createElement('div'); - $element[0].appendChild(element); - - // Root controller cannot return a Promise so use an internal async function and call it immediately - (async () => { - const appRoute = app.appRoute || `/app/${app.id}`; - const appBasePath = npSetup.core.http.basePath.prepend(appRoute); - const params = { - element, - appBasePath, - history: new ScopedHistory( - createBrowserHistory({ basename: npSetup.core.http.basePath.get() }), - appRoute - ), - onAppLeave: () => undefined, - }; - const unmount = isAppMountDeprecated(app.mount) - ? await app.mount({ core: npStart.core }, params) - : await app.mount(params); - $scope.$on('$destroy', () => { - unmount(); - }); - })(); - }); -}; - -function isAppMountDeprecated(mount: (...args: any[]) => any): mount is AppMountDeprecated { - // Mount functions with two arguments are assumed to expect deprecated `context` object. - return mount.length === 2; -} diff --git a/src/legacy/ui/public/new_platform/set_services.test.ts b/src/legacy/ui/public/new_platform/set_services.test.ts deleted file mode 100644 index 74e789ec220b1..0000000000000 --- a/src/legacy/ui/public/new_platform/set_services.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { __reset__, __setup__, __start__, PluginsSetup, PluginsStart } from './new_platform'; -import * as dataServices from '../../../../plugins/data/public/services'; -import * as visualizationsServices from '../../../../plugins/visualizations/public/services'; -import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; -import { coreMock } from '../../../../core/public/mocks'; -import { npSetup, npStart } from './__mocks__'; - -describe('ui/new_platform', () => { - describe('set service getters', () => { - const testServiceGetters = (name: string, services: Record) => { - const getters = Object.keys(services).filter((k) => k.substring(0, 3) === 'get'); - getters.forEach((g) => { - it(`ui/new_platform sets a value for ${name} getter ${g}`, () => { - __reset__(); - __setup__( - (coreMock.createSetup() as unknown) as LegacyCoreSetup, - (npSetup.plugins as unknown) as PluginsSetup - ); - __start__( - (coreMock.createStart() as unknown) as LegacyCoreStart, - (npStart.plugins as unknown) as PluginsStart - ); - - expect(services[g]()).toBeDefined(); - }); - }); - }; - - testServiceGetters('data', dataServices); - testServiceGetters('visualizations', visualizationsServices); - }); -}); diff --git a/src/legacy/ui/public/new_platform/set_services.ts b/src/legacy/ui/public/new_platform/set_services.ts deleted file mode 100644 index 036157a9f3fbc..0000000000000 --- a/src/legacy/ui/public/new_platform/set_services.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { pick } from 'lodash'; - -import { PluginsSetup, PluginsStart } from './new_platform'; -import { LegacyCoreSetup, LegacyCoreStart } from '../../../../core/public'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import * as dataServices from '../../../../plugins/data/public/services'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import * as visualizationsServices from '../../../../plugins/visualizations/public/services'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { createSavedVisLoader } from '../../../../plugins/visualizations/public/saved_visualizations/saved_visualizations'; - -interface NpSetup { - core: LegacyCoreSetup; - plugins: PluginsSetup; -} - -interface NpStart { - core: LegacyCoreStart; - plugins: PluginsStart; -} - -export function setSetupServices(npSetup: NpSetup) { - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - visualizationsServices.setUISettings(npSetup.core.uiSettings); - visualizationsServices.setUsageCollector(npSetup.plugins.usageCollection); -} - -export function setStartServices(npStart: NpStart) { - // Services that need to be set in the legacy platform since the legacy data plugin - // which previously provided them has been removed. - dataServices.setNotifications(npStart.core.notifications); - dataServices.setOverlays(npStart.core.overlays); - dataServices.setUiSettings(npStart.core.uiSettings); - dataServices.setFieldFormats(npStart.plugins.data.fieldFormats); - dataServices.setIndexPatterns(npStart.plugins.data.indexPatterns); - dataServices.setQueryService(npStart.plugins.data.query); - dataServices.setSearchService(npStart.plugins.data.search); - - visualizationsServices.setI18n(npStart.core.i18n); - visualizationsServices.setTypes( - pick(npStart.plugins.visualizations, ['get', 'all', 'getAliases']) - ); - visualizationsServices.setCapabilities(npStart.core.application.capabilities); - visualizationsServices.setHttp(npStart.core.http); - visualizationsServices.setApplication(npStart.core.application); - visualizationsServices.setEmbeddable(npStart.plugins.embeddable); - visualizationsServices.setSavedObjects(npStart.core.savedObjects); - visualizationsServices.setIndexPatterns(npStart.plugins.data.indexPatterns); - visualizationsServices.setFilterManager(npStart.plugins.data.query.filterManager); - visualizationsServices.setExpressions(npStart.plugins.expressions); - visualizationsServices.setUiActions(npStart.plugins.uiActions); - visualizationsServices.setTimeFilter(npStart.plugins.data.query.timefilter.timefilter); - visualizationsServices.setAggs(npStart.plugins.data.search.aggs); - visualizationsServices.setOverlays(npStart.core.overlays); - visualizationsServices.setChrome(npStart.core.chrome); - visualizationsServices.setSearch(npStart.plugins.data.search); - const savedVisualizationsLoader = createSavedVisLoader({ - savedObjectsClient: npStart.core.savedObjects.client, - indexPatterns: npStart.plugins.data.indexPatterns, - search: npStart.plugins.data.search, - chrome: npStart.core.chrome, - overlays: npStart.core.overlays, - visualizationTypes: visualizationsServices.getTypes(), - }); - visualizationsServices.setSavedVisualizationsLoader(savedVisualizationsLoader); - visualizationsServices.setSavedSearchLoader(npStart.plugins.discover.savedSearchLoader); -} diff --git a/src/legacy/ui/public/notify/banners/BANNERS.md b/src/legacy/ui/public/notify/banners/BANNERS.md deleted file mode 100644 index fc6bddc3aa4ed..0000000000000 --- a/src/legacy/ui/public/notify/banners/BANNERS.md +++ /dev/null @@ -1,360 +0,0 @@ -# Banners - -Use this service to surface banners at the top of the screen. The expectation is that the banner will used an -`` to render, but that is not a requirement. See [the EUI docs](https://elastic.github.io/eui/) for -more information on banners and their role within the UI. - -Banners should be considered with respect to their lifecycle. Most banners are best served by using the `add` and -`remove` functions. - -## Importing the module - -```js -import { banners } from 'ui/notify'; -``` - -## Interface - -There are three methods defined to manipulate the list of banners: `add`, `set`, and `remove`. A fourth method, -`onChange` exists to listen to changes made via `add`, `set`, and `remove`. - -### `add()` - -This is the preferred way to add banners because it implies the best usage of the banner: added once during a page's -lifecycle. For other usages, consider *not* using a banner. - -#### Syntax - -```js -const bannerId = banners.add({ - // required: - component, - // optional: - priority, -}); -``` - -##### Parameters - -| Field | Type | Description | -|-------|------|-------------| -| `component` | Any | The value displayed as the banner. | -| `priority` | Number | Optional priority, which defaults to `0` used to place the banner. | - -To add a banner, you only need to define the `component` field. - -The `priority` sorts in descending order. Items sharing the same priority are sorted from oldest to newest. For example: - -```js -const banner1 = banners.add({ component: }); -const banner2 = banners.add({ component: , priority: 0 }); -const banner3 = banners.add({ component: , priority: 1 }); -``` - -That would be displayed as: - -``` -[ fake3 ] -[ fake1 ] -[ fake2 ] -``` - -##### Returns - -| Type | Description | -|------|-------------| -| String | A newly generated ID. | - -#### Example - -This example includes buttons that allow the user to remove the banner. In some cases, you may not want any buttons -and in other cases you will want an action to proceed the banner's removal (e.g., apply an Advanced Setting). - -This makes the most sense to use when a banner is added at the beginning of the page life cycle and not expected to -be touched, except by its own buttons triggering an action or navigating away. - -```js -const bannerId = banners.add({ - component: ( - - - - banners.remove(bannerId)} - > - Dismiss - - - - window.alert('Do Something Else')} - > - Do Something Else - - - - - ), -}); -``` - -### `remove()` - -Unlike toast notifications, banners stick around until they are explicitly removed. Using the `add` example above,you can remove it by calling `remove`. - -Note: They will stick around as long as the scope is remembered by whatever set it; navigating away won't remove it -unless the scope is forgotten (e.g., when the "app" changes)! - -#### Syntax - -```js -const removed = banners.remove(bannerId); -``` - -##### Parameters - -| Field | Type | Description | -|-------|------|-------------| -| `id` | String | ID of a banner. | - -##### Returns - -| Type | Description | -|------|-------------| -| Boolean | `true` if the ID was recognized and the banner was removed. `false` otherwise. | - -#### Example - -To remove a banner, you need to pass the `id` of the banner. - -```js -if (banners.remove(bannerId)) { - // removed; otherwise it didn't exist (maybe it was already removed) -} -``` - -#### Scheduled removal - -Like toast notifications do automatically, you can have a banner automatically removed after a set of time, by -setting a timer: - -```js -setTimeout(() => banners.remove(bannerId), 15000); -``` - -Note: It is safe to remove a banner more than once as unknown IDs will be ignored. - -### `set()` - -Banners can be replaced once added by supplying their `id`. If one is supplied, then the ID will be used to replace -any banner with the same ID and a **new** `id` will be returned. - -You should only consider using `set` when the banner is manipulated frequently in the lifecycle of the page, where -maintaining the banner's `id` can be a burden. It is easier to allow `banners` to create the ID for you in most -situations where a banner is useful (e.g., set once), which safely avoids any chance to have an ID-based collision, -which happens automatically with `add`. - -Usage of `set` can imply that your use case is abusing the banner system. - -Note: `set` will only trigger the callback once for both the implicit remove and add operation. - -#### Syntax - -```js -const id = banners.set({ - // required: - component, - // optional: - id, - priority, -}); -``` - -##### Parameters - -| Field | Type | Description | -|-------|------|-------------| -| `component` | Any | The value displayed as the banner. | -| `id` | String | Optional ID used to remove an existing banner. | -| `priority` | Number | Optional priority, which defaults to `0` used to place the banner. | - -The `id` is optional because it follows the same semantics as the `remove` method: unknown IDs are ignored. This -is useful when first creating a banner so that you do not have to call `add` instead. - -##### Returns - -| Type | Description | -|------|-------------| -| String | A newly generated ID. | - -#### Example - -This example does not include any way for the user to clear the banner directly. Instead, it is cleared based on -time. Related to it being cleared by time, it can also reappear within the same page life cycle by navigating between -different paths that need it displayed. Instead of adding a new banner for every navigation, you should replace any -existing banner. - -```js -let bannerId; -let timeoutId; - -function displayBanner() { - clearTimeout(timeoutId); - - bannerId = banners.set({ - id: bannerId, // the first time it will be undefined, but reused as long as this is in the same lifecycle - component: ( - - ) - }); - - // hide the message after the user has had a chance to acknowledge it -- so it doesn't permanently stick around - banner.timeoutId = setTimeout(() => { - banners.remove(bannerId); - timeoutId = undefined; - }, 6000); -} -``` - -### `onChange()` - -For React components that intend to display the banners, it is not enough to simply `render` the `banners.list` -values. Because they can change after being rendered, the React component that renders the list must be alerted -to changes to the list. - -#### Syntax - -```js -// inside your React component -banners.onChange(() => this.forceUpdate()); -``` - -##### Parameters - -| Field | Type | Description | -|-------|------|-------------| -| `callback` | Function | The function to invoke whenever the internal banner list is changed. | - -Every new `callback` replaces the previous callback. So calling this with `null` or `undefined` will unset the -callback. - -##### Returns - -Nothing. - -#### Example - -This can be used inside of a React component to trigger a re-`render` of the banners. - -```js -import { GlobalBannerList } from 'ui/notify'; - - -``` - -### `list` - -For React components that intend to display the banners, it is not enough to simply `render` the `banners.list` -values. Because they can change after being rendered, the React component that renders the list must be alerted -to changes to the list. - -#### Syntax - -```js - -``` - -##### Returns - -| Type | Description | -|------|-------------| -| Array | The array of banner objects. | - -Banner objects are sorted in descending order based on their `priority`, in the form: - -```js -{ - id: 'banner-123', - component: , - priority: 12, -} -``` - -| Field | Type | Description | -|-------|------|-------------| -| `component` | Any | The value displayed as the banner. | -| `id` | String | The ID of the banner, which can be used as a React "key". | -| `priority` | Number | The priority of the banner. | - -#### Example - -This can be used to supply the banners to the `GlobalBannerList` React component (which is done for you). - -```js -import { GlobalBannerList } from 'ui/notify'; - - -``` - -## Use in functional tests - -Functional tests are commonly used to verify that an action yielded a successful outcome. You can place a -`data-test-subj` attribute on the banner and use it to check if the banner exists inside of your functional test. -This acts as a proxy for verifying the successful outcome. Any unrecognized field will be added as a property of the -containing element. - -```js -banners.add({ - component: ( - - ), - data-test-subj: 'my-tested-banner', -}); -``` - -This will apply the `data-test-subj` to the element containing the `component`, so the inner HTML of that element -will exclusively be the specified `component`. - -Given that `component` is expected to be a React component, you could also add the `data-test-subj` directly to it: - -```js -banners.add({ - component: ( - - ), -}); -``` \ No newline at end of file diff --git a/src/legacy/ui/public/notify/banners/banners.tsx b/src/legacy/ui/public/notify/banners/banners.tsx deleted file mode 100644 index 777e408b8dfc4..0000000000000 --- a/src/legacy/ui/public/notify/banners/banners.tsx +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import { npStart } from 'ui/new_platform'; -import { I18nProvider } from '@kbn/i18n/react'; - -const npBanners = npStart.core.overlays.banners; - -/** compatibility layer for new platform */ -const mountForComponent = (component: React.ReactElement) => (element: HTMLElement) => { - ReactDOM.render({component}, element); - return () => ReactDOM.unmountComponentAtNode(element); -}; - -/** - * Banners represents a prioritized list of displayed components. - */ -export class Banners { - /** - * Add a new banner. - * - * @param {Object} component The React component to display. - * @param {Number} priority The optional priority order to display this banner. Higher priority values are shown first. - * @return {String} A newly generated ID. This value can be used to remove/replace the banner. - */ - add = ({ component, priority }: { component: React.ReactElement; priority?: number }) => { - return npBanners.add(mountForComponent(component), priority); - }; - - /** - * Remove an existing banner. - * - * @param {String} id The ID of the banner to remove. - * @return {Boolean} {@code true} if the ID is recognized and the banner is removed. {@code false} otherwise. - */ - remove = (id: string): boolean => { - return npBanners.remove(id); - }; - - /** - * Replace an existing banner by removing it, if it exists, and adding a new one in its place. - * - * This is similar to calling banners.remove, followed by banners.add, except that it only notifies the listener - * after adding. - * - * @param {Object} component The React component to display. - * @param {String} id The ID of the Banner to remove. - * @param {Number} priority The optional priority order to display this banner. Higher priority values are shown first. - * @return {String} A newly generated ID. This value can be used to remove/replace the banner. - */ - set = ({ - component, - id, - priority = 0, - }: { - component: React.ReactElement; - id: string; - priority?: number; - }): string => { - return npBanners.replace(id, mountForComponent(component), priority); - }; -} - -/** - * A singleton instance meant to represent all Kibana banners. - */ -export const banners = new Banners(); diff --git a/src/legacy/ui/public/notify/banners/index.ts b/src/legacy/ui/public/notify/banners/index.ts deleted file mode 100644 index 9221f95074cd9..0000000000000 --- a/src/legacy/ui/public/notify/banners/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { banners } from './banners'; diff --git a/src/legacy/ui/public/notify/fatal_error.ts b/src/legacy/ui/public/notify/fatal_error.ts deleted file mode 100644 index 5614ffea7913e..0000000000000 --- a/src/legacy/ui/public/notify/fatal_error.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { npSetup } from 'ui/new_platform'; -import { AngularHttpError, addFatalError } from '../../../../plugins/kibana_legacy/public'; - -export function fatalError(error: AngularHttpError | Error | string, location?: string) { - addFatalError(npSetup.core.fatalErrors, error, location); -} diff --git a/src/legacy/ui/public/notify/index.d.ts b/src/legacy/ui/public/notify/index.d.ts deleted file mode 100644 index 3c1a7ba02db72..0000000000000 --- a/src/legacy/ui/public/notify/index.d.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { toastNotifications } from './toasts'; -export { fatalError } from './fatal_error'; diff --git a/src/legacy/ui/public/notify/index.js b/src/legacy/ui/public/notify/index.js deleted file mode 100644 index 51394033e4d2e..0000000000000 --- a/src/legacy/ui/public/notify/index.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { fatalError } from './fatal_error'; -export { toastNotifications } from './toasts'; -export { banners } from './banners'; diff --git a/src/legacy/ui/public/notify/toasts/index.ts b/src/legacy/ui/public/notify/toasts/index.ts deleted file mode 100644 index c5e8e3b7ca7a3..0000000000000 --- a/src/legacy/ui/public/notify/toasts/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -export { ToastNotifications } from './toast_notifications'; -export { toastNotifications } from './toasts'; diff --git a/src/legacy/ui/public/notify/toasts/toast_notifications.ts b/src/legacy/ui/public/notify/toasts/toast_notifications.ts deleted file mode 100644 index d3ec8edb5d73a..0000000000000 --- a/src/legacy/ui/public/notify/toasts/toast_notifications.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -/** - * ToastNotifications is deprecated! Please use npSetup.core.notifications.toasts instead - */ -export { ToastNotifications } from '../../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/ui/public/notify/toasts/toasts.ts b/src/legacy/ui/public/notify/toasts/toasts.ts deleted file mode 100644 index 15be7a7891553..0000000000000 --- a/src/legacy/ui/public/notify/toasts/toasts.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { npSetup } from 'ui/new_platform'; -import { ToastNotifications } from './toast_notifications'; -export const toastNotifications = new ToastNotifications(npSetup.core.notifications.toasts); diff --git a/src/legacy/ui/public/private/__tests__/private.js b/src/legacy/ui/public/private/__tests__/private.js deleted file mode 100644 index 1f9d696bb440f..0000000000000 --- a/src/legacy/ui/public/private/__tests__/private.js +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; - -describe('Private module loader', function () { - let Private; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function ($injector) { - Private = $injector.get('Private'); - }) - ); - - it('accepts a provider that will be called to init a module', function () { - const football = {}; - function Provider() { - return football; - } - - const instance = Private(Provider); - expect(instance).to.be(football); - }); - - it('injects angular dependencies into the Provider', function () { - function Provider(Private) { - return Private; - } - - const instance = Private(Provider); - expect(instance).to.be(Private); - }); - - it('detects circular dependencies', function () { - expect(function () { - function Provider1() { - Private(Provider2); - } - - function Provider2() { - Private(Provider1); - } - - Private(Provider1); - }).to.throwException(/circular/i); - }); - - it('always provides the same instance form the Provider', function () { - function Provider() { - return {}; - } - - expect(Private(Provider)).to.be(Private(Provider)); - }); - - describe('#stub', function () { - it('accepts a replacement instance for a Provider', function () { - const replaced = {}; - const replacement = {}; - - function Provider() { - return replaced; - } - - const instance = Private(Provider); - expect(instance).to.be(replaced); - - Private.stub(Provider, replacement); - - const instance2 = Private(Provider); - expect(instance2).to.be(replacement); - - Private.stub(Provider, replaced); - - const instance3 = Private(Provider); - expect(instance3).to.be(replaced); - }); - }); - - describe('#swap', function () { - it('accepts a new Provider that should replace an existing Provider', function () { - function Provider1() { - return {}; - } - - function Provider2() { - return {}; - } - - const instance1 = Private(Provider1); - expect(instance1).to.be.an('object'); - - Private.swap(Provider1, Provider2); - - const instance2 = Private(Provider1); - expect(instance2).to.be.an('object'); - expect(instance2).to.not.be(instance1); - - Private.swap(Provider1, Provider1); - - const instance3 = Private(Provider1); - expect(instance3).to.be(instance1); - }); - - it('gives the new Provider access to the Provider it replaced via an injectable dependency called $decorate', function () { - function Provider1() { - return {}; - } - - function Provider2($decorate) { - return { - instance1: $decorate(), - }; - } - - const instance1 = Private(Provider1); - expect(instance1).to.be.an('object'); - - Private.swap(Provider1, Provider2); - - const instance2 = Private(Provider1); - expect(instance2).to.have.property('instance1'); - expect(instance2.instance1).to.be(instance1); - }); - }); -}); diff --git a/src/legacy/ui/public/private/index.d.ts b/src/legacy/ui/public/private/index.d.ts deleted file mode 100644 index 3b692ba58cbe1..0000000000000 --- a/src/legacy/ui/public/private/index.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { IPrivate } from '../../../../plugins/kibana_legacy/public/'; diff --git a/src/legacy/ui/public/private/index.js b/src/legacy/ui/public/private/index.js deleted file mode 100644 index 3815f230df466..0000000000000 --- a/src/legacy/ui/public/private/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './private'; diff --git a/src/legacy/ui/public/private/private.js b/src/legacy/ui/public/private/private.js deleted file mode 100644 index 7a0751959417e..0000000000000 --- a/src/legacy/ui/public/private/private.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiModules } from '../modules'; -import { PrivateProvider } from '../../../../plugins/kibana_legacy/public'; - -uiModules.get('kibana/private').provider('Private', PrivateProvider); diff --git a/src/legacy/ui/public/promises/__tests__/promises.js b/src/legacy/ui/public/promises/__tests__/promises.js deleted file mode 100644 index 7041aa2993376..0000000000000 --- a/src/legacy/ui/public/promises/__tests__/promises.js +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import sinon from 'sinon'; - -describe('Promise service', () => { - let Promise; - let $rootScope; - - const sandbox = sinon.createSandbox(); - function tick(ms = 0) { - sandbox.clock.tick(ms); - - // Ugly, but necessary for promises to resolve: https://github.com/angular/angular.js/issues/12555 - $rootScope.$apply(); - } - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(($injector) => { - sandbox.useFakeTimers(); - - Promise = $injector.get('Promise'); - $rootScope = $injector.get('$rootScope'); - }) - ); - - afterEach(() => sandbox.restore()); - - describe('Constructor', () => { - it('provides resolve and reject function', () => { - const executor = sinon.stub(); - new Promise(executor); - - sinon.assert.calledOnce(executor); - sinon.assert.calledWithExactly(executor, sinon.match.func, sinon.match.func); - }); - }); - - it('Promise.resolve', () => { - const onResolve = sinon.stub(); - Promise.resolve(true).then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, true); - }); - - describe('Promise.fromNode', () => { - it('creates a callback that controls a promise', () => { - const callback = sinon.stub(); - Promise.fromNode(callback); - - tick(); - - sinon.assert.calledOnce(callback); - sinon.assert.calledWithExactly(callback, sinon.match.func); - }); - - it('rejects if the callback receives an error', () => { - const err = new Error(); - const onReject = sinon.stub(); - Promise.fromNode(sinon.stub().yields(err)).catch(onReject); - - tick(); - - sinon.assert.calledOnce(onReject); - sinon.assert.calledWithExactly(onReject, sinon.match.same(err)); - }); - - it('resolves with the second argument', () => { - const result = {}; - const onResolve = sinon.stub(); - Promise.fromNode(sinon.stub().yields(null, result)).then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, sinon.match.same(result)); - }); - - it('resolves with an array if multiple arguments are received', () => { - const result1 = {}; - const result2 = {}; - const onResolve = sinon.stub(); - Promise.fromNode(sinon.stub().yields(null, result1, result2)).then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, [ - sinon.match.same(result1), - sinon.match.same(result2), - ]); - }); - - it('resolves with an array if multiple undefined are received', () => { - const onResolve = sinon.stub(); - Promise.fromNode(sinon.stub().yields(null, undefined, undefined)).then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, [undefined, undefined]); - }); - }); - - describe('Promise.race()', () => { - it(`resolves with the first resolved promise's value`, () => { - const p1 = new Promise((resolve) => setTimeout(resolve, 100, 1)); - const p2 = new Promise((resolve) => setTimeout(resolve, 200, 2)); - const onResolve = sinon.stub(); - Promise.race([p1, p2]).then(onResolve); - - tick(200); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 1); - }); - - it(`rejects with the first rejected promise's rejection reason`, () => { - const p1Error = new Error('1'); - const p1 = new Promise((r, reject) => setTimeout(reject, 200, p1Error)); - - const p2Error = new Error('2'); - const p2 = new Promise((r, reject) => setTimeout(reject, 100, p2Error)); - - const onReject = sinon.stub(); - Promise.race([p1, p2]).catch(onReject); - - tick(200); - - sinon.assert.calledOnce(onReject); - sinon.assert.calledWithExactly(onReject, sinon.match.same(p2Error)); - }); - - it('does not wait for subsequent promises to resolve/reject', () => { - const onP1Resolve = sinon.stub(); - const p1 = new Promise((resolve) => setTimeout(resolve, 100)).then(onP1Resolve); - - const onP2Resolve = sinon.stub(); - const p2 = new Promise((resolve) => setTimeout(resolve, 101)).then(onP2Resolve); - - const onResolve = sinon.stub(); - Promise.race([p1, p2]).then(onResolve); - - tick(100); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledOnce(onP1Resolve); - sinon.assert.callOrder(onP1Resolve, onResolve); - sinon.assert.notCalled(onP2Resolve); - }); - - it('allows non-promises in the array', () => { - const onResolve = sinon.stub(); - Promise.race([1, 2, 3]).then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 1); - }); - - describe('argument is undefined', () => { - it('rejects the promise', () => { - const football = {}; - const onReject = sinon.stub(); - Promise.race() - .catch(() => football) - .then(onReject); - - tick(); - - sinon.assert.calledOnce(onReject); - sinon.assert.calledWithExactly(onReject, sinon.match.same(football)); - }); - }); - - describe('argument is a string', () => { - it(`resolves with the first character`, () => { - const onResolve = sinon.stub(); - Promise.race('abc').then(onResolve); - - tick(); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 'a'); - }); - }); - - describe('argument is a non-iterable object', () => { - it('reject the promise', () => { - const football = {}; - const onReject = sinon.stub(); - Promise.race({}) - .catch(() => football) - .then(onReject); - - tick(); - - sinon.assert.calledOnce(onReject); - sinon.assert.calledWithExactly(onReject, sinon.match.same(football)); - }); - }); - - describe('argument is a generator', () => { - it('resolves with the first resolved value', () => { - function* gen() { - yield new Promise((resolve) => setTimeout(resolve, 100, 1)); - yield new Promise((resolve) => setTimeout(resolve, 200, 2)); - } - - const onResolve = sinon.stub(); - Promise.race(gen()).then(onResolve); - - tick(200); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 1); - }); - - it('resolves with the first non-promise value', () => { - function* gen() { - yield 1; - yield new Promise((resolve) => setTimeout(resolve, 200, 2)); - } - - const onResolve = sinon.stub(); - Promise.race(gen()).then(onResolve); - - tick(200); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 1); - }); - - it('iterates all values from the generator, even if one is already "resolved"', () => { - let yieldCount = 0; - function* gen() { - yieldCount += 1; - yield 1; - yieldCount += 1; - yield new Promise((resolve) => setTimeout(resolve, 200, 2)); - } - - const onResolve = sinon.stub(); - Promise.race(gen()).then(onResolve); - - tick(200); - - sinon.assert.calledOnce(onResolve); - sinon.assert.calledWithExactly(onResolve, 1); - expect(yieldCount).to.be(2); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/promises/defer.ts b/src/legacy/ui/public/promises/defer.ts deleted file mode 100644 index 3d435f2ba8dfd..0000000000000 --- a/src/legacy/ui/public/promises/defer.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export interface Defer { - promise: Promise; - resolve(value: T): void; - reject(reason: Error): void; -} - -export function createDefer(Class: typeof Promise): Defer { - const defer: Partial> = {}; - defer.promise = new Class((resolve, reject) => { - defer.resolve = resolve; - defer.reject = reject; - }); - return defer as Defer; -} diff --git a/src/legacy/ui/public/promises/index.ts b/src/legacy/ui/public/promises/index.ts deleted file mode 100644 index 07bb2554c5eda..0000000000000 --- a/src/legacy/ui/public/promises/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { PromiseServiceCreator } from '../../../../plugins/kibana_legacy/public'; -export { createDefer } from './defer'; -// @ts-ignore -import { uiModules } from '../modules'; - -const module = uiModules.get('kibana'); -// Provides a tiny subset of the excellent API from -// bluebird, reimplemented using the $q service -module.service('Promise', PromiseServiceCreator); diff --git a/src/legacy/ui/public/react_components.js b/src/legacy/ui/public/react_components.js deleted file mode 100644 index 21fb53d6407fa..0000000000000 --- a/src/legacy/ui/public/react_components.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import 'ngreact'; - -import { EuiIcon, EuiIconTip } from '@elastic/eui'; - -import { uiModules } from './modules'; - -const app = uiModules.get('app/kibana', ['react']); - -app.directive('icon', (reactDirective) => reactDirective(EuiIcon)); - -app.directive('iconTip', (reactDirective) => - reactDirective(EuiIconTip, ['content', 'type', 'position', 'title', 'color']) -); diff --git a/src/legacy/ui/public/registry/__tests__/registry.js b/src/legacy/ui/public/registry/__tests__/registry.js deleted file mode 100644 index 10b2423abc0e3..0000000000000 --- a/src/legacy/ui/public/registry/__tests__/registry.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiRegistry } from '../_registry'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; - -describe('Registry', function () { - let Private; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function ($injector) { - Private = $injector.get('Private'); - }) - ); - - it('is technically a function', function () { - const reg = uiRegistry(); - expect(reg).to.be.a('function'); - }); - - describe('#register', function () { - it('accepts a Private module', function () { - const reg = uiRegistry(); - const mod = function SomePrivateModule() {}; - - reg.register(mod); - // modules are not exposed, so this is the most that we can test - }); - - it('applies the filter function if one is specified', function () { - const reg = uiRegistry({ - filter: (item) => item.value % 2 === 0, // register only even numbers - }); - - reg.register(() => ({ value: 17 })); - reg.register(() => ({ value: 18 })); // only this one should get registered - reg.register(() => ({ value: 19 })); - - const modules = Private(reg); - expect(modules).to.have.length(1); - expect(modules[0].value).to.be(18); - }); - }); - - describe('as a module', function () { - it('exposes the list of registered modules', function () { - const reg = uiRegistry(); - const mod = function SomePrivateModule(Private) { - this.PrivateModuleLoader = Private; - }; - - reg.register(mod); - const modules = Private(reg); - expect(modules).to.have.length(1); - expect(modules[0]).to.have.property('PrivateModuleLoader', Private); - }); - }); - - describe('spec', function () { - it('executes with the module list as "this", and can override it', function () { - let self; - - const reg = uiRegistry({ - constructor: function () { - return { mods: (self = this) }; - }, - }); - - const modules = Private(reg); - expect(modules).to.be.an('object'); - expect(modules).to.have.property('mods', self); - }); - }); - - describe('spec.name', function () { - it('sets the displayName of the registry and the name param on the final instance', function () { - const reg = uiRegistry({ - name: 'visTypes', - }); - - expect(reg).to.have.property('displayName', '[registry visTypes]'); - expect(Private(reg)).to.have.property('name', 'visTypes'); - }); - }); - - describe('spec.constructor', function () { - it('executes before the modules are returned', function () { - let i = 0; - - const reg = uiRegistry({ - constructor: function () { - i = i + 1; - }, - }); - - Private(reg); - expect(i).to.be(1); - }); - - it('executes with the module list as "this", and can override it', function () { - let self; - - const reg = uiRegistry({ - constructor: function () { - return { mods: (self = this) }; - }, - }); - - const modules = Private(reg); - expect(modules).to.be.an('object'); - expect(modules).to.have.property('mods', self); - }); - }); - - describe('spec.invokeProviders', () => { - it('is called with the registered providers and defines the initial set of values in the registry', () => { - const reg = uiRegistry({ - invokeProviders(providers) { - return providers.map((i) => i * 1000); - }, - }); - - reg.register(1); - reg.register(2); - reg.register(3); - expect(Private(reg).toJSON()).to.eql([1000, 2000, 3000]); - }); - it('does not get assigned as a property of the registry', () => { - expect( - uiRegistry({ - invokeProviders() {}, - }) - ).to.not.have.property('invokeProviders'); - }); - }); - - describe('spec[any]', function () { - it('mixes the extra properties into the module list', function () { - const reg = uiRegistry({ - someMethod: function () { - return this; - }, - }); - - const modules = Private(reg); - expect(modules).to.have.property('someMethod'); - expect(modules.someMethod()).to.be(modules); - }); - }); -}); diff --git a/src/legacy/ui/public/registry/_registry.d.ts b/src/legacy/ui/public/registry/_registry.d.ts deleted file mode 100644 index 42f1bb521763c..0000000000000 --- a/src/legacy/ui/public/registry/_registry.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexedArray, IndexedArrayConfig } from '../indexed_array'; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface UIRegistry extends IndexedArray {} - -interface UIRegistrySpec extends IndexedArrayConfig { - name: string; - filter?(item: T): boolean; -} - -/** - * Creates a new UiRegistry (See js method for detailed documentation) - * The generic type T is the type of objects which are stored in the registry. - * The generic type A is an interface of accessors which depend on the - * fields of the objects stored in the registry. - * Example: if there is a string field "name" in type T, then A should be - * `{ byName: { [typeName: string]: T }; }` - */ -declare function uiRegistry( - spec: UIRegistrySpec -): { (): UIRegistry & A; register(privateModule: T): UIRegistry & A }; diff --git a/src/legacy/ui/public/registry/_registry.js b/src/legacy/ui/public/registry/_registry.js deleted file mode 100644 index 85aa1d9f2eca8..0000000000000 --- a/src/legacy/ui/public/registry/_registry.js +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { IndexedArray } from '../indexed_array'; - -const notPropsOptNames = IndexedArray.OPT_NAMES.concat('constructor', 'invokeProviders'); - -/** - * Create a registry, which is just a Private module provider. - * - * The registry allows modifying the values it will provide - * using the #register method. - * - * To access these modules, pass the registry to the Private - * module loader. - * - * # Examples - * - * + register a module - * ```js - * let registry = require('ui/registry/vis_types'); - * registry.add(function InjectablePrivateModule($http, Promise) { - * ... - * }) - * ``` - * - * + get all registered modules - * ```js - * let visTypes = Private(RegistryVisTypesProvider); - * ``` - * - * - * @param {object} [spec] - an object describing the properties of - * the registry to create. Any property specified - * that is not listed below will be mixed into the - * final IndexedArray object. - * - * # init - * @param {Function} [spec.constructor] - an injectable function that is called when - * the registry is first instantiated by the app. - * @param {boolean} [spec.filter] - function that will be used to filter items before - * registering them. Function will called on each item and - * should return true to keep the item (register it) or - * skip it (don't register it) - * - * # IndexedArray params - * @param {array[String]} [spec.index] - passed to the IndexedArray constructor - * @param {array[String]} [spec.group] - passed to the IndexedArray constructor - * @param {array[String]} [spec.order] - passed to the IndexedArray constructor - * @param {array[String]} [spec.initialSet] - passed to the IndexedArray constructor - * @param {array[String]} [spec.immutable] - passed to the IndexedArray constructor - * - * @return {[type]} [description] - */ -export function uiRegistry(spec) { - spec = spec || {}; - - const constructor = _.has(spec, 'constructor') && spec.constructor; - const filter = _.has(spec, 'filter') && spec.filter; - const invokeProviders = _.has(spec, 'invokeProviders') && spec.invokeProviders; - const iaOpts = _.defaults(_.pick(spec, IndexedArray.OPT_NAMES), { index: ['name'] }); - const props = _.omit(spec, notPropsOptNames); - const providers = []; - - let isInstantiated = false; - let getInvokedProviders; - let modules; - - /** - * This is the Private module that will be instantiated by - * - * @tag:PrivateModule - * @return {IndexedArray} - an indexed array containing the values - * that were registered, the registry spec - * defines how things will be indexed. - */ - const registry = function (Private, $injector) { - getInvokedProviders = function (newProviders) { - let set = invokeProviders - ? $injector.invoke(invokeProviders, undefined, { providers: newProviders }) - : newProviders.map(Private); - - if (filter && _.isFunction(filter)) { - set = set.filter((item) => filter(item)); - } - - return set; - }; - - iaOpts.initialSet = getInvokedProviders(providers); - - modules = new IndexedArray(iaOpts); - - // mixin other props - _.assign(modules, props); - - // construct - if (constructor) { - modules = $injector.invoke(constructor, modules) || modules; - } - - isInstantiated = true; - - return modules; - }; - - registry.displayName = '[registry ' + props.name + ']'; - - registry.register = function (privateModule) { - providers.push(privateModule); - - if (isInstantiated) { - const [provider] = getInvokedProviders([privateModule]); - - if (provider) { - modules.push(provider); - } - } - - return registry; - }; - - return registry; -} diff --git a/src/legacy/ui/public/registry/chrome_header_nav_controls.ts b/src/legacy/ui/public/registry/chrome_header_nav_controls.ts deleted file mode 100644 index 7e6c80692cd63..0000000000000 --- a/src/legacy/ui/public/registry/chrome_header_nav_controls.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IndexedArray } from '../indexed_array'; -import { uiRegistry, UIRegistry } from './_registry'; - -interface ChromeHeaderNavControlsRegistryAccessors { - bySide: { [typeName: string]: IndexedArray }; -} - -export enum NavControlSide { - Left = 'left', - Right = 'right', -} - -export interface NavControl { - name: string; - order: number; - side: NavControlSide; - render: (targetDomElement: HTMLDivElement) => () => void; -} - -export type ChromeHeaderNavControlsRegistry = UIRegistry & - ChromeHeaderNavControlsRegistryAccessors; - -export const chromeHeaderNavControlsRegistry = uiRegistry< - NavControl, - ChromeHeaderNavControlsRegistryAccessors ->({ - name: 'chromeHeaderNavControls', - order: ['order'], - group: ['side'], -}); diff --git a/src/legacy/ui/public/registry/field_format_editors.ts b/src/legacy/ui/public/registry/field_format_editors.ts deleted file mode 100644 index 5489ce9250e04..0000000000000 --- a/src/legacy/ui/public/registry/field_format_editors.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiRegistry } from './_registry'; - -export const RegistryFieldFormatEditorsProvider = uiRegistry({ - name: 'fieldFormatEditors', - index: ['formatId'], -}); diff --git a/src/legacy/ui/public/routes/__tests__/_route_manager.js b/src/legacy/ui/public/routes/__tests__/_route_manager.js deleted file mode 100644 index eb47a3e9ace70..0000000000000 --- a/src/legacy/ui/public/routes/__tests__/_route_manager.js +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import ngMock from 'ng_mock'; -import sinon from 'sinon'; -import RouteManager from '../route_manager'; -import expect from '@kbn/expect'; - -let routes; // will contain an new instance of RouteManager for each test -const chainableMethods = [ - { name: 'when', args: ['', {}] }, - { name: 'otherwise', args: [{}] }, - { name: 'defaults', args: [/regexp/, {}] }, -]; - -let $rp; -describe('routes/route_manager', function () { - beforeEach( - ngMock.module('kibana', function ($routeProvider) { - $rp = $routeProvider; - sinon.stub($rp, 'otherwise'); - sinon.stub($rp, 'when'); - }) - ); - - beforeEach( - ngMock.inject(function () { - routes = new RouteManager(); - }) - ); - - it('should have chainable methods: ' + _.map(chainableMethods, 'name').join(', '), function () { - chainableMethods.forEach(function (meth) { - expect(routes[meth.name].apply(routes, _.clone(meth.args))).to.be(routes); - }); - }); - - describe('#otherwise', function () { - it('should forward the last otherwise route', function () { - const otherRoute = {}; - routes.otherwise({}); - routes.otherwise(otherRoute); - - routes.config($rp); - - expect($rp.otherwise.callCount).to.be(1); - expect($rp.otherwise.getCall(0).args[0]).to.be(otherRoute); - }); - }); - - describe('#when', function () { - it('should merge the additions into the when() defined routes', function () { - routes.when('/some/route'); - routes.when('/some/other/route'); - - // add the addition resolve to every route - routes.defaults(/.*/, { - resolve: { - addition: function () {}, - }, - }); - - routes.config($rp); - - // should have run once for each when route - expect($rp.when.callCount).to.be(2); - expect($rp.otherwise.callCount).to.be(0); - - // every route should have the "addition" resolve - expect($rp.when.getCall(0).args[1].resolve.addition).to.be.a('function'); - expect($rp.when.getCall(1).args[1].resolve.addition).to.be.a('function'); - }); - }); - - describe('#config', function () { - it('should add defined routes to the global $routeProvider service in order', function () { - const args = [ - ['/one', {}], - ['/two', {}], - ]; - - args.forEach(function (a) { - routes.when(a[0], a[1]); - }); - - routes.config($rp); - - expect($rp.when.callCount).to.be(args.length); - _.times(args.length, function (i) { - const call = $rp.when.getCall(i); - const a = args.shift(); - - expect(call.args[0]).to.be(a[0]); - expect(call.args[1]).to.be(a[1]); - }); - }); - - it('sets route.reloadOnSearch to false by default', function () { - routes.when('/nothing-set'); - routes.when('/no-reload', { reloadOnSearch: false }); - routes.when('/always-reload', { reloadOnSearch: true }); - routes.config($rp); - - expect($rp.when.callCount).to.be(3); - expect($rp.when.firstCall.args[1]).to.have.property('reloadOnSearch', false); - expect($rp.when.secondCall.args[1]).to.have.property('reloadOnSearch', false); - expect($rp.when.lastCall.args[1]).to.have.property('reloadOnSearch', true); - }); - }); - - describe('#defaults()', () => { - it('adds defaults to routes with matching paths', () => { - routes.when('/foo', { name: 'foo' }); - routes.when('/bar', { name: 'bar' }); - routes.when('/baz', { name: 'baz' }); - routes.defaults(/^\/ba/, { - withDefaults: true, - }); - routes.config($rp); - - sinon.assert.calledWithExactly( - $rp.when, - '/foo', - sinon.match({ name: 'foo', withDefaults: undefined }) - ); - sinon.assert.calledWithExactly( - $rp.when, - '/bar', - sinon.match({ name: 'bar', withDefaults: true }) - ); - sinon.assert.calledWithExactly( - $rp.when, - '/baz', - sinon.match({ name: 'baz', withDefaults: true }) - ); - }); - - it('does not override values specified in the route', () => { - routes.when('/foo', { name: 'foo' }); - routes.defaults(/./, { name: 'bar' }); - routes.config($rp); - - sinon.assert.calledWithExactly($rp.when, '/foo', sinon.match({ name: 'foo' })); - }); - - // See https://github.com/elastic/kibana/issues/13294 - it('does not assign defaults by reference, to prevent accidentally merging unrelated defaults together', () => { - routes.when('/foo', { name: 'foo' }); - routes.when('/bar', { name: 'bar' }); - routes.when('/baz', { name: 'baz', funcs: { bazFunc() {} } }); - - // multiple defaults must be defined that, when applied correctly, will - // create a new object property on all routes that is unique to all of them - routes.defaults(/./, { funcs: { all() {} } }); - routes.defaults(/^\/foo/, { funcs: { fooFunc() {} } }); - routes.defaults(/^\/bar/, { funcs: { barFunc() {} } }); - routes.config($rp); - - sinon.assert.calledThrice($rp.when); - sinon.assert.calledWithExactly( - $rp.when, - '/foo', - sinon.match({ - name: 'foo', - funcs: sinon.match({ - all: sinon.match.func, - fooFunc: sinon.match.func, - barFunc: undefined, - bazFunc: undefined, - }), - }) - ); - sinon.assert.calledWithExactly( - $rp.when, - '/bar', - sinon.match({ - name: 'bar', - funcs: sinon.match({ - all: sinon.match.func, - fooFunc: undefined, - barFunc: sinon.match.func, - bazFunc: undefined, - }), - }) - ); - sinon.assert.calledWithExactly( - $rp.when, - '/baz', - sinon.match({ - name: 'baz', - funcs: sinon.match({ - all: sinon.match.func, - fooFunc: undefined, - barFunc: undefined, - bazFunc: sinon.match.func, - }), - }) - ); - }); - }); -}); diff --git a/src/legacy/ui/public/routes/__tests__/_work_queue.js b/src/legacy/ui/public/routes/__tests__/_work_queue.js deleted file mode 100644 index 72891f7321fbd..0000000000000 --- a/src/legacy/ui/public/routes/__tests__/_work_queue.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { WorkQueue } from '../work_queue'; -import sinon from 'sinon'; -import { createDefer } from 'ui/promises'; - -describe('work queue', function () { - let queue; - let Promise; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function (_Promise_) { - Promise = _Promise_; - }) - ); - beforeEach(function () { - queue = new WorkQueue(); - }); - afterEach(function () { - queue.empty(); - }); - - describe('#push', function () { - it('adds to the interval queue', function () { - queue.push(createDefer(Promise)); - expect(queue).to.have.length(1); - }); - }); - - describe('#resolveWhenFull', function () { - it('resolves requests waiting for the queue to fill when appropriate', function () { - const size = _.random(5, 50); - queue.limit = size; - - const whenFull = createDefer(Promise); - sinon.stub(whenFull, 'resolve'); - queue.resolveWhenFull(whenFull); - - // push all but one into the queue - _.times(size - 1, function () { - queue.push(createDefer(Promise)); - }); - - expect(whenFull.resolve.callCount).to.be(0); - queue.push(createDefer(Promise)); - expect(whenFull.resolve.callCount).to.be(1); - - queue.empty(); - }); - }); - - /** - * Fills the queue with a random number of work defers, but stubs all defer methods - * with the same stub and passed it back. - * - * @param {function} then - called with then(size, stub) so that the test - * can manipulate the filled queue - */ - function fillWithStubs(then) { - const size = _.random(5, 50); - const stub = sinon.stub(); - - _.times(size, function () { - const d = createDefer(Promise); - // overwrite the defer methods with the stub - d.resolve = stub; - d.reject = stub; - queue.push(d); - }); - - then(size, stub); - } - - describe('#doWork', function () { - it('flushes the queue and resolves all promises', function () { - fillWithStubs(function (size, stub) { - expect(queue).to.have.length(size); - queue.doWork(); - expect(queue).to.have.length(0); - expect(stub.callCount).to.be(size); - }); - }); - }); - - describe('#empty()', function () { - it('empties the internal queue WITHOUT resolving any promises', function () { - fillWithStubs(function (size, stub) { - expect(queue).to.have.length(size); - queue.empty(); - expect(queue).to.have.length(0); - expect(stub.callCount).to.be(0); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/routes/__tests__/_wrap_route_with_prep.js b/src/legacy/ui/public/routes/__tests__/_wrap_route_with_prep.js deleted file mode 100644 index 8ae85fce591a1..0000000000000 --- a/src/legacy/ui/public/routes/__tests__/_wrap_route_with_prep.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import RouteManager from '../route_manager'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; - -import _ from 'lodash'; -import '../../private'; - -let routes; - -describe('wrapRouteWithPrep fn', function () { - require('test_utils/no_digest_promises').activateForSuite(); - - beforeEach(function () { - routes = new RouteManager(); - }); - - const SchedulingTest = function (opts) { - opts = opts || {}; - - const delaySetup = opts.delayUserWork ? 0 : 50; - const delayUserWork = opts.delayUserWork ? 50 : 0; - - return function () { - ngMock.module('kibana'); - let setupComplete = false; - let userWorkComplete = false; - let route; - let Promise; - let $injector; - - ngMock.inject(function (_Promise_, _$injector_) { - Promise = _Promise_; - $injector = _$injector_; - }); - - routes.addSetupWork(function () { - return new Promise(function (resolve) { - setTimeout(function () { - setupComplete = true; - resolve(); - }, delaySetup); - }); - }); - - routes - .when('/', { - resolve: { - test: function () { - expect(setupComplete).to.be(true); - userWorkComplete = true; - }, - }, - }) - .config({ - when: function (p, _r) { - route = _r; - }, - }); - - return new Promise(function (resolve, reject) { - setTimeout(function () { - Promise.all( - _.map(route.resolve, function (fn) { - return $injector.invoke(fn); - }) - ) - .then(function () { - expect(setupComplete).to.be(true); - expect(userWorkComplete).to.be(true); - }) - .then(resolve, reject); - }, delayUserWork); - }); - }; - }; - - it('always waits for setup to complete before calling user work', new SchedulingTest()); - - it('does not call user work when setup fails', new SchedulingTest({ failSetup: true })); - - it( - 'calls all user work even if it is not initialized until after setup is complete', - new SchedulingTest({ - delayUserWork: false, - }) - ); -}); diff --git a/src/legacy/ui/public/routes/__tests__/index.js b/src/legacy/ui/public/routes/__tests__/index.js deleted file mode 100644 index 30cedaaca3d19..0000000000000 --- a/src/legacy/ui/public/routes/__tests__/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import './_route_manager'; -import './_work_queue'; -import './_wrap_route_with_prep'; -describe('Custom Route Management', function () {}); diff --git a/src/legacy/ui/public/routes/breadcrumbs.js b/src/legacy/ui/public/routes/breadcrumbs.js deleted file mode 100644 index 7917ffbd7c6e6..0000000000000 --- a/src/legacy/ui/public/routes/breadcrumbs.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { trim, startCase } from 'lodash'; - -/** - * Take a path (from $location.path() usually) and parse - * it's segments into a list of breadcrumbs - * - * @param {string} path - * @return {Array} - */ -export function parsePathToBreadcrumbs(path) { - return trim(path, '/') - .split('/') - .reduce( - (acc, id, i, parts) => [ - ...acc, - { - id, - display: startCase(id), - href: i === 0 ? `#/${id}` : `${acc[i - 1].href}/${id}`, - current: i === parts.length - 1, - }, - ], - [] - ); -} diff --git a/src/legacy/ui/public/routes/index.d.ts b/src/legacy/ui/public/routes/index.d.ts deleted file mode 100644 index 84e17b0a69452..0000000000000 --- a/src/legacy/ui/public/routes/index.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiRoutes, UIRoutes } from 'ui/routes/routes'; - -// eslint-disable-next-line import/no-default-export -export default uiRoutes; -export { UIRoutes }; diff --git a/src/legacy/ui/public/routes/index.js b/src/legacy/ui/public/routes/index.js deleted file mode 100644 index 9c826ebee1230..0000000000000 --- a/src/legacy/ui/public/routes/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { uiRoutes } from './routes'; - -// eslint-disable-next-line import/no-default-export -export default uiRoutes; diff --git a/src/legacy/ui/public/routes/route_manager.d.ts b/src/legacy/ui/public/routes/route_manager.d.ts deleted file mode 100644 index a5261a7c8ee3a..0000000000000 --- a/src/legacy/ui/public/routes/route_manager.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * WARNING: these types are incomplete - */ - -import { ChromeBreadcrumb } from '../../../../core/public'; - -export interface RouteConfiguration { - controller?: string | ((...args: any[]) => void); - redirectTo?: string; - resolveRedirectTo?: (...args: any[]) => void; - reloadOnSearch?: boolean; - reloadOnUrl?: boolean; - outerAngularWrapperRoute?: boolean; - resolve?: object; - template?: string; - k7Breadcrumbs?: (...args: any[]) => ChromeBreadcrumb[]; - requireUICapability?: string; -} - -interface RouteManager { - addSetupWork(cb: (...args: any[]) => void): void; - when(path: string, routeConfiguration: RouteConfiguration): RouteManager; - otherwise(routeConfiguration: RouteConfiguration): RouteManager; - defaults(path: string | RegExp, defaults: RouteConfiguration): RouteManager; -} - -// eslint-disable-next-line import/no-default-export -export default RouteManager; diff --git a/src/legacy/ui/public/routes/route_manager.js b/src/legacy/ui/public/routes/route_manager.js deleted file mode 100644 index de8a541d1c50a..0000000000000 --- a/src/legacy/ui/public/routes/route_manager.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { cloneDeep, defaultsDeep, wrap } from 'lodash'; - -import { wrapRouteWithPrep } from './wrap_route_with_prep'; -import { RouteSetupManager } from './route_setup_manager'; -import { parsePathToBreadcrumbs } from './breadcrumbs'; - -// eslint-disable-next-line import/no-default-export -export default function RouteManager() { - const self = this; - const setup = new RouteSetupManager(); - const when = []; - const defaults = []; - let otherwise; - - self.config = function ($routeProvider) { - when.forEach(function (args) { - const path = args[0]; - const route = args[1] || {}; - - defaults.forEach((def) => { - if (def.regex.test(path)) { - defaultsDeep(route, cloneDeep(def.value)); - } - }); - - if (route.reloadOnSearch == null) { - route.reloadOnSearch = false; - } - - wrapRouteWithPrep(route, setup); - $routeProvider.when(path, route); - }); - - if (otherwise) { - wrapRouteWithPrep(otherwise, setup); - $routeProvider.otherwise(otherwise); - } - }; - - self.run = function ($location, $route, $injector, $rootScope) { - if (window.elasticApm && typeof window.elasticApm.startTransaction === 'function') { - /** - * capture route-change events as transactions which happens after - * the browser's on load event. - * - * In Kibana app, this logic would run after the boostrap js files gets - * downloaded and get associated with the page-load transaction - */ - $rootScope.$on('$routeChangeStart', (_, nextRoute) => { - if (nextRoute.$$route) { - const name = nextRoute.$$route.originalPath; - window.elasticApm.startTransaction(name, 'route-change'); - } - }); - } - - self.getBreadcrumbs = () => { - const breadcrumbs = parsePathToBreadcrumbs($location.path()); - const map = $route.current.mapBreadcrumbs; - return map ? $injector.invoke(map, null, { breadcrumbs }) : breadcrumbs; - }; - }; - - const wrapSetupAndChain = (fn, ...args) => { - fn.apply(setup, args); - return this; - }; - - this.addSetupWork = wrap(setup.addSetupWork, wrapSetupAndChain); - this.afterSetupWork = wrap(setup.afterSetupWork, wrapSetupAndChain); - this.afterWork = wrap(setup.afterWork, wrapSetupAndChain); - - self.when = function (path, route) { - when.push([path, route]); - return self; - }; - - // before attaching the routes to the routeProvider, test the RE - // against the .when() path and add/override the resolves if there is a match - self.defaults = function (regex, value) { - defaults.push({ regex, value }); - return self; - }; - - self.otherwise = function (route) { - otherwise = route; - return self; - }; - - self.getBreadcrumbs = function () { - // overwritten in self.run(); - return []; - }; - - self.RouteManager = RouteManager; -} diff --git a/src/legacy/ui/public/routes/route_setup_manager.js b/src/legacy/ui/public/routes/route_setup_manager.js deleted file mode 100644 index a7a2f078f40fb..0000000000000 --- a/src/legacy/ui/public/routes/route_setup_manager.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { createDefer } from 'ui/promises'; - -// Throw this inside of an Angular route resolver after calling `kbnUrl.change` -// so that the $router can observe the $location update. Otherwise, the location -// route setup work will resolve before the route update occurs. -export const WAIT_FOR_URL_CHANGE_TOKEN = new Error('WAIT_FOR_URL_CHANGE_TOKEN'); - -export class RouteSetupManager { - constructor() { - this.setupWork = []; - this.onSetupComplete = []; - this.onSetupError = []; - this.onWorkComplete = []; - this.onWorkError = []; - } - - addSetupWork(fn) { - this.setupWork.push(fn); - } - - afterSetupWork(onComplete, onError) { - this.onSetupComplete.push(onComplete); - this.onSetupError.push(onError); - } - - afterWork(onComplete, onError) { - this.onWorkComplete.push(onComplete); - this.onWorkError.push(onError); - } - - /** - * Do each setupWork function by injecting it with angular dependencies - * and accepting promises from it. - * @return {[type]} [description] - */ - doWork(Promise, $injector, userWork) { - const invokeEach = (arr, locals) => { - return Promise.map(arr, (fn) => { - if (!fn) return; - return $injector.invoke(fn, null, locals); - }); - }; - - // call each error handler in order, until one of them resolves - // or we run out of handlers - const callErrorHandlers = (handlers, origError) => { - if (!_.size(handlers)) throw origError; - - // clone so we don't discard handlers or loose them - handlers = handlers.slice(0); - - const next = (err) => { - if (!handlers.length) throw err; - - const handler = handlers.shift(); - if (!handler) return next(err); - - return Promise.try(function () { - return $injector.invoke(handler, null, { err }); - }).catch(next); - }; - - return next(origError); - }; - - return invokeEach(this.setupWork) - .then( - () => invokeEach(this.onSetupComplete), - (err) => callErrorHandlers(this.onSetupError, err) - ) - .then(() => { - // wait for the queue to fill up, then do all the work - const defer = createDefer(Promise); - userWork.resolveWhenFull(defer); - - return defer.promise.then(() => Promise.all(userWork.doWork())); - }) - .catch((error) => { - if (error === WAIT_FOR_URL_CHANGE_TOKEN) { - // prevent moving forward, return a promise that never resolves - // so that the $router can observe the $location update - return Promise.halt(); - } - - throw error; - }) - .then( - () => invokeEach(this.onWorkComplete), - (err) => callErrorHandlers(this.onWorkError, err) - ); - } -} diff --git a/src/legacy/ui/public/routes/routes.d.ts b/src/legacy/ui/public/routes/routes.d.ts deleted file mode 100644 index d48230e9d56f9..0000000000000 --- a/src/legacy/ui/public/routes/routes.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import RouteManager from 'ui/routes/route_manager'; - -interface DefaultRouteManager extends RouteManager { - WAIT_FOR_URL_CHANGE_TOKEN: string; - enable(): void; -} - -export const uiRoutes: DefaultRouteManager; -export type UIRoutes = DefaultRouteManager; diff --git a/src/legacy/ui/public/routes/routes.js b/src/legacy/ui/public/routes/routes.js deleted file mode 100644 index 2cfb7a608e853..0000000000000 --- a/src/legacy/ui/public/routes/routes.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import RouteManager from './route_manager'; -import 'angular-route/angular-route'; -import { uiModules } from '../modules'; -import { WAIT_FOR_URL_CHANGE_TOKEN } from './route_setup_manager'; -const defaultRouteManager = new RouteManager(); - -export const uiRoutes = Object.create(defaultRouteManager, { - WAIT_FOR_URL_CHANGE_TOKEN: { - value: WAIT_FOR_URL_CHANGE_TOKEN, - }, - - enable: { - value() { - uiModules - .get('kibana', ['ngRoute']) - .config(defaultRouteManager.config) - .run(defaultRouteManager.run); - }, - }, -}); diff --git a/src/legacy/ui/public/routes/work_queue.js b/src/legacy/ui/public/routes/work_queue.js deleted file mode 100644 index 5c4c83663c590..0000000000000 --- a/src/legacy/ui/public/routes/work_queue.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export function WorkQueue() { - const q = this; - - const work = []; - const fullDefers = []; - - q.limit = 0; - Object.defineProperty(q, 'length', { - get: function () { - return work.length; - }, - }); - - const resolve = function (defers) { - return defers.splice(0).map(function (defer) { - return defer.resolve(); - }); - }; - - const checkIfFull = function () { - if (work.length >= q.limit && fullDefers.length) { - resolve(fullDefers); - } - }; - - q.resolveWhenFull = function (defer) { - fullDefers.push(defer); - checkIfFull(); - }; - - q.doWork = function () { - const resps = resolve(work); - checkIfFull(); - return resps; - }; - - q.empty = function () { - work.splice(0); - checkIfFull(); - }; - - q.push = function (defer) { - work.push(defer); - checkIfFull(); - }; -} diff --git a/src/legacy/ui/public/routes/wrap_route_with_prep.js b/src/legacy/ui/public/routes/wrap_route_with_prep.js deleted file mode 100644 index e9ed33148d9ac..0000000000000 --- a/src/legacy/ui/public/routes/wrap_route_with_prep.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import angular from 'angular'; -import _ from 'lodash'; -import { createDefer } from 'ui/promises'; -import { WorkQueue } from './work_queue'; - -export function wrapRouteWithPrep(route, setup) { - if (!route.resolve && route.redirectTo) return; - - const userWork = new WorkQueue(); - // the point at which we will consider the queue "full" - userWork.limit = _.keys(route.resolve).length; - - const resolve = { - __prep__: function ($injector) { - return $injector.invoke(setup.doWork, setup, { userWork }); - }, - }; - - // send each user resolve to the userWork queue, which will prevent it from running before the - // prep is complete - _.forOwn(route.resolve || {}, function (expr, name) { - resolve[name] = function ($injector, Promise) { - const defer = createDefer(Promise); - userWork.push(defer); - return defer.promise.then(function () { - return $injector[angular.isString(expr) ? 'get' : 'invoke'](expr); - }); - }; - }); - - // we're copied everything over so now overwrite - route.resolve = resolve; -} diff --git a/src/legacy/ui/public/state_management/__tests__/app_state.js b/src/legacy/ui/public/state_management/__tests__/app_state.js deleted file mode 100644 index c47fa3bb0417f..0000000000000 --- a/src/legacy/ui/public/state_management/__tests__/app_state.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { AppStateProvider } from '../app_state'; - -describe('State Management', function () { - let $rootScope; - let AppState; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function (_$rootScope_, _$location_, Private) { - $rootScope = _$rootScope_; - AppState = Private(AppStateProvider); - }) - ); - - describe('App State', function () { - let appState; - - beforeEach(function () { - appState = new AppState(); - }); - - it('should have _urlParam of _a', function () { - expect(appState).to.have.property('_urlParam'); - expect(appState._urlParam).to.equal('_a'); - }); - - it('should use passed in params', function () { - const params = { - test: true, - mock: false, - }; - - appState = new AppState(params); - expect(appState).to.have.property('_defaults'); - - Object.keys(params).forEach(function (key) { - expect(appState._defaults).to.have.property(key); - expect(appState._defaults[key]).to.equal(params[key]); - }); - }); - - it('should have a destroy method', function () { - expect(appState).to.have.property('destroy'); - }); - - it('should be destroyed on $routeChangeStart', function () { - const destroySpy = sinon.spy(appState, 'destroy'); - - $rootScope.$emit('$routeChangeStart'); - - expect(destroySpy.callCount).to.be(1); - }); - }); -}); diff --git a/src/legacy/ui/public/state_management/__tests__/config_provider.js b/src/legacy/ui/public/state_management/__tests__/config_provider.js deleted file mode 100644 index 9f756bc51dcb0..0000000000000 --- a/src/legacy/ui/public/state_management/__tests__/config_provider.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import '../config_provider'; - -describe('State Management Config', function () { - let stateManagementConfig; - - describe('is enabled', () => { - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function (_stateManagementConfig_) { - stateManagementConfig = _stateManagementConfig_; - }) - ); - - it('should be enabled by default', () => { - expect(stateManagementConfig.enabled).to.be(true); - }); - }); - - describe('can be disabled', () => { - beforeEach( - ngMock.module('kibana', function (stateManagementConfigProvider) { - stateManagementConfigProvider.disable(); - }) - ); - - beforeEach( - ngMock.inject(function (_stateManagementConfig_) { - stateManagementConfig = _stateManagementConfig_; - }) - ); - - it('is disabled by config', () => { - expect(stateManagementConfig.enabled).to.be(false); - }); - }); -}); diff --git a/src/legacy/ui/public/state_management/__tests__/global_state.js b/src/legacy/ui/public/state_management/__tests__/global_state.js deleted file mode 100644 index e9dae5880a8d1..0000000000000 --- a/src/legacy/ui/public/state_management/__tests__/global_state.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import '../global_state'; - -describe('State Management', function () { - let $location; - let state; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function (_$location_, globalState) { - $location = _$location_; - state = globalState; - }) - ); - - describe('Global State', function () { - it('should use previous state when not in URL', function () { - // set satte via URL - $location.search({ _g: '(foo:(bar:baz))' }); - state.fetch(); - expect(state.toObject()).to.eql({ foo: { bar: 'baz' } }); - - $location.search({ _g: '(fizz:buzz)' }); - state.fetch(); - expect(state.toObject()).to.eql({ fizz: 'buzz' }); - - $location.search({}); - state.fetch(); - expect(state.toObject()).to.eql({ fizz: 'buzz' }); - }); - }); -}); diff --git a/src/legacy/ui/public/state_management/__tests__/state.js b/src/legacy/ui/public/state_management/__tests__/state.js deleted file mode 100644 index b6c705e814509..0000000000000 --- a/src/legacy/ui/public/state_management/__tests__/state.js +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import { encode as encodeRison } from 'rison-node'; -import uiRoutes from 'ui/routes'; -import '../../private'; -import { toastNotifications } from '../../notify'; -import * as FatalErrorNS from '../../notify/fatal_error'; -import { StateProvider } from '../state'; -import { - unhashQuery, - createStateHash, - isStateHash, - HashedItemStore, -} from '../../../../../plugins/kibana_utils/public'; -import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; -import { EventsProvider } from '../../events'; - -describe('State Management', () => { - const sandbox = sinon.createSandbox(); - afterEach(() => sandbox.restore()); - - uiRoutes.enable(); - - describe('Enabled', () => { - let $rootScope; - let $location; - let Events; - let setup; - - beforeEach(ngMock.module('kibana')); - beforeEach( - ngMock.inject(function (_$rootScope_, _$location_, Private, config) { - const State = Private(StateProvider); - $location = _$location_; - $rootScope = _$rootScope_; - Events = Private(EventsProvider); - - setup = (opts) => { - const { param, initial, storeInHash } = opts || {}; - sinon.stub(config, 'get').withArgs('state:storeInSessionStorage').returns(!!storeInHash); - const store = new StubBrowserStorage(); - const hashedItemStore = new HashedItemStore(store); - const state = new State(param, initial, hashedItemStore); - - const getUnhashedSearch = () => unhashQuery($location.search()); - - return { store, hashedItemStore, state, getUnhashedSearch }; - }; - }) - ); - - describe('Provider', () => { - it('should reset the state to the defaults', () => { - const { state, getUnhashedSearch } = setup({ initial: { message: ['test'] } }); - state.reset(); - const search = getUnhashedSearch(state); - expect(search).to.have.property('_s'); - expect(search._s).to.equal('(message:!(test))'); - expect(state.message).to.eql(['test']); - }); - - it('should apply the defaults upon initialization', () => { - const { state } = setup({ initial: { message: 'test' } }); - expect(state).to.have.property('message', 'test'); - }); - - it('should inherit from Events', () => { - const { state } = setup(); - expect(state).to.be.an(Events); - }); - - it('should emit an event if reset with changes', (done) => { - const { state } = setup({ initial: { message: ['test'] } }); - state.on('reset_with_changes', (keys) => { - expect(keys).to.eql(['message']); - done(); - }); - state.save(); - state.message = 'foo'; - state.reset(); - $rootScope.$apply(); - }); - - it('should not emit an event if reset without changes', () => { - const { state } = setup({ initial: { message: 'test' } }); - state.on('reset_with_changes', () => { - expect().fail(); - }); - state.save(); - state.message = 'test'; - state.reset(); - $rootScope.$apply(); - }); - }); - - describe('Search', () => { - it('should save to $location.search()', () => { - const { state, getUnhashedSearch } = setup({ initial: { test: 'foo' } }); - state.save(); - const search = getUnhashedSearch(state); - expect(search).to.have.property('_s'); - expect(search._s).to.equal('(test:foo)'); - }); - - it('should emit an event if changes are saved', (done) => { - const { state, getUnhashedSearch } = setup(); - state.on('save_with_changes', (keys) => { - expect(keys).to.eql(['test']); - done(); - }); - state.test = 'foo'; - state.save(); - getUnhashedSearch(state); - $rootScope.$apply(); - }); - }); - - describe('Fetch', () => { - it('should emit an event if changes are fetched', (done) => { - const { state } = setup(); - state.on('fetch_with_changes', (keys) => { - expect(keys).to.eql(['foo']); - done(); - }); - $location.search({ _s: '(foo:bar)' }); - state.fetch(); - expect(state).to.have.property('foo', 'bar'); - $rootScope.$apply(); - }); - - it('should have events that attach to scope', (done) => { - const { state } = setup(); - state.on('test', (message) => { - expect(message).to.equal('foo'); - done(); - }); - state.emit('test', 'foo'); - $rootScope.$apply(); - }); - - it('should fire listeners for #onUpdate() on #fetch()', (done) => { - const { state } = setup(); - state.on('fetch_with_changes', (keys) => { - expect(keys).to.eql(['foo']); - done(); - }); - $location.search({ _s: '(foo:bar)' }); - state.fetch(); - expect(state).to.have.property('foo', 'bar'); - $rootScope.$apply(); - }); - - it('should apply defaults to fetches', () => { - const { state } = setup({ initial: { message: 'test' } }); - $location.search({ _s: '(foo:bar)' }); - state.fetch(); - expect(state).to.have.property('foo', 'bar'); - expect(state).to.have.property('message', 'test'); - }); - - it('should call fetch when $routeUpdate is fired on $rootScope', () => { - const { state } = setup(); - const spy = sinon.spy(state, 'fetch'); - $rootScope.$emit('$routeUpdate', 'test'); - sinon.assert.calledOnce(spy); - }); - - it('should clear state when missing form URL', () => { - let stateObj; - const { state } = setup(); - - // set state via URL - $location.search({ _s: '(foo:(bar:baz))' }); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({ foo: { bar: 'baz' } }); - - // ensure changing URL changes state - $location.search({ _s: '(one:two)' }); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({ one: 'two' }); - - // remove search, state should be empty - $location.search({}); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({}); - }); - - it('should clear state when it is invalid', () => { - let stateObj; - const { state } = setup(); - - $location.search({ _s: '' }); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({}); - - $location.search({ _s: '!n' }); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({}); - - $location.search({ _s: 'alert(1)' }); - state.fetch(); - stateObj = state.toObject(); - expect(stateObj).to.eql({}); - }); - - it('does not replace the state value on read', () => { - const { state } = setup(); - sinon.stub($location, 'search').callsFake((newSearch) => { - if (newSearch) { - return $location; - } else { - return { - [state.getQueryParamName()]: '(a:1)', - }; - } - }); - const replaceStub = sinon.stub($location, 'replace').returns($location); - - state.fetch(); - sinon.assert.notCalled(replaceStub); - }); - }); - - describe('Hashing', () => { - it('stores state values in a hashedItemStore, writing the hash to the url', () => { - const { state, hashedItemStore } = setup({ storeInHash: true }); - state.foo = 'bar'; - state.save(); - const urlVal = $location.search()[state.getQueryParamName()]; - - expect(isStateHash(urlVal)).to.be(true); - expect(hashedItemStore.getItem(urlVal)).to.eql(JSON.stringify({ foo: 'bar' })); - }); - - it('should replace rison in the URL with a hash', () => { - const { state, hashedItemStore } = setup({ storeInHash: true }); - const obj = { foo: { bar: 'baz' } }; - const rison = encodeRison(obj); - - $location.search({ _s: rison }); - state.fetch(); - - const urlVal = $location.search()._s; - expect(urlVal).to.not.be(rison); - expect(isStateHash(urlVal)).to.be(true); - expect(hashedItemStore.getItem(urlVal)).to.eql(JSON.stringify(obj)); - }); - - describe('error handling', () => { - it('notifies the user when a hash value does not map to a stored value', () => { - // Ideally, state.js shouldn't be tightly coupled to toastNotifications. Instead, it - // should notify its consumer of this error state and the consumer should be responsible - // for notifying the user of the error. This test verifies the side effect of the error - // until we can remove this coupling. - - // Clear existing toasts. - toastNotifications.list.splice(0); - - const { state } = setup({ storeInHash: true }); - const search = $location.search(); - const badHash = createStateHash('{"a": "b"}', () => null); - - search[state.getQueryParamName()] = badHash; - $location.search(search); - - expect(toastNotifications.list).to.have.length(0); - state.fetch(); - expect(toastNotifications.list).to.have.length(1); - expect(toastNotifications.list[0].title).to.match(/use the share functionality/i); - }); - - it.skip('triggers fatal error linking to github when setting item fails', () => { - // NOTE: this test needs to be written in jest and removed from the browser ones - // More info could be read in the opened issue: - // https://github.com/elastic/kibana/issues/22751 - const { state, hashedItemStore } = setup({ storeInHash: true }); - const fatalErrorStub = sandbox.stub(); - Object.defineProperty(FatalErrorNS, 'fatalError', { - writable: true, - value: fatalErrorStub, - }); - - sandbox.stub(hashedItemStore, 'setItem').returns(false); - state.toQueryParam(); - sinon.assert.calledOnce(fatalErrorStub); - sinon.assert.calledWith( - fatalErrorStub, - sinon.match((error) => error instanceof Error && error.message.includes('github.com')) - ); - }); - - it('translateHashToRison should gracefully fallback if parameter can not be parsed', () => { - const { state, hashedItemStore } = setup({ storeInHash: false }); - - expect(state.translateHashToRison('(a:b)')).to.be('(a:b)'); - expect(state.translateHashToRison('')).to.be(''); - - const existingHash = createStateHash('{"a": "b"}', () => null); - hashedItemStore.setItem(existingHash, '{"a": "b"}'); - - const nonExistingHash = createStateHash('{"b": "c"}', () => null); - - expect(state.translateHashToRison(existingHash)).to.be('(a:b)'); - expect(state.translateHashToRison(nonExistingHash)).to.be('!n'); - }); - }); - }); - }); - - describe('Disabled with persisted state', () => { - let state; - let $location; - let $rootScope; - const stateParam = '_config_test'; - - const getLocationState = () => { - const search = $location.search(); - return search[stateParam]; - }; - - beforeEach( - ngMock.module('kibana', function (stateManagementConfigProvider) { - stateManagementConfigProvider.disable(); - }) - ); - beforeEach( - ngMock.inject(function (_$rootScope_, _$location_, Private, config) { - const State = Private(StateProvider); - $location = _$location_; - $rootScope = _$rootScope_; - - sinon.stub(config, 'get').withArgs('state:storeInSessionStorage').returns(false); - - class MockPersistedState extends State { - _persistAcrossApps = true; - } - - MockPersistedState.prototype._persistAcrossApps = true; - - state = new MockPersistedState(stateParam); - }) - ); - - describe('changing state', () => { - const methods = ['save', 'replace', 'reset']; - - methods.forEach((method) => { - it(`${method} should not change the URL`, () => { - $location.search({ _s: '(foo:bar)' }); - state[method](); - $rootScope.$apply(); - expect(getLocationState()).to.be(undefined); - }); - }); - }); - - describe('reading state', () => { - it('should not change the URL', () => { - const saveSpy = sinon.spy(state, 'save'); - $location.search({ _s: '(foo:bar)' }); - state.fetch(); - $rootScope.$apply(); - sinon.assert.notCalled(saveSpy); - expect(getLocationState()).to.be(undefined); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/state_management/__tests__/state_monitor_factory.js b/src/legacy/ui/public/state_management/__tests__/state_monitor_factory.js deleted file mode 100644 index dc00d4e05e82f..0000000000000 --- a/src/legacy/ui/public/state_management/__tests__/state_monitor_factory.js +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import sinon from 'sinon'; -import { EventEmitter } from 'events'; -import { cloneDeep } from 'lodash'; -import { stateMonitorFactory } from '../state_monitor_factory'; - -describe('stateMonitorFactory', function () { - const noop = () => {}; - const eventTypes = ['save_with_changes', 'reset_with_changes', 'fetch_with_changes']; - - let mockState; - - function setState(mockState, obj, emit = true) { - mockState.toJSON = () => cloneDeep(obj); - if (emit) mockState.emit(eventTypes[0]); - } - - function createMockState(state = {}) { - const mockState = new EventEmitter(); - setState(mockState, state, false); - return mockState; - } - - beforeEach(() => { - mockState = createMockState({}); - }); - - it('should have a create method', function () { - expect(stateMonitorFactory).to.have.property('create'); - expect(stateMonitorFactory.create).to.be.a('function'); - }); - - describe('factory creation', function () { - it('should not call onChange with only the state', function () { - const monitor = stateMonitorFactory.create(mockState); - const changeStub = sinon.stub(); - monitor.onChange(changeStub); - sinon.assert.notCalled(changeStub); - }); - - it('should not call onChange with matching defaultState', function () { - const monitor = stateMonitorFactory.create(mockState, {}); - const changeStub = sinon.stub(); - monitor.onChange(changeStub); - sinon.assert.notCalled(changeStub); - }); - - it('should call onChange with differing defaultState', function () { - const monitor = stateMonitorFactory.create(mockState, { test: true }); - const changeStub = sinon.stub(); - monitor.onChange(changeStub); - sinon.assert.calledOnce(changeStub); - }); - }); - - describe('instance', function () { - let monitor; - - beforeEach(() => { - monitor = stateMonitorFactory.create(mockState); - }); - - describe('onChange', function () { - it('should throw if not given a handler function', function () { - const fn = () => monitor.onChange('not a function'); - expect(fn).to.throwException(/must be a function/); - }); - - eventTypes.forEach((eventType) => { - describe(`when ${eventType} is emitted`, function () { - let handlerFn; - - beforeEach(() => { - handlerFn = sinon.stub(); - monitor.onChange(handlerFn); - sinon.assert.notCalled(handlerFn); - }); - - it('should get called', function () { - mockState.emit(eventType); - sinon.assert.calledOnce(handlerFn); - }); - - it('should be given the state status', function () { - mockState.emit(eventType); - const args = handlerFn.firstCall.args; - expect(args[0]).to.be.an('object'); - }); - - it('should be given the event type', function () { - mockState.emit(eventType); - const args = handlerFn.firstCall.args; - expect(args[1]).to.equal(eventType); - }); - - it('should be given the changed keys', function () { - const keys = ['one', 'two', 'three']; - mockState.emit(eventType, keys); - const args = handlerFn.firstCall.args; - expect(args[2]).to.equal(keys); - }); - }); - }); - }); - - describe('ignoreProps', function () { - it('should not set status to dirty when ignored properties change', function () { - let status; - const mockState = createMockState({ messages: { world: 'hello', foo: 'bar' } }); - const monitor = stateMonitorFactory.create(mockState); - const changeStub = sinon.stub(); - monitor.ignoreProps('messages.world'); - monitor.onChange(changeStub); - sinon.assert.notCalled(changeStub); - - // update the ignored state prop - setState(mockState, { messages: { world: 'howdy', foo: 'bar' } }); - sinon.assert.calledOnce(changeStub); - status = changeStub.firstCall.args[0]; - expect(status).to.have.property('clean', true); - expect(status).to.have.property('dirty', false); - - // update a prop that is not ignored - setState(mockState, { messages: { world: 'howdy', foo: 'baz' } }); - sinon.assert.calledTwice(changeStub); - status = changeStub.secondCall.args[0]; - expect(status).to.have.property('clean', false); - expect(status).to.have.property('dirty', true); - }); - }); - - describe('setInitialState', function () { - let changeStub; - - beforeEach(() => { - changeStub = sinon.stub(); - monitor.onChange(changeStub); - sinon.assert.notCalled(changeStub); - }); - - it('should throw if no state is provided', function () { - const fn = () => monitor.setInitialState(); - expect(fn).to.throwException(/must be an object/); - }); - - it('should throw if given the wrong type', function () { - const fn = () => monitor.setInitialState([]); - expect(fn).to.throwException(/must be an object/); - }); - - it('should trigger the onChange handler', function () { - monitor.setInitialState({ new: 'state' }); - sinon.assert.calledOnce(changeStub); - }); - - it('should change the status with differing state', function () { - monitor.setInitialState({ new: 'state' }); - sinon.assert.calledOnce(changeStub); - - const status = changeStub.firstCall.args[0]; - expect(status).to.have.property('clean', false); - expect(status).to.have.property('dirty', true); - }); - - it('should not trigger the onChange handler without state change', function () { - monitor.setInitialState(cloneDeep(mockState.toJSON())); - sinon.assert.notCalled(changeStub); - }); - }); - - describe('status object', function () { - let handlerFn; - - beforeEach(() => { - handlerFn = sinon.stub(); - monitor.onChange(handlerFn); - }); - - it('should be clean by default', function () { - mockState.emit(eventTypes[0]); - const status = handlerFn.firstCall.args[0]; - expect(status).to.have.property('clean', true); - expect(status).to.have.property('dirty', false); - }); - - it('should be dirty when state changes', function () { - setState(mockState, { message: 'i am dirty now' }); - const status = handlerFn.firstCall.args[0]; - expect(status).to.have.property('clean', false); - expect(status).to.have.property('dirty', true); - }); - - it('should be clean when state is reset', function () { - const defaultState = { message: 'i am the original state' }; - const handlerFn = sinon.stub(); - - let status; - - // initial state and monitor setup - const mockState = createMockState(defaultState); - const monitor = stateMonitorFactory.create(mockState); - monitor.onChange(handlerFn); - sinon.assert.notCalled(handlerFn); - - // change the state and emit an event - setState(mockState, { message: 'i am dirty now' }); - sinon.assert.calledOnce(handlerFn); - status = handlerFn.firstCall.args[0]; - expect(status).to.have.property('clean', false); - expect(status).to.have.property('dirty', true); - - // reset the state and emit an event - setState(mockState, defaultState); - sinon.assert.calledTwice(handlerFn); - status = handlerFn.secondCall.args[0]; - expect(status).to.have.property('clean', true); - expect(status).to.have.property('dirty', false); - }); - }); - - describe('destroy', function () { - let stateSpy; - - beforeEach(() => { - stateSpy = sinon.spy(mockState, 'off'); - sinon.assert.notCalled(stateSpy); - }); - - it('should remove the listeners', function () { - monitor.onChange(noop); - monitor.destroy(); - sinon.assert.callCount(stateSpy, eventTypes.length); - eventTypes.forEach((eventType) => { - sinon.assert.calledWith(stateSpy, eventType); - }); - }); - - it('should stop the instance from being used any more', function () { - monitor.onChange(noop); - monitor.destroy(); - const fn = () => monitor.onChange(noop); - expect(fn).to.throwException(/has been destroyed/); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/state_management/app_state.d.ts b/src/legacy/ui/public/state_management/app_state.d.ts deleted file mode 100644 index ddd0b90710766..0000000000000 --- a/src/legacy/ui/public/state_management/app_state.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { State } from './state'; - -export class AppState extends State {} - -export type AppStateClass< - TAppState extends AppState = AppState, - TDefaults = { [key: string]: any } -> = new (defaults: TDefaults) => TAppState; diff --git a/src/legacy/ui/public/state_management/app_state.js b/src/legacy/ui/public/state_management/app_state.js deleted file mode 100644 index ec680d163b9da..0000000000000 --- a/src/legacy/ui/public/state_management/app_state.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * @name AppState - * - * @extends State - * - * @description Inherits State, which inherits Events. This class seems to be - * concerned with mapping "props" to PersistedState instances, and surfacing the - * ability to destroy those mappings. - */ - -import { uiModules } from '../modules'; -import { StateProvider } from './state'; -import { PersistedState } from '../../../../plugins/visualizations/public'; -import { createLegacyClass } from '../utils/legacy_class'; - -const urlParam = '_a'; - -export function AppStateProvider(Private, $location) { - const State = Private(StateProvider); - - let persistedStates; - let eventUnsubscribers; - - createLegacyClass(AppState).inherits(State); - function AppState(defaults) { - // Initialize persistedStates. This object maps "prop" names to - // PersistedState instances. These are used to make properties "stateful". - persistedStates = {}; - - // Initialize eventUnsubscribers. These will be called in `destroy`, to - // remove handlers for the 'change' and 'fetch_with_changes' events which - // are dispatched via the rootScope. - eventUnsubscribers = []; - - AppState.Super.call(this, urlParam, defaults); - AppState.getAppState._set(this); - } - - // if the url param is missing, write it back - AppState.prototype._persistAcrossApps = false; - - AppState.prototype.destroy = function () { - AppState.Super.prototype.destroy.call(this); - AppState.getAppState._set(null); - - eventUnsubscribers.forEach((listener) => listener()); - }; - - /** - * @returns PersistedState instance. - */ - AppState.prototype.makeStateful = function (prop) { - if (persistedStates[prop]) return persistedStates[prop]; - const self = this; - - // set up the ui state - persistedStates[prop] = new PersistedState(); - - // update the app state when the stateful instance changes - const updateOnChange = function () { - const replaceState = false; // TODO: debouncing logic - self[prop] = persistedStates[prop].getChanges(); - // Save state to the URL. - self.save(replaceState); - }; - const handlerOnChange = (method) => persistedStates[prop][method]('change', updateOnChange); - handlerOnChange('on'); - eventUnsubscribers.push(() => handlerOnChange('off')); - - // update the stateful object when the app state changes - const persistOnChange = function (changes) { - if (!changes) return; - - if (changes.indexOf(prop) !== -1) { - persistedStates[prop].set(self[prop]); - } - }; - const handlePersist = (method) => this[method]('fetch_with_changes', persistOnChange); - handlePersist('on'); - eventUnsubscribers.push(() => handlePersist('off')); - - // if the thing we're making stateful has an appState value, write to persisted state - if (self[prop]) persistedStates[prop].setSilent(self[prop]); - - return persistedStates[prop]; - }; - - AppState.getAppState = (function () { - let currentAppState; - - function get() { - return currentAppState; - } - - // Checks to see if the appState might already exist, even if it hasn't been newed up - get.previouslyStored = function () { - const search = $location.search(); - return search[urlParam] ? true : false; - }; - - get._set = function (current) { - currentAppState = current; - }; - - return get; - })(); - - return AppState; -} - -uiModules - .get('kibana/global_state') - .factory('AppState', function (Private) { - return Private(AppStateProvider); - }) - .service('getAppState', function (Private) { - return Private(AppStateProvider).getAppState; - }); diff --git a/src/legacy/ui/public/state_management/config_provider.js b/src/legacy/ui/public/state_management/config_provider.js deleted file mode 100644 index 68de449a57a56..0000000000000 --- a/src/legacy/ui/public/state_management/config_provider.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * @name stateManagementConfig - * - * @description Allows apps to configure state management - */ - -import { uiModules } from '../modules'; - -export class StateManagementConfigProvider { - _enabled = true; - - $get(/* inject stuff */) { - return { - enabled: this._enabled, - }; - } - - disable() { - this._enabled = false; - } - - enable() { - this._enabled = true; - } -} - -uiModules - .get('kibana/state_management') - .provider('stateManagementConfig', StateManagementConfigProvider); diff --git a/src/legacy/ui/public/state_management/global_state.d.ts b/src/legacy/ui/public/state_management/global_state.d.ts deleted file mode 100644 index 66a85d88956c7..0000000000000 --- a/src/legacy/ui/public/state_management/global_state.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { State } from './state'; - -export type GlobalState = State; diff --git a/src/legacy/ui/public/state_management/global_state.js b/src/legacy/ui/public/state_management/global_state.js deleted file mode 100644 index 0e8dfe40d5950..0000000000000 --- a/src/legacy/ui/public/state_management/global_state.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { StateProvider } from './state'; -import { uiModules } from '../modules'; -import { createLegacyClass } from '../utils/legacy_class'; - -const module = uiModules.get('kibana/global_state'); - -export function GlobalStateProvider(Private) { - const State = Private(StateProvider); - - createLegacyClass(GlobalState).inherits(State); - function GlobalState(defaults) { - GlobalState.Super.call(this, '_g', defaults); - } - - // if the url param is missing, write it back - GlobalState.prototype._persistAcrossApps = true; - - return new GlobalState(); -} - -module.service('globalState', function (Private) { - return Private(GlobalStateProvider); -}); diff --git a/src/legacy/ui/public/state_management/state.d.ts b/src/legacy/ui/public/state_management/state.d.ts deleted file mode 100644 index b31862b681f55..0000000000000 --- a/src/legacy/ui/public/state_management/state.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export class State { - [key: string]: any; - translateHashToRison: (stateHashOrRison: string | string[]) => string | string[]; - getQueryParamName: () => string; -} diff --git a/src/legacy/ui/public/state_management/state.js b/src/legacy/ui/public/state_management/state.js deleted file mode 100644 index d91834adb4a79..0000000000000 --- a/src/legacy/ui/public/state_management/state.js +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * @name State - * - * @extends Events - * - * @description Persists generic "state" to and reads it from the URL. - */ - -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; -import angular from 'angular'; -import rison from 'rison-node'; -import { EventsProvider } from '../events'; -import { fatalError, toastNotifications } from '../notify'; -import './config_provider'; -import { createLegacyClass } from '../utils/legacy_class'; -import { - hashedItemStore, - isStateHash, - createStateHash, - applyDiff, -} from '../../../../plugins/kibana_utils/public'; - -export function StateProvider( - Private, - $rootScope, - $location, - stateManagementConfig, - config, - kbnUrl, - $injector -) { - const Events = Private(EventsProvider); - - const isDummyRoute = () => - $injector.has('$route') && - $injector.get('$route').current && - $injector.get('$route').current.outerAngularWrapperRoute; - - createLegacyClass(State).inherits(Events); - function State(urlParam, defaults, _hashedItemStore = hashedItemStore) { - State.Super.call(this); - - this.setDefaults(defaults); - this._urlParam = urlParam || '_s'; - this._hashedItemStore = _hashedItemStore; - - // When the URL updates we need to fetch the values from the URL - this._cleanUpListeners = [ - // partial route update, no app reload - $rootScope.$on('$routeUpdate', () => { - this.fetch(); - }), - - // beginning of full route update, new app will be initialized before - // $routeChangeSuccess or $routeChangeError - $rootScope.$on('$routeChangeStart', () => { - if (!this._persistAcrossApps) { - this.destroy(); - } - }), - - $rootScope.$on('$routeChangeSuccess', () => { - if (this._persistAcrossApps) { - this.fetch(); - } - }), - ]; - - // Initialize the State with fetch - this.fetch(); - } - - State.prototype._readFromURL = function () { - const search = $location.search(); - const urlVal = search[this._urlParam]; - - if (!urlVal) { - return null; - } - - if (isStateHash(urlVal)) { - return this._parseStateHash(urlVal); - } - - let risonEncoded; - let unableToParse; - try { - risonEncoded = rison.decode(urlVal); - } catch (e) { - unableToParse = true; - } - - if (unableToParse) { - toastNotifications.addDanger( - i18n.translate('common.ui.stateManagement.unableToParseUrlErrorMessage', { - defaultMessage: 'Unable to parse URL', - }) - ); - search[this._urlParam] = this.toQueryParam(this._defaults); - $location.search(search).replace(); - } - - if (!risonEncoded) { - return null; - } - - if (this.isHashingEnabled()) { - // RISON can find its way into the URL any number of ways, including the navbar links or - // shared urls with the entire state embedded. These values need to be translated into - // hashes and replaced in the browser history when state-hashing is enabled - search[this._urlParam] = this.toQueryParam(risonEncoded); - $location.search(search).replace(); - } - - return risonEncoded; - }; - - /** - * Fetches the state from the url - * @returns {void} - */ - State.prototype.fetch = function () { - if (!stateManagementConfig.enabled) { - return; - } - - let stash = this._readFromURL(); - - // nothing to read from the url? save if ordered to persist, but only if it's not on a wrapper route - if (stash === null) { - if (this._persistAcrossApps) { - return this.save(); - } else { - stash = {}; - } - } - - _.defaults(stash, this._defaults); - // apply diff to state from stash, will change state in place via side effect - const diffResults = applyDiff(this, stash); - - if (!isDummyRoute() && diffResults.keys.length) { - this.emit('fetch_with_changes', diffResults.keys); - } - }; - - /** - * Saves the state to the url - * @returns {void} - */ - State.prototype.save = function (replace) { - if (!stateManagementConfig.enabled) { - return; - } - - if (isDummyRoute()) { - return; - } - - let stash = this._readFromURL(); - const state = this.toObject(); - replace = replace || false; - - if (!stash) { - replace = true; - stash = {}; - } - - // apply diff to state from stash, will change state in place via side effect - const diffResults = applyDiff(stash, _.defaults({}, state, this._defaults)); - - if (diffResults.keys.length) { - this.emit('save_with_changes', diffResults.keys); - } - - // persist the state in the URL - const search = $location.search(); - search[this._urlParam] = this.toQueryParam(state); - if (replace) { - $location.search(search).replace(); - } else { - $location.search(search); - } - }; - - /** - * Calls save with a forced replace - * @returns {void} - */ - State.prototype.replace = function () { - if (!stateManagementConfig.enabled) { - return; - } - - this.save(true); - }; - - /** - * Resets the state to the defaults - * - * @returns {void} - */ - State.prototype.reset = function () { - if (!stateManagementConfig.enabled) { - return; - } - - kbnUrl.removeParam(this.getQueryParamName()); - // apply diff to attributes from defaults, this is side effecting so - // it will change the state in place. - const diffResults = applyDiff(this, this._defaults); - if (diffResults.keys.length) { - this.emit('reset_with_changes', diffResults.keys); - } - this.save(); - }; - - /** - * Cleans up the state object - * @returns {void} - */ - State.prototype.destroy = function () { - this.off(); // removes all listeners - - // Removes the $routeUpdate listener - this._cleanUpListeners.forEach((listener) => listener(this)); - }; - - State.prototype.setDefaults = function (defaults) { - this._defaults = defaults || {}; - }; - - /** - * Parse the state hash to it's unserialized value. Hashes are restored - * to their pre-hashed state. - * - * @param {string} stateHash - state hash value from the query string. - * @return {any} - the stored value, or null if hash does not resolve. - */ - State.prototype._parseStateHash = function (stateHash) { - const json = this._hashedItemStore.getItem(stateHash); - if (json === null) { - toastNotifications.addDanger( - i18n.translate('common.ui.stateManagement.unableToRestoreUrlErrorMessage', { - defaultMessage: - 'Unable to completely restore the URL, be sure to use the share functionality.', - }) - ); - } - - return JSON.parse(json); - }; - - /** - * Lookup the value for a hash and return it's value in rison format or just - * return passed argument if it's not recognized as state hash. - * - * @param {string} stateHashOrRison - either state hash value or rison string. - * @return {string} rison - */ - State.prototype.translateHashToRison = function (stateHashOrRison) { - if (isStateHash(stateHashOrRison)) { - return rison.encode(this._parseStateHash(stateHashOrRison)); - } - - return stateHashOrRison; - }; - - State.prototype.isHashingEnabled = function () { - return !!config.get('state:storeInSessionStorage'); - }; - - /** - * Produce the hash version of the state in it's current position - * - * @return {string} - */ - State.prototype.toQueryParam = function (state = this.toObject()) { - if (!this.isHashingEnabled()) { - return rison.encode(state); - } - - // We need to strip out Angular-specific properties. - const json = angular.toJson(state); - const hash = createStateHash(json); - const isItemSet = this._hashedItemStore.setItem(hash, json); - - if (isItemSet) { - return hash; - } - - // If we ran out of space trying to persist the state, notify the user. - const message = i18n.translate( - 'common.ui.stateManagement.unableToStoreHistoryInSessionErrorMessage', - { - defaultMessage: - 'Kibana is unable to store history items in your session ' + - `because it is full and there don't seem to be items any items safe ` + - 'to delete.\n\n' + - 'This can usually be fixed by moving to a fresh tab, but could ' + - 'be caused by a larger issue. If you are seeing this message regularly, ' + - 'please file an issue at {gitHubIssuesUrl}.', - values: { gitHubIssuesUrl: 'https://github.com/elastic/kibana/issues' }, - } - ); - fatalError(new Error(message)); - }; - - /** - * Get the query string parameter name where this state writes and reads - * @return {string} - */ - State.prototype.getQueryParamName = function () { - return this._urlParam; - }; - - /** - * Returns an object with each property name and value corresponding to the entries in this collection - * excluding fields started from '$', '_' and all methods - * - * @return {object} - */ - State.prototype.toObject = function () { - return _.omitBy(this, (value, key) => { - return key.charAt(0) === '$' || key.charAt(0) === '_' || _.isFunction(value); - }); - }; - - /** Alias for method 'toObject' - * - * @obsolete Please use 'toObject' method instead - * @return {object} - */ - State.prototype.toJSON = function () { - return this.toObject(); - }; - - return State; -} diff --git a/src/legacy/ui/public/state_management/state_monitor_factory.ts b/src/legacy/ui/public/state_management/state_monitor_factory.ts deleted file mode 100644 index 968ececfe3be5..0000000000000 --- a/src/legacy/ui/public/state_management/state_monitor_factory.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { set } from '@elastic/safer-lodash-set'; -import { cloneDeep, isEqual, isPlainObject } from 'lodash'; -import { State } from './state'; - -export const stateMonitorFactory = { - create: ( - state: State, - customInitialState: TStateDefault - ) => stateMonitor(state, customInitialState), -}; - -interface StateStatus { - clean: boolean; - dirty: boolean; -} - -export interface StateMonitor { - setInitialState: (innerCustomInitialState: TStateDefault) => void; - ignoreProps: (props: string[] | string) => void; - onChange: (callback: ChangeHandlerFn) => StateMonitor; - destroy: () => void; -} - -type ChangeHandlerFn = (status: StateStatus, type: string | null, keys: string[]) => void; - -function stateMonitor( - state: State, - customInitialState: TStateDefault -): StateMonitor { - let destroyed = false; - let ignoredProps: string[] = []; - let changeHandlers: ChangeHandlerFn[] | undefined = []; - let initialState: TStateDefault; - - setInitialState(customInitialState); - - function setInitialState(innerCustomInitialState: TStateDefault) { - // state.toJSON returns a reference, clone so we can mutate it safely - initialState = cloneDeep(innerCustomInitialState) || cloneDeep(state.toJSON()); - } - - function removeIgnoredProps(innerState: TStateDefault) { - ignoredProps.forEach((path) => { - set(innerState, path, true); - }); - return innerState; - } - - function getStatus(): StateStatus { - // state.toJSON returns a reference, clone so we can mutate it safely - const currentState = removeIgnoredProps(cloneDeep(state.toJSON())); - const isClean = isEqual(currentState, initialState); - - return { - clean: isClean, - dirty: !isClean, - }; - } - - function dispatchChange(type: string | null = null, keys: string[] = []) { - const status = getStatus(); - if (!changeHandlers) { - throw new Error('Change handlers is undefined, this object has been destroyed'); - } - changeHandlers.forEach((changeHandler) => { - changeHandler(status, type, keys); - }); - } - - function dispatchFetch(keys: string[]) { - dispatchChange('fetch_with_changes', keys); - } - - function dispatchSave(keys: string[]) { - dispatchChange('save_with_changes', keys); - } - - function dispatchReset(keys: string[]) { - dispatchChange('reset_with_changes', keys); - } - - return { - setInitialState(innerCustomInitialState: TStateDefault) { - if (!isPlainObject(innerCustomInitialState)) { - throw new TypeError('The default state must be an object'); - } - - // check the current status - const previousStatus = getStatus(); - - // update the initialState and apply ignoredProps - setInitialState(innerCustomInitialState); - removeIgnoredProps(initialState); - - // fire the change handler if the status has changed - if (!isEqual(previousStatus, getStatus())) { - dispatchChange(); - } - }, - - ignoreProps(props: string[] | string) { - ignoredProps = ignoredProps.concat(props); - removeIgnoredProps(initialState); - return this; - }, - - onChange(callback: ChangeHandlerFn) { - if (destroyed || !changeHandlers) { - throw new Error('Monitor has been destroyed'); - } - if (typeof callback !== 'function') { - throw new Error('onChange handler must be a function'); - } - - changeHandlers.push(callback); - - // Listen for state events. - state.on('fetch_with_changes', dispatchFetch); - state.on('save_with_changes', dispatchSave); - state.on('reset_with_changes', dispatchReset); - - // if the state is already dirty, fire the change handler immediately - const status = getStatus(); - if (status.dirty) { - dispatchChange(); - } - - return this; - }, - - destroy() { - destroyed = true; - changeHandlers = undefined; - state.off('fetch_with_changes', dispatchFetch); - state.off('save_with_changes', dispatchSave); - state.off('reset_with_changes', dispatchReset); - }, - }; -} diff --git a/src/legacy/ui/public/system_api/__tests__/system_api.js b/src/legacy/ui/public/system_api/__tests__/system_api.js deleted file mode 100644 index 816024f13f8b2..0000000000000 --- a/src/legacy/ui/public/system_api/__tests__/system_api.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; -import { - addSystemApiHeader, - isSystemApiRequest, -} from '../../../../../plugins/kibana_legacy/public'; - -describe('system_api', () => { - describe('#addSystemApiHeader', () => { - it('adds the correct system API header', () => { - const headers = { - 'kbn-version': '4.6.0', - }; - const newHeaders = addSystemApiHeader(headers); - - expect(newHeaders).to.have.property('kbn-system-request'); - expect(newHeaders['kbn-system-request']).to.be(true); - - expect(newHeaders).to.have.property('kbn-version'); - expect(newHeaders['kbn-version']).to.be('4.6.0'); - }); - }); - - describe('#isSystemApiRequest', () => { - it('returns true for a system HTTP request', () => { - const mockRequest = { - headers: { - 'kbn-system-request': true, - }, - }; - expect(isSystemApiRequest(mockRequest)).to.be(true); - }); - - it('returns true for a legacy system API HTTP request', () => { - const mockRequest = { - headers: { - 'kbn-system-api': true, - }, - }; - expect(isSystemApiRequest(mockRequest)).to.be(true); - }); - - it('returns false for a non-system API HTTP request', () => { - const mockRequest = { - headers: {}, - }; - expect(isSystemApiRequest(mockRequest)).to.be(false); - }); - }); -}); diff --git a/src/legacy/ui/public/system_api/index.js b/src/legacy/ui/public/system_api/index.js deleted file mode 100644 index 6361c0ea6c69a..0000000000000 --- a/src/legacy/ui/public/system_api/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { addSystemApiHeader, isSystemApiRequest } from '../../../../plugins/kibana_legacy/public'; diff --git a/src/legacy/ui/public/timefilter/index.ts b/src/legacy/ui/public/timefilter/index.ts deleted file mode 100644 index 83795c73112be..0000000000000 --- a/src/legacy/ui/public/timefilter/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import uiRoutes from 'ui/routes'; -import { npStart } from 'ui/new_platform'; - -import { TimefilterContract, TimeHistoryContract } from '../../../../plugins/data/public'; -import { registerTimefilterWithGlobalState } from './setup_router'; - -export { - getTime, - InputTimeRange, - TimeHistoryContract, - TimefilterContract, -} from '../../../../plugins/data/public'; -export type Timefilter = TimefilterContract; -export type TimeHistory = TimeHistoryContract; -export const timeHistory = npStart.plugins.data.query.timefilter.history; -export const timefilter = npStart.plugins.data.query.timefilter.timefilter; - -uiRoutes.addSetupWork((globalState, $rootScope) => { - return registerTimefilterWithGlobalState(timefilter, globalState, $rootScope); -}); diff --git a/src/legacy/ui/public/timefilter/setup_router.test.js b/src/legacy/ui/public/timefilter/setup_router.test.js deleted file mode 100644 index 2ae9a053ae2db..0000000000000 --- a/src/legacy/ui/public/timefilter/setup_router.test.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import { registerTimefilterWithGlobalState } from './setup_router'; - -jest.mock('../../../../plugins/kibana_legacy/public', () => ({ - subscribeWithScope: jest.fn(), -})); - -describe('registerTimefilterWithGlobalState()', () => { - it('should always use iso8601 strings', async () => { - const setTime = jest.fn(); - const timefilter = { - setTime, - setRefreshInterval: jest.fn(), - getRefreshIntervalUpdate$: jest.fn(), - getTimeUpdate$: jest.fn(), - }; - - const globalState = { - time: { - from: '2017-09-07T20:12:04.011Z', - to: '2017-09-07T20:18:55.733Z', - }, - on: (eventName, callback) => { - callback(); - }, - }; - - const rootScope = { - $on: jest.fn(), - }; - - registerTimefilterWithGlobalState(timefilter, globalState, rootScope); - - expect(setTime.mock.calls.length).toBe(2); - expect(setTime.mock.calls[1][0]).toEqual(globalState.time); - }); -}); diff --git a/src/legacy/ui/public/timefilter/setup_router.ts b/src/legacy/ui/public/timefilter/setup_router.ts deleted file mode 100644 index 7c25c6aa3166e..0000000000000 --- a/src/legacy/ui/public/timefilter/setup_router.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { IScope } from 'angular'; -import moment from 'moment'; -import chrome from 'ui/chrome'; -import { Subscription } from 'rxjs'; -import { fatalError } from 'ui/notify/fatal_error'; -import { subscribeWithScope } from '../../../../plugins/kibana_legacy/public'; -import { - RefreshInterval, - TimeRange, - TimefilterContract, - UI_SETTINGS, -} from '../../../../plugins/data/public'; - -// TODO -// remove everything underneath once globalState is no longer an angular service -// and listener can be registered without angular. -function convertISO8601(stringTime: string): string { - const obj = moment(stringTime, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true); - return obj.isValid() ? obj.toISOString() : stringTime; -} - -export function getTimefilterConfig() { - const settings = chrome.getUiSettingsClient(); - return { - timeDefaults: settings.get('timepicker:timeDefaults'), - refreshIntervalDefaults: settings.get(UI_SETTINGS.TIMEPICKER_REFRESH_INTERVAL_DEFAULTS), - }; -} - -export const registerTimefilterWithGlobalStateFactory = ( - timefilter: TimefilterContract, - globalState: any, - $rootScope: IScope -) => { - // settings have to be re-fetched here, to make sure that settings changed by overrideLocalDefault are taken into account. - const config = getTimefilterConfig(); - timefilter.setTime(_.defaults(globalState.time || {}, config.timeDefaults)); - timefilter.setRefreshInterval( - _.defaults(globalState.refreshInterval || {}, config.refreshIntervalDefaults) - ); - - globalState.on('fetch_with_changes', () => { - // clone and default to {} in one - const newTime: TimeRange = _.defaults({}, globalState.time, config.timeDefaults); - const newRefreshInterval: RefreshInterval = _.defaults( - {}, - globalState.refreshInterval, - config.refreshIntervalDefaults - ); - - if (newTime) { - if (newTime.to) newTime.to = convertISO8601(newTime.to); - if (newTime.from) newTime.from = convertISO8601(newTime.from); - } - - timefilter.setTime(newTime); - timefilter.setRefreshInterval(newRefreshInterval); - }); - - const updateGlobalStateWithTime = () => { - globalState.time = timefilter.getTime(); - globalState.refreshInterval = timefilter.getRefreshInterval(); - globalState.save(); - }; - - const subscriptions = new Subscription(); - subscriptions.add( - subscribeWithScope( - $rootScope, - timefilter.getRefreshIntervalUpdate$(), - { - next: updateGlobalStateWithTime, - }, - fatalError - ) - ); - - subscriptions.add( - subscribeWithScope( - $rootScope, - timefilter.getTimeUpdate$(), - { - next: updateGlobalStateWithTime, - }, - fatalError - ) - ); - - $rootScope.$on('$destroy', () => { - subscriptions.unsubscribe(); - }); -}; - -// Currently some parts of Kibana (index patterns, timefilter) rely on addSetupWork in the uiRouter -// and require it to be executed to properly function. -// This function is exposed for applications that do not use uiRoutes like APM -// Kibana issue https://github.com/elastic/kibana/issues/19110 tracks the removal of this dependency on uiRouter -export const registerTimefilterWithGlobalState = _.once(registerTimefilterWithGlobalStateFactory); diff --git a/src/legacy/ui/public/url/__tests__/extract_app_path_and_id.js b/src/legacy/ui/public/url/__tests__/extract_app_path_and_id.js deleted file mode 100644 index 965e8f4bc9f38..0000000000000 --- a/src/legacy/ui/public/url/__tests__/extract_app_path_and_id.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; - -import { extractAppPathAndId } from '../extract_app_path_and_id'; - -describe('extractAppPathAndId', function () { - describe('from an absolute url with a base path', function () { - describe('with a base path', function () { - const basePath = '/gza'; - const absoluteUrl = 'http://www.test.com:5601/gza/app/appId#appPathIsHere?query=here'; - it('extracts app path', function () { - expect(extractAppPathAndId(absoluteUrl, basePath).appPath).to.be( - 'appPathIsHere?query=here' - ); - }); - - it('extracts app id', function () { - expect(extractAppPathAndId(absoluteUrl, basePath).appId).to.be('appId'); - }); - - it('returns an empty object when there is no app path', function () { - const appPathAndId = extractAppPathAndId('http://www.test.com:5601/gza/noapppath'); - expect(appPathAndId.appId).to.be(undefined); - expect(appPathAndId.appPath).to.be(undefined); - }); - }); - - describe('without a base path', function () { - const absoluteUrl = 'http://www.test.com:5601/app/appId#appPathIsHere?query=here'; - it('extracts app path', function () { - expect(extractAppPathAndId(absoluteUrl).appPath).to.be('appPathIsHere?query=here'); - }); - - it('extracts app id', function () { - expect(extractAppPathAndId(absoluteUrl).appId).to.be('appId'); - }); - - it('returns an empty object when there is no app path', function () { - const appPathAndId = extractAppPathAndId('http://www.test.com:5601/noapppath'); - expect(appPathAndId.appId).to.be(undefined); - expect(appPathAndId.appPath).to.be(undefined); - }); - }); - - describe('when appPath is empty', function () { - const absoluteUrl = 'http://www.test.com:5601/app/appId'; - it('extracts app id', function () { - expect(extractAppPathAndId(absoluteUrl).appId).to.be('appId'); - }); - it('extracts empty appPath', function () { - expect(extractAppPathAndId(absoluteUrl).appPath).to.be(''); - }); - }); - }); - - describe('from a root relative url', function () { - describe('with a base path', function () { - const basePath = '/gza'; - const rootRelativePath = '/gza/app/appId#appPathIsHere?query=here'; - it('extracts app path', function () { - expect(extractAppPathAndId(rootRelativePath, basePath).appPath).to.be( - 'appPathIsHere?query=here' - ); - }); - - it('extracts app id', function () { - expect(extractAppPathAndId(rootRelativePath, basePath).appId).to.be('appId'); - }); - - it('returns an empty object when there is no app path', function () { - const appPathAndId = extractAppPathAndId('/gza/notformattedright'); - expect(appPathAndId.appId).to.be(undefined); - expect(appPathAndId.appPath).to.be(undefined); - }); - }); - - describe('without a base path', function () { - const rootRelativePath = '/app/appId#appPathIsHere?query=here'; - it('extracts app path', function () { - expect(extractAppPathAndId(rootRelativePath).appPath).to.be('appPathIsHere?query=here'); - }); - - it('extracts app id', function () { - expect(extractAppPathAndId(rootRelativePath).appId).to.be('appId'); - }); - - it('returns an empty object when there is no app path', function () { - const appPathAndId = extractAppPathAndId('/notformattedright'); - expect(appPathAndId.appId).to.be(undefined); - expect(appPathAndId.appPath).to.be(undefined); - }); - }); - - describe('when appPath is empty', function () { - const rootRelativePath = '/app/appId'; - it('extracts app id', function () { - expect(extractAppPathAndId(rootRelativePath).appId).to.be('appId'); - }); - it('extracts empty appPath', function () { - expect(extractAppPathAndId(rootRelativePath).appPath).to.be(''); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/url/__tests__/kibana_parsed_url.js b/src/legacy/ui/public/url/__tests__/kibana_parsed_url.js deleted file mode 100644 index 6ea199c3d22cc..0000000000000 --- a/src/legacy/ui/public/url/__tests__/kibana_parsed_url.js +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; - -import { KibanaParsedUrl } from '../kibana_parsed_url'; - -describe('KibanaParsedUrl', function () { - it('getHashedAppPath', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/hi', - appId: 'bye', - appPath: 'visualize?hi=there&bye', - }); - expect(kibanaParsedUrl.getHashedAppPath()).to.be('#visualize?hi=there&bye'); - }); - - it('getAppRootPath', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/hi', - appId: 'appId', - appPath: 'dashboard?edit=123', - }); - expect(kibanaParsedUrl.getAppRootPath()).to.be('/app/appId#dashboard?edit=123'); - }); - - describe('when basePath is specified', function () { - it('getRootRelativePath', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/base', - appId: 'appId', - appPath: 'visualize?hi=there&bye', - }); - expect(kibanaParsedUrl.getRootRelativePath()).to.be('/base/app/appId#visualize?hi=there&bye'); - }); - - describe('getAbsolutePath', function () { - const protocol = 'http'; - const hostname = 'www.test.com'; - const port = '5601'; - - it('returns the absolute url when there is a port', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/base', - appId: 'appId', - appPath: 'visualize?hi=there&bye', - hostname, - protocol, - port, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be( - 'http://www.test.com:5601/base/app/appId#visualize?hi=there&bye' - ); - }); - - it('returns the absolute url when there are no query parameters', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/base', - appId: 'appId', - appPath: 'visualize', - hostname, - protocol, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be( - 'http://www.test.com/base/app/appId#visualize' - ); - }); - - it('returns the absolute url when the are query parameters', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - basePath: '/base', - appId: 'appId', - appPath: 'visualize?hi=bye&tata', - hostname, - protocol, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be( - 'http://www.test.com/base/app/appId#visualize?hi=bye&tata' - ); - }); - }); - }); - - describe('when basePath is not specified', function () { - it('getRootRelativePath', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - appId: 'appId', - appPath: 'visualize?hi=there&bye', - }); - expect(kibanaParsedUrl.getRootRelativePath()).to.be('/app/appId#visualize?hi=there&bye'); - }); - - describe('getAbsolutePath', function () { - const protocol = 'http'; - const hostname = 'www.test.com'; - const port = '5601'; - - it('returns the absolute url when there is a port', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - appId: 'appId', - appPath: 'visualize?hi=there&bye', - hostname, - protocol, - port, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be( - 'http://www.test.com:5601/app/appId#visualize?hi=there&bye' - ); - }); - - it('returns the absolute url when there are no query parameters', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - appId: 'appId', - appPath: 'visualize', - hostname, - protocol, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be('http://www.test.com/app/appId#visualize'); - }); - - it('returns the absolute url when there are query parameters', function () { - const kibanaParsedUrl = new KibanaParsedUrl({ - appId: 'appId', - appPath: 'visualize?hi=bye&tata', - hostname, - protocol, - }); - expect(kibanaParsedUrl.getAbsoluteUrl()).to.be( - 'http://www.test.com/app/appId#visualize?hi=bye&tata' - ); - }); - }); - }); - - describe('getGlobalState', function () { - const basePath = '/xyz'; - const appId = 'myApp'; - - it('returns an empty string when the KibanaParsedUrl is in an invalid state', function () { - const url = new KibanaParsedUrl({ basePath }); - expect(url.getGlobalState()).to.be(''); - }); - - it('returns an empty string when there is no global state', function () { - const url = new KibanaParsedUrl({ basePath, appId, appPath: '/hi?notg=something' }); - expect(url.getGlobalState()).to.be(''); - }); - - it('returns the global state when it is the last parameter', function () { - const url = new KibanaParsedUrl({ - basePath, - appId, - appPath: '/hi?notg=something&_g=(thisismyglobalstate)', - }); - expect(url.getGlobalState()).to.be('(thisismyglobalstate)'); - }); - - it('returns the global state when it is the first parameter', function () { - const url = new KibanaParsedUrl({ - basePath, - appId, - appPath: '/hi?_g=(thisismyglobalstate)&hi=bye', - }); - expect(url.getGlobalState()).to.be('(thisismyglobalstate)'); - }); - }); - - describe('setGlobalState', function () { - const basePath = '/xyz'; - const appId = 'myApp'; - - it('does nothing when KibanaParsedUrl is in an invalid state', function () { - const url = new KibanaParsedUrl({ basePath }); - url.setGlobalState('newglobalstate'); - expect(url.getGlobalState()).to.be(''); - }); - - it('clears the global state when setting it to an empty string', function () { - const url = new KibanaParsedUrl({ basePath, appId, appPath: '/hi?_g=globalstate' }); - url.setGlobalState(''); - expect(url.getGlobalState()).to.be(''); - }); - - it('updates the global state when a string is passed in', function () { - const url = new KibanaParsedUrl({ - basePath, - appId, - appPath: '/hi?notg=something&_g=oldstate', - }); - url.setGlobalState('newstate'); - expect(url.getGlobalState()).to.be('newstate'); - }); - - it('adds the global state parameters if it did not exist before', function () { - const url = new KibanaParsedUrl({ basePath, appId, appPath: '/hi' }); - url.setGlobalState('newstate'); - expect(url.getGlobalState()).to.be('newstate'); - expect(url.appPath).to.be('/hi?_g=newstate'); - }); - }); -}); diff --git a/src/legacy/ui/public/url/__tests__/prepend_path.js b/src/legacy/ui/public/url/__tests__/prepend_path.js deleted file mode 100644 index 36991b77553e4..0000000000000 --- a/src/legacy/ui/public/url/__tests__/prepend_path.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from '@kbn/expect'; - -import { prependPath } from '../prepend_path'; - -describe('url prependPath', function () { - describe('returns the relative path unchanged', function () { - it('if it is null', function () { - expect(prependPath(null, 'kittens')).to.be(null); - }); - - it('if it is undefined', function () { - expect(prependPath(undefined, 'kittens')).to.be(undefined); - }); - - it('if it is an absolute url', function () { - expect(prependPath('http://www.hithere.com/howareyou', 'welcome')).to.be( - 'http://www.hithere.com/howareyou' - ); - }); - - it('if it does not start with a /', function () { - expect(prependPath('are/so/cool', 'cats')).to.be('are/so/cool'); - }); - - it('when new path is empty', function () { - expect(prependPath('/are/so/cool', '')).to.be('/are/so/cool'); - }); - - it('when it is only a slash and new path is empty', function () { - expect(prependPath('/', '')).to.be('/'); - }); - }); - - describe('returns an updated relative path', function () { - it('when it starts with a slash', function () { - expect(prependPath('/are/so/cool', 'dinosaurs')).to.be('dinosaurs/are/so/cool'); - }); - - it('when new path starts with a slash', function () { - expect(prependPath('/are/so/cool', '/fish')).to.be('/fish/are/so/cool'); - }); - - it('with two slashes if new path is a slash', function () { - expect(prependPath('/are/so/cool', '/')).to.be('//are/so/cool'); - }); - - it('when there is a slash on the end', function () { - expect(prependPath('/are/delicious/', 'lollipops')).to.be('lollipops/are/delicious/'); - }); - - it('when pathname that ends with a file', function () { - expect(prependPath('/are/delicious/index.html', 'donuts')).to.be( - 'donuts/are/delicious/index.html' - ); - }); - - it('when it is only a slash', function () { - expect(prependPath('/', 'kittens')).to.be('kittens/'); - }); - }); -}); diff --git a/src/legacy/ui/public/url/__tests__/url.js b/src/legacy/ui/public/url/__tests__/url.js deleted file mode 100644 index 8b173482e1bb4..0000000000000 --- a/src/legacy/ui/public/url/__tests__/url.js +++ /dev/null @@ -1,562 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import sinon from 'sinon'; -import expect from '@kbn/expect'; -import ngMock from 'ng_mock'; -import faker from 'faker'; -import _ from 'lodash'; -import { AppStateProvider } from '../../state_management/app_state'; -import '..'; - -// global vars, injected and mocked in init() -let kbnUrl; -let $route; -let $location; -let $rootScope; -let appState; - -class StubAppState { - constructor() { - this.getQueryParamName = () => '_a'; - this.toQueryParam = () => 'stateQueryParam'; - this.destroy = sinon.stub(); - } -} - -function init() { - ngMock.module('kibana/url', 'kibana', function ($provide, PrivateProvider) { - $provide.service('$route', function () { - return { - reload: _.noop, - }; - }); - - appState = new StubAppState(); - PrivateProvider.swap(AppStateProvider, ($decorate) => { - const AppState = $decorate(); - AppState.getAppState = () => appState; - return AppState; - }); - }); - - ngMock.inject(function ($injector) { - $route = $injector.get('$route'); - $location = $injector.get('$location'); - $rootScope = $injector.get('$rootScope'); - kbnUrl = $injector.get('kbnUrl'); - }); -} - -describe('kbnUrl', function () { - beforeEach(function () { - init(); - }); - - describe('forcing reload', function () { - it('schedules a listener for $locationChangeSuccess on the $rootScope', function () { - $location.url('/url'); - $route.current = { - $$route: { - regex: /.*/, - }, - }; - - sinon.stub($rootScope, '$on'); - - expect($rootScope.$on.callCount).to.be(0); - kbnUrl.change('/url'); - sinon.assert.calledOnce(appState.destroy); - expect($rootScope.$on.callCount).to.be(1); - expect($rootScope.$on.firstCall.args[0]).to.be('$locationChangeSuccess'); - }); - - it('handler unbinds the listener and calls reload', function () { - $location.url('/url'); - $route.current = { - $$route: { - regex: /.*/, - }, - }; - - const unbind = sinon.stub(); - sinon.stub($rootScope, '$on').returns(unbind); - $route.reload = sinon.stub(); - - expect($rootScope.$on.callCount).to.be(0); - kbnUrl.change('/url'); - expect($rootScope.$on.callCount).to.be(1); - - const handler = $rootScope.$on.firstCall.args[1]; - handler(); - expect(unbind.callCount).to.be(1); - expect($route.reload.callCount).to.be(1); - }); - - it('reloads requested before the first are ignored', function () { - $location.url('/url'); - $route.current = { - $$route: { - regex: /.*/, - }, - }; - $route.reload = sinon.stub(); - - sinon.stub($rootScope, '$on').returns(sinon.stub()); - - expect($rootScope.$on.callCount).to.be(0); - kbnUrl.change('/url'); - expect($rootScope.$on.callCount).to.be(1); - - // don't call the first handler - - kbnUrl.change('/url'); - expect($rootScope.$on.callCount).to.be(1); - }); - - it('one reload can happen once the first has completed', function () { - $location.url('/url'); - $route.current = { - $$route: { - regex: /.*/, - }, - }; - $route.reload = sinon.stub(); - - sinon.stub($rootScope, '$on').returns(sinon.stub()); - - expect($rootScope.$on.callCount).to.be(0); - kbnUrl.change('/url'); - expect($rootScope.$on.callCount).to.be(1); - - // call the first handler - $rootScope.$on.firstCall.args[1](); - expect($route.reload.callCount).to.be(1); - - expect($rootScope.$on.callCount).to.be(1); - kbnUrl.change('/url'); - expect($rootScope.$on.callCount).to.be(2); - }); - }); - - describe('remove', function () { - it('removes a parameter with a value from the url', function () { - $location.url('/myurl?exist&WithAParamToRemove=2&anothershouldexist=5'); - kbnUrl.removeParam('WithAParamToRemove'); - expect($location.url()).to.be('/myurl?exist&anothershouldexist=5'); - }); - - it('removes a parameter with no value from the url', function () { - $location.url('/myurl?removeme&hi=5'); - kbnUrl.removeParam('removeme'); - expect($location.url()).to.be('/myurl?hi=5'); - }); - - it('is noop if the given parameter doesn\t exist in the url', function () { - $location.url('/myurl?hi&bye'); - kbnUrl.removeParam('noexist'); - expect($location.url()).to.be('/myurl?hi&bye'); - }); - - it('is noop if given empty string param', function () { - $location.url('/myurl?hi&bye'); - kbnUrl.removeParam(''); - expect($location.url()).to.be('/myurl?hi&bye'); - }); - }); - - describe('change', function () { - it('should set $location.url', function () { - sinon.stub($location, 'url'); - - expect($location.url.callCount).to.be(0); - kbnUrl.change('/some-url'); - expect($location.url.callCount).to.be(1); - }); - - it('should uri encode replaced params', function () { - const url = '/some/path/'; - const params = { replace: faker.Lorem.words(3).join(' ') }; - const check = encodeURIComponent(params.replace); - sinon.stub($location, 'url'); - - kbnUrl.change(url + '{{replace}}', params); - - expect($location.url.firstCall.args[0]).to.be(url + check); - }); - - it('should parse angular expression in substitutions and uri encode the results', function () { - // build url by piecing together these parts - const urlParts = ['/', '/', '?', '&', '#']; - // make sure it can parse templates with weird spacing - const wrappers = [ - ['{{', '}}'], - ['{{ ', ' }}'], - ['{{', ' }}'], - ['{{ ', '}}'], - ['{{ ', ' }}'], - ]; - // make sure filters are evaluated via angular expressions - const objIndex = 4; // used to case one replace as an object - const filters = ['', 'uppercase', '', 'uppercase', '']; - - // the words (template keys) used must all be unique - const words = _.uniq(faker.Lorem.words(10)) - .slice(0, urlParts.length) - .map(function (word, i) { - if (filters[i].length) { - return word + '|' + filters[i]; - } - return word; - }); - - const replacements = faker.Lorem.words(urlParts.length).map(function (word, i) { - // make selected replacement into an object - if (i === objIndex) { - return { replace: word }; - } - - return word; - }); - - // build the url and test url - let url = ''; - let testUrl = ''; - urlParts.forEach(function (part, i) { - url += part + wrappers[i][0] + words[i] + wrappers[i][1]; - const locals = {}; - locals[words[i].split('|')[0]] = replacements[i]; - testUrl += part + encodeURIComponent($rootScope.$eval(words[i], locals)); - }); - - // create the locals replacement object - const params = {}; - replacements.forEach(function (replacement, i) { - const word = words[i].split('|')[0]; - params[word] = replacement; - }); - - sinon.stub($location, 'url'); - - kbnUrl.change(url, params); - - expect($location.url.firstCall.args[0]).to.not.be(url); - expect($location.url.firstCall.args[0]).to.be(testUrl); - }); - - it('should handle dot notation', function () { - const url = '/some/thing/{{that.is.substituted}}'; - - kbnUrl.change(url, { - that: { - is: { - substituted: 'test', - }, - }, - }); - - expect($location.url()).to.be('/some/thing/test'); - }); - - it('should throw when params are missing', function () { - const url = '/{{replace_me}}'; - const params = {}; - - try { - kbnUrl.change(url, params); - throw new Error('this should not run'); - } catch (err) { - expect(err).to.be.an(Error); - expect(err.message).to.match(/replace_me/); - } - }); - - it('should throw when filtered params are missing', function () { - const url = '/{{replace_me|number}}'; - const params = {}; - - try { - kbnUrl.change(url, params); - throw new Error('this should not run'); - } catch (err) { - expect(err).to.be.an(Error); - expect(err.message).to.match(/replace_me\|number/); - } - }); - - it('should change the entire url', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.change(newPath); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql({}); - expect($location.hash()).to.be(''); - }); - - it('should allow setting app state on the target url', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.change(newPath, null, new StubAppState()); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql({ _a: 'stateQueryParam' }); - expect($location.hash()).to.be(''); - }); - }); - - describe('changePath', function () { - it('should change just the path', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.changePath(newPath); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - }); - }); - - describe('redirect', function () { - it('should change the entire url', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.redirect(newPath); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql({}); - expect($location.hash()).to.be(''); - }); - - it('should allow setting app state on the target url', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.redirect(newPath, null, new StubAppState()); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql({ _a: 'stateQueryParam' }); - expect($location.hash()).to.be(''); - }); - - it('should replace the current history entry', function () { - sinon.stub($location, 'replace'); - $location.url('/some/path'); - - expect($location.replace.callCount).to.be(0); - kbnUrl.redirect('/new/path/'); - expect($location.replace.callCount).to.be(1); - }); - - it('should call replace on $location', function () { - sinon.stub(kbnUrl, '_shouldForceReload').returns(false); - sinon.stub($location, 'replace'); - - expect($location.replace.callCount).to.be(0); - kbnUrl.redirect('/poop'); - expect($location.replace.callCount).to.be(1); - }); - }); - - describe('redirectPath', function () { - it('should only change the path', function () { - const path = '/test/path'; - const search = { search: 'test' }; - const hash = 'hash'; - const newPath = '/new/location'; - - $location.path(path).search(search).hash(hash); - - // verify the starting state - expect($location.path()).to.be(path); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - - kbnUrl.redirectPath(newPath); - - // verify the ending state - expect($location.path()).to.be(newPath); - expect($location.search()).to.eql(search); - expect($location.hash()).to.be(hash); - }); - - it('should call replace on $location', function () { - sinon.stub(kbnUrl, '_shouldForceReload').returns(false); - sinon.stub($location, 'replace'); - - expect($location.replace.callCount).to.be(0); - kbnUrl.redirectPath('/poop'); - expect($location.replace.callCount).to.be(1); - }); - }); - - describe('_shouldForceReload', function () { - let next; - let prev; - - beforeEach(function () { - $route.current = { - $$route: { - regexp: /^\/is-current-route\/(\d+)/, - reloadOnSearch: true, - }, - }; - - prev = { path: '/is-current-route/1', search: {} }; - next = { path: '/is-current-route/1', search: {} }; - }); - - it("returns false if the passed url doesn't match the current route", function () { - next.path = '/not current'; - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - - describe('if the passed url does match the route', function () { - describe('and the route reloads on search', function () { - describe('and the path is the same', function () { - describe('and the search params are the same', function () { - it('returns true', function () { - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(true); - }); - }); - describe('but the search params are different', function () { - it('returns false', function () { - next.search = {}; - prev.search = { q: 'search term' }; - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - }); - }); - - describe('and the path is different', function () { - beforeEach(function () { - next.path = '/not-same'; - }); - - describe('and the search params are the same', function () { - it('returns false', function () { - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - }); - describe('but the search params are different', function () { - it('returns false', function () { - next.search = {}; - prev.search = { q: 'search term' }; - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - }); - }); - }); - - describe('but the route does not reload on search', function () { - beforeEach(function () { - $route.current.$$route.reloadOnSearch = false; - }); - - describe('and the path is the same', function () { - describe('and the search params are the same', function () { - it('returns true', function () { - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(true); - }); - }); - describe('but the search params are different', function () { - it('returns true', function () { - next.search = {}; - prev.search = { q: 'search term' }; - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(true); - }); - }); - }); - - describe('and the path is different', function () { - beforeEach(function () { - next.path = '/not-same'; - }); - - describe('and the search params are the same', function () { - it('returns false', function () { - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - }); - describe('but the search params are different', function () { - it('returns false', function () { - next.search = {}; - prev.search = { q: 'search term' }; - expect(kbnUrl._shouldForceReload(next, prev, $route)).to.be(false); - }); - }); - }); - }); - }); - }); -}); diff --git a/src/legacy/ui/public/url/absolute_to_parsed_url.ts b/src/legacy/ui/public/url/absolute_to_parsed_url.ts deleted file mode 100644 index 30f493c25776c..0000000000000 --- a/src/legacy/ui/public/url/absolute_to_parsed_url.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { parse } from 'url'; - -import { extractAppPathAndId } from './extract_app_path_and_id'; -import { KibanaParsedUrl } from './kibana_parsed_url'; - -/** - * - * @param absoluteUrl - an absolute url, e.g. https://localhost:5601/gra/app/visualize#/edit/viz_id?hi=bye - * @param basePath - An optional base path for kibana. If supplied, should start with a "/". - * e.g. in https://localhost:5601/gra/app/visualize#/edit/viz_id the basePath is - * "/gra". - * @return {KibanaParsedUrl} - */ -export function absoluteToParsedUrl(absoluteUrl: string, basePath = '') { - const { appPath, appId } = extractAppPathAndId(absoluteUrl, basePath); - const { hostname, port, protocol } = parse(absoluteUrl); - return new KibanaParsedUrl({ - basePath, - appId: appId!, - appPath, - hostname, - port, - protocol, - }); -} diff --git a/src/legacy/ui/public/url/extract_app_path_and_id.ts b/src/legacy/ui/public/url/extract_app_path_and_id.ts deleted file mode 100644 index 44bba272e0873..0000000000000 --- a/src/legacy/ui/public/url/extract_app_path_and_id.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { parse } from 'url'; - -/** - * If the url is determined to contain an appId and appPath, it returns those portions. If it is not in the right - * format and an appId and appPath can't be extracted, it returns an empty object. - * @param {string} url - a relative or absolute url which contains an appPath, an appId, and optionally, a basePath. - * @param {string} basePath - optional base path, if given should start with "/". - */ -export function extractAppPathAndId(url: string, basePath = '') { - const parsedUrl = parse(url); - if (!parsedUrl.path) { - return {}; - } - const pathWithoutBase = parsedUrl.path.slice(basePath.length); - - if (!pathWithoutBase.startsWith('/app/')) { - return {}; - } - - const appPath = parsedUrl.hash && parsedUrl.hash.length > 0 ? parsedUrl.hash.slice(1) : ''; - return { appId: pathWithoutBase.slice('/app/'.length), appPath }; -} diff --git a/src/legacy/ui/public/url/index.js b/src/legacy/ui/public/url/index.js deleted file mode 100644 index 8ef267de2890c..0000000000000 --- a/src/legacy/ui/public/url/index.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export { KbnUrlProvider } from './url'; -export { RedirectWhenMissingProvider } from './redirect_when_missing'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -export { modifyUrl } from '../../../../core/utils'; diff --git a/src/legacy/ui/public/url/kbn_url.ts b/src/legacy/ui/public/url/kbn_url.ts deleted file mode 100644 index 42b6a8f19f9a9..0000000000000 --- a/src/legacy/ui/public/url/kbn_url.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export interface KbnUrl { - change: (url: string) => void; - removeParam: (param: string) => void; -} diff --git a/src/legacy/ui/public/url/kibana_parsed_url.ts b/src/legacy/ui/public/url/kibana_parsed_url.ts deleted file mode 100644 index 1c60e8729e0ff..0000000000000 --- a/src/legacy/ui/public/url/kibana_parsed_url.ts +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { parse } from 'url'; - -import { modifyUrl } from '../../../../core/public'; -import { prependPath } from './prepend_path'; - -interface Options { - /** - * An optional base path for kibana. If supplied, should start with a "/". - * e.g. in https://localhost:5601/gra/app/visualize#/edit/viz_id the - * basePath is "/gra" - */ - basePath?: string; - - /** - * The app id. - * e.g. in https://localhost:5601/gra/app/visualize#/edit/viz_id the app id is "kibana". - */ - appId: string; - - /** - * The path for a page in the the app. Should start with a "/". Don't include the hash sign. Can - * include all query parameters. - * e.g. in https://localhost:5601/gra/app/visualize#/edit/viz_id?g=state the appPath is - * "/edit/viz_id?g=state" - */ - appPath?: string; - - /** - * Optional hostname. Uses current window location's hostname if hostname, port, - * and protocol are undefined. - */ - hostname?: string; - - /** - * Optional port. Uses current window location's port if hostname, port, - * and protocol are undefined. - */ - port?: string; - - /** - * Optional protocol. Uses current window location's protocol if hostname, port, - * and protocol are undefined. - */ - protocol?: string; -} - -/** - * Represents the pieces that make up a url in Kibana, offering some helpful functionality - * for translating those pieces into absolute or relative urls. A Kibana url with a basePath - * looks like this: http://localhost:5601/basePath/app/appId#/an/appPath?with=query¶ms - * - * - basePath is "/basePath" - * - appId is "appId" - * - appPath is "/an/appPath?with=query¶ms" - * - * Almost all urls in Kibana should have this structure, including the "/app" portion in front of the appId - * (one exception is the login link). - */ -export class KibanaParsedUrl { - public appId: string; - public appPath: string; - public basePath: string; - public hostname?: string; - public protocol?: string; - public port?: string; - - constructor(options: Options) { - const { appId, basePath = '', appPath = '', hostname, protocol, port } = options; - - // We'll use window defaults - const hostOrProtocolSpecified = hostname || protocol || port; - - this.basePath = basePath; - this.appId = appId; - this.appPath = appPath; - this.hostname = hostOrProtocolSpecified ? hostname : window.location.hostname; - this.port = hostOrProtocolSpecified ? port : window.location.port; - this.protocol = hostOrProtocolSpecified ? protocol : window.location.protocol; - } - - public getGlobalState() { - if (!this.appPath) { - return ''; - } - const parsedUrl = parse(this.appPath, true); - const query = parsedUrl.query || {}; - return query._g || ''; - } - - public setGlobalState(newGlobalState: string | string[]) { - if (!this.appPath) { - return; - } - - this.appPath = modifyUrl(this.appPath, (parsed) => { - parsed.query._g = newGlobalState; - }); - } - - public addQueryParameter(name: string, val: string) { - this.appPath = modifyUrl(this.appPath, (parsed) => { - parsed.query[name] = val; - }); - } - - public getHashedAppPath() { - return `#${this.appPath}`; - } - - public getAppBasePath() { - return `/${this.appId}`; - } - - public getAppRootPath() { - return `/app${this.getAppBasePath()}${this.getHashedAppPath()}`; - } - - public getRootRelativePath() { - return prependPath(this.getAppRootPath(), this.basePath); - } - - public getAbsoluteUrl() { - return modifyUrl(this.getRootRelativePath(), (parsed) => { - parsed.protocol = this.protocol; - parsed.port = this.port; - parsed.hostname = this.hostname; - }); - } -} diff --git a/src/legacy/ui/public/url/prepend_path.ts b/src/legacy/ui/public/url/prepend_path.ts deleted file mode 100644 index b8a77d5c23bee..0000000000000 --- a/src/legacy/ui/public/url/prepend_path.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { isString } from 'lodash'; -import { format, parse } from 'url'; - -/** - * - * @param {string} relativePath - a relative path that must start with a "/". - * @param {string} newPath - the new path to prefix. ex: 'xyz' - * @return {string} the url with the basePath prepended. ex. '/xyz/app/kibana#/management'. If - * the relative path isn't in the right format (e.g. doesn't start with a "/") the relativePath is returned - * unchanged. - */ -export function prependPath(relativePath: string, newPath = '') { - if (!relativePath || !isString(relativePath)) { - return relativePath; - } - - const parsed = parse(relativePath, true, true); - if (!parsed.host && parsed.pathname) { - if (parsed.pathname[0] === '/') { - parsed.pathname = newPath + parsed.pathname; - } - } - - return format({ - protocol: parsed.protocol, - host: parsed.host, - pathname: parsed.pathname, - query: parsed.query, - hash: parsed.hash, - }); -} diff --git a/src/legacy/ui/public/url/redirect_when_missing.js b/src/legacy/ui/public/url/redirect_when_missing.js deleted file mode 100644 index 85c90a14d9fd7..0000000000000 --- a/src/legacy/ui/public/url/redirect_when_missing.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { MarkdownSimple } from '../../../../plugins/kibana_react/public'; -import { toastNotifications } from 'ui/notify'; -import { SavedObjectNotFound } from '../../../../plugins/kibana_utils/public'; -import { uiModules } from '../modules'; - -uiModules.get('kibana/url').service('redirectWhenMissing', function (Private) { - return Private(RedirectWhenMissingProvider); -}); - -export function RedirectWhenMissingProvider(kbnUrl, Promise) { - /** - * Creates an error handler that will redirect to a url when a SavedObjectNotFound - * error is thrown - * - * @param {string|object} mapping - a mapping of url's to redirect to based on the saved object that - * couldn't be found, or just a string that will be used for all types - * @return {function} - the handler to pass to .catch() - */ - return function (mapping) { - if (typeof mapping === 'string') { - mapping = { '*': mapping }; - } - - return function (error) { - // if this error is not "404", rethrow - const savedObjectNotFound = error instanceof SavedObjectNotFound; - const unknownVisType = error.message.indexOf('Invalid type') === 0; - if (unknownVisType) { - error.savedObjectType = 'visualization'; - } else if (!savedObjectNotFound) { - throw error; - } - - let url = mapping[error.savedObjectType] || mapping['*']; - if (!url) url = '/'; - - url += (url.indexOf('?') >= 0 ? '&' : '?') + `notFound=${error.savedObjectType}`; - - toastNotifications.addWarning({ - title: i18n.translate('common.ui.url.savedObjectIsMissingNotificationMessage', { - defaultMessage: 'Saved object is missing', - }), - text: {error.message}, - }); - - kbnUrl.redirect(url); - return Promise.halt(); - }; - }; -} diff --git a/src/legacy/ui/public/url/relative_to_absolute.ts b/src/legacy/ui/public/url/relative_to_absolute.ts deleted file mode 100644 index 7d0737d145a1b..0000000000000 --- a/src/legacy/ui/public/url/relative_to_absolute.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * - * @param {string} url - a relative or root relative url. If a relative path is given then the - * absolute url returned will depend on the current page where this function is called from. For example - * if you are on page "http://www.mysite.com/shopping/kids" and you pass this function "adults", you would get - * back "http://www.mysite.com/shopping/adults". If you passed this function a root relative path, or one that - * starts with a "/", for example "/account/cart", you would get back "http://www.mysite.com/account/cart". - * @return {string} the relative url transformed into an absolute url - */ -export function relativeToAbsolute(url: string) { - // convert all link urls to absolute urls - const a = document.createElement('a'); - a.setAttribute('href', url); - return a.href; -} diff --git a/src/legacy/ui/public/url/url.js b/src/legacy/ui/public/url/url.js deleted file mode 100644 index fb243b02e05c7..0000000000000 --- a/src/legacy/ui/public/url/url.js +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; -import { i18n } from '@kbn/i18n'; -import { uiModules } from '../modules'; -import { AppStateProvider } from '../state_management/app_state'; - -uiModules.get('kibana/url').service('kbnUrl', function (Private, $injector) { - //config is not directly used but registers global event listeners to kbnUrl to function - $injector.get('config'); - return Private(KbnUrlProvider); -}); - -export function KbnUrlProvider($injector, $location, $rootScope, $parse, Private) { - /** - * the `kbnUrl` service was created to smooth over some of the - * inconsistent behavior that occurs when modifying the url via - * the `$location` api. In general it is recommended that you use - * the `kbnUrl` service any time you want to modify the url. - * - * "features" that `kbnUrl` does it's best to guarantee, which - * are not guaranteed with the `$location` service: - * - calling `kbnUrl.change()` with a url that resolves to the current - * route will force a full transition (rather than just updating the - * properties of the $route object) - * - * Additional features of `kbnUrl` - * - parameterized urls - * - easily include an app state with the url - * - * @type {KbnUrl} - */ - const self = this; - - /** - * Navigate to a url - * - * @param {String} url - the new url, can be a template. See #eval - * @param {Object} [paramObj] - optional set of parameters for the url template - * @return {undefined} - */ - self.change = function (url, paramObj, appState) { - self._changeLocation('url', url, paramObj, false, appState); - }; - - /** - * Same as #change except only changes the url's path, - * leaving the search string and such intact - * - * @param {String} path - the new path, can be a template. See #eval - * @param {Object} [paramObj] - optional set of parameters for the path template - * @return {undefined} - */ - self.changePath = function (path, paramObj) { - self._changeLocation('path', path, paramObj); - }; - - /** - * Same as #change except that it removes the current url from history - * - * @param {String} url - the new url, can be a template. See #eval - * @param {Object} [paramObj] - optional set of parameters for the url template - * @return {undefined} - */ - self.redirect = function (url, paramObj, appState) { - self._changeLocation('url', url, paramObj, true, appState); - }; - - /** - * Same as #redirect except only changes the url's path, - * leaving the search string and such intact - * - * @param {String} path - the new path, can be a template. See #eval - * @param {Object} [paramObj] - optional set of parameters for the path template - * @return {undefined} - */ - self.redirectPath = function (path, paramObj) { - self._changeLocation('path', path, paramObj, true); - }; - - /** - * Evaluate a url template. templates can contain double-curly wrapped - * expressions that are evaluated in the context of the paramObj - * - * @param {String} template - the url template to evaluate - * @param {Object} [paramObj] - the variables to expose to the template - * @return {String} - the evaluated result - * @throws {Error} If any of the expressions can't be parsed. - */ - self.eval = function (template, paramObj) { - paramObj = paramObj || {}; - - return template.replace(/\{\{([^\}]+)\}\}/g, function (match, expr) { - // remove filters - const key = expr.split('|')[0].trim(); - - // verify that the expression can be evaluated - const p = $parse(key)(paramObj); - - // if evaluation can't be made, throw - if (_.isUndefined(p)) { - throw new Error( - i18n.translate('common.ui.url.replacementFailedErrorMessage', { - defaultMessage: 'Replacement failed, unresolved expression: {expr}', - values: { expr }, - }) - ); - } - - return encodeURIComponent($parse(expr)(paramObj)); - }); - }; - - /** - * convert an object's route to an href, compatible with - * window.location.href= and - * - * @param {Object} obj - any object that list's it's routes at obj.routes{} - * @param {string} route - the route name - * @return {string} - the computed href - */ - self.getRouteHref = function (obj, route) { - return '#' + self.getRouteUrl(obj, route); - }; - - /** - * convert an object's route to a url, compatible with url.change() or $location.url() - * - * @param {Object} obj - any object that list's it's routes at obj.routes{} - * @param {string} route - the route name - * @return {string} - the computed url - */ - self.getRouteUrl = function (obj, route) { - const template = obj && obj.routes && obj.routes[route]; - if (template) return self.eval(template, obj); - }; - - /** - * Similar to getRouteUrl, supports objects which list their routes, - * and redirects to the named route. See #redirect - * - * @param {Object} obj - any object that list's it's routes at obj.routes{} - * @param {string} route - the route name - * @return {undefined} - */ - self.redirectToRoute = function (obj, route) { - self.redirect(self.getRouteUrl(obj, route)); - }; - - /** - * Similar to getRouteUrl, supports objects which list their routes, - * and changes the url to the named route. See #change - * - * @param {Object} obj - any object that list's it's routes at obj.routes{} - * @param {string} route - the route name - * @return {undefined} - */ - self.changeToRoute = function (obj, route) { - self.change(self.getRouteUrl(obj, route)); - }; - - /** - * Removes the given parameter from the url. Does so without modifying the browser - * history. - * @param param - */ - self.removeParam = function (param) { - $location.search(param, null).replace(); - }; - - ///// - // private api - ///// - let reloading; - - self._changeLocation = function (type, url, paramObj, replace, appState) { - const prev = { - path: $location.path(), - search: $location.search(), - }; - - url = self.eval(url, paramObj); - $location[type](url); - if (replace) $location.replace(); - - if (appState) { - $location.search(appState.getQueryParamName(), appState.toQueryParam()); - } - - const next = { - path: $location.path(), - search: $location.search(), - }; - - if ($injector.has('$route')) { - const $route = $injector.get('$route'); - - if (self._shouldForceReload(next, prev, $route)) { - const appState = Private(AppStateProvider).getAppState(); - if (appState) appState.destroy(); - - reloading = $rootScope.$on('$locationChangeSuccess', function () { - // call the "unlisten" function returned by $on - reloading(); - reloading = false; - - $route.reload(); - }); - } - } - }; - - // determine if the router will automatically reload the route - self._shouldForceReload = function (next, prev, $route) { - if (reloading) return false; - - const route = $route.current && $route.current.$$route; - if (!route) return false; - - // for the purposes of determining whether the router will - // automatically be reloading, '' and '/' are equal - const nextPath = next.path || '/'; - const prevPath = prev.path || '/'; - if (nextPath !== prevPath) return false; - - const reloadOnSearch = route.reloadOnSearch; - const searchSame = _.isEqual(next.search, prev.search); - return (reloadOnSearch && searchSame) || !reloadOnSearch; - }; -} diff --git a/src/legacy/ui/public/utils/collection.test.ts b/src/legacy/ui/public/utils/collection.test.ts deleted file mode 100644 index 0841e3554c0d0..0000000000000 --- a/src/legacy/ui/public/utils/collection.test.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { move } from './collection'; - -describe('collection', () => { - describe('move', () => { - test('accepts previous from->to syntax', () => { - const list = [1, 1, 1, 1, 1, 1, 1, 1, 8, 1, 1]; - - expect(list[3]).toBe(1); - expect(list[8]).toBe(8); - - move(list, 8, 3); - - expect(list[8]).toBe(1); - expect(list[3]).toBe(8); - }); - - test('moves an object up based on a function callback', () => { - const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; - - expect(list[4]).toBe(0); - expect(list[5]).toBe(1); - expect(list[6]).toBe(0); - - move(list, 5, false, (v: any) => v === 0); - - expect(list[4]).toBe(1); - expect(list[5]).toBe(0); - expect(list[6]).toBe(0); - }); - - test('moves an object down based on a function callback', () => { - const list = [1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1]; - - expect(list[4]).toBe(0); - expect(list[5]).toBe(1); - expect(list[6]).toBe(0); - - move(list, 5, true, (v: any) => v === 0); - - expect(list[4]).toBe(0); - expect(list[5]).toBe(0); - expect(list[6]).toBe(1); - }); - - test('moves an object up based on a where callback', () => { - const list = [ - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - ]; - - expect(list[4]).toHaveProperty('v', 0); - expect(list[5]).toHaveProperty('v', 1); - expect(list[6]).toHaveProperty('v', 0); - - move(list, 5, false, { v: 0 }); - - expect(list[4]).toHaveProperty('v', 1); - expect(list[5]).toHaveProperty('v', 0); - expect(list[6]).toHaveProperty('v', 0); - }); - - test('moves an object down based on a where callback', () => { - const list = [ - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 0 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - { v: 1 }, - ]; - - expect(list[4]).toHaveProperty('v', 0); - expect(list[5]).toHaveProperty('v', 1); - expect(list[6]).toHaveProperty('v', 0); - - move(list, 5, true, { v: 0 }); - - expect(list[4]).toHaveProperty('v', 0); - expect(list[5]).toHaveProperty('v', 0); - expect(list[6]).toHaveProperty('v', 1); - }); - - test('moves an object down based on a pluck callback', () => { - const list = [ - { id: 0, normal: true }, - { id: 1, normal: true }, - { id: 2, normal: true }, - { id: 3, normal: true }, - { id: 4, normal: true }, - { id: 5, normal: false }, - { id: 6, normal: true }, - { id: 7, normal: true }, - { id: 8, normal: true }, - { id: 9, normal: true }, - ]; - - expect(list[4]).toHaveProperty('id', 4); - expect(list[5]).toHaveProperty('id', 5); - expect(list[6]).toHaveProperty('id', 6); - - move(list, 5, true, 'normal'); - - expect(list[4]).toHaveProperty('id', 4); - expect(list[5]).toHaveProperty('id', 6); - expect(list[6]).toHaveProperty('id', 5); - }); - }); -}); diff --git a/src/legacy/ui/public/utils/collection.ts b/src/legacy/ui/public/utils/collection.ts deleted file mode 100644 index b882a2bbe6e5b..0000000000000 --- a/src/legacy/ui/public/utils/collection.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import _ from 'lodash'; - -/** - * move an obj either up or down in the collection by - * injecting it either before/after the prev/next obj that - * satisfied the qualifier - * - * or, just from one index to another... - * - * @param {array} objs - the list to move the object within - * @param {number|any} obj - the object that should be moved, or the index that the object is currently at - * @param {number|boolean} below - the index to move the object to, or whether it should be moved up or down - * @param {function} qualifier - a lodash-y callback, object = _.where, string = _.pluck - * @return {array} - the objs argument - */ -export function move( - objs: any[], - obj: object | number, - below: number | boolean, - qualifier?: ((object: object, index: number) => any) | Record | string -): object[] { - const origI = _.isNumber(obj) ? obj : objs.indexOf(obj); - if (origI === -1) { - return objs; - } - - if (_.isNumber(below)) { - // move to a specific index - objs.splice(below, 0, objs.splice(origI, 1)[0]); - return objs; - } - - below = !!below; - qualifier = qualifier && _.iteratee(qualifier); - - const above = !below; - const finder = below ? _.findIndex : _.findLastIndex; - - // find the index of the next/previous obj that meets the qualifications - const targetI = finder(objs, (otherAgg, otherI) => { - if (below && otherI <= origI) { - return; - } - if (above && otherI >= origI) { - return; - } - return Boolean(_.isFunction(qualifier) && qualifier(otherAgg, otherI)); - }); - - if (targetI === -1) { - return objs; - } - - // place the obj at it's new index - objs.splice(targetI, 0, objs.splice(origI, 1)[0]); - return objs; -} diff --git a/src/legacy/ui/public/utils/legacy_class.js b/src/legacy/ui/public/utils/legacy_class.js deleted file mode 100644 index f47650a77bb6d..0000000000000 --- a/src/legacy/ui/public/utils/legacy_class.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// create a property descriptor for properties -// that won't change -function describeConst(val) { - return { - writable: false, - enumerable: false, - configurable: false, - value: val, - }; -} - -const props = { - inherits: describeConst(function (SuperClass) { - const prototype = Object.create(SuperClass.prototype, { - constructor: describeConst(this), - superConstructor: describeConst(SuperClass), - }); - - Object.defineProperties(this, { - prototype: describeConst(prototype), - Super: describeConst(SuperClass), - }); - - return this; - }), -}; - -/** - * Add class-related behavior to a function, currently this - * only attaches an .inherits() method. - * - * @param {Constructor} ClassConstructor - The function that should be extended - * @return {Constructor} - the constructor passed in; - */ -export function createLegacyClass(ClassConstructor) { - return Object.defineProperties(ClassConstructor, props); -} diff --git a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx index 6103041cf0a4c..68a21c6a1587c 100644 --- a/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx +++ b/src/plugins/advanced_settings/public/management_app/advanced_settings.test.tsx @@ -32,10 +32,6 @@ import { AdvancedSettingsComponent } from './advanced_settings'; import { notificationServiceMock, docLinksServiceMock } from '../../../../core/public/mocks'; import { ComponentRegistry } from '../component_registry'; -jest.mock('ui/new_platform', () => ({ - npStart: mockConfig(), -})); - jest.mock('./components/field', () => ({ Field: () => { return 'field'; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts index 51f4fc7ce94b9..5e0eeaee3c0d0 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.test.mock.ts @@ -22,5 +22,3 @@ import { setup } from 'test_utils/http_test_setup'; export const { http } = setup((injectedMetadata) => { injectedMetadata.getBasePath.mockReturnValue('/hola/daro/'); }); - -jest.doMock('ui/new_platform', () => ({ npSetup: { core: { http } } })); diff --git a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx index 46be1044c0d4d..e6cf8a57686f1 100644 --- a/src/plugins/discover/public/application/components/field_name/field_name.test.tsx +++ b/src/plugins/discover/public/application/components/field_name/field_name.test.tsx @@ -20,8 +20,6 @@ import React from 'react'; import { render } from 'enzyme'; import { FieldName } from './field_name'; -jest.mock('ui/new_platform'); - // Note that it currently provides just 2 basic tests, there should be more, but // the components involved will soon change test('FieldName renders a string field by providing fieldType and fieldName', () => { diff --git a/src/plugins/es_ui_shared/public/indices/validate/validate_index.test.ts b/src/plugins/es_ui_shared/public/indices/validate/validate_index.test.ts index a6792543cd726..b28cac1cb76f5 100644 --- a/src/plugins/es_ui_shared/public/indices/validate/validate_index.test.ts +++ b/src/plugins/es_ui_shared/public/indices/validate/validate_index.test.ts @@ -17,8 +17,6 @@ * under the License. */ -jest.mock('ui/new_platform'); - import { INDEX_ILLEGAL_CHARACTERS_VISIBLE } from '../constants'; import { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx index 80132167b7f58..ed50317aed6a0 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx @@ -46,14 +46,6 @@ jest.mock('./components/table', () => ({ }, })); -jest.mock('ui/documentation_links', () => ({ - documentationLinks: { - scriptedFields: { - painless: 'painlessDocs', - }, - }, -})); - const helpers = { redirectToRoute: () => {}, getRouteHref: () => '#', diff --git a/src/plugins/timelion/public/directives/_saved_object_finder.scss b/src/plugins/timelion/public/directives/_saved_object_finder.scss index b97dace5e9e00..e1a055a5f49e9 100644 --- a/src/plugins/timelion/public/directives/_saved_object_finder.scss +++ b/src/plugins/timelion/public/directives/_saved_object_finder.scss @@ -23,3 +23,74 @@ } } } + + +saved-object-finder { + + .list-sort-button { + border-top-left-radius: 0; + border-top-right-radius: 0; + border: none; + padding: $euiSizeS $euiSize; + font-weight: $euiFontWeightRegular; + background-color: $euiColorLightestShade; + } + + .li-striped { + li { + border: none; + } + + li:nth-child(even) { + background-color: $euiColorLightestShade; + } + + li:nth-child(odd) { + background-color: $euiColorEmptyShade; + } + + .paginate-heading { + font-weight: $euiFontWeightRegular; + color: $euiColorDarkestShade; + } + + .list-group-item { + padding: $euiSizeS $euiSize; + + ul { + padding: 0; + display: flex; + flex-direction: row; + + .finder-type { + margin-right: $euiSizeS; + } + } + + a { + display: block; + color: $euiColorPrimary; + + i { + color: shade($euiColorPrimary, 10%); + margin-right: $euiSizeS; + } + } + + &:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + + &.list-group-no-results p { + margin-bottom: 0; + } + } + } + + paginate { + paginate-controls { + margin: $euiSize; + } + } +} diff --git a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js b/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js index cfbd5ecb7bf65..068e3fea8b768 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js +++ b/src/plugins/vis_type_timeseries/public/application/components/lib/get_axis_label_string.test.js @@ -19,8 +19,6 @@ import { getAxisLabelString } from './get_axis_label_string'; -jest.mock('ui/new_platform'); - describe('getAxisLabelString(interval)', () => { test('should return a valid label for 10 seconds', () => { expect(getAxisLabelString(10000)).toEqual('per 10 seconds'); diff --git a/src/test_utils/public/no_digest_promises.js b/src/test_utils/public/no_digest_promises.js deleted file mode 100644 index 396d6fb4892f3..0000000000000 --- a/src/test_utils/public/no_digest_promises.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import Bluebird from 'bluebird'; -import 'ui/promises'; -import { uiModules } from 'ui/modules'; - -Bluebird.longStackTraces(); - -/** - * replace the Promise service with Bluebird so that tests - * can use promises without having to call $rootScope.apply() - * - * import noDigestPromises from 'test_utils/no_digest_promises'; - * - * describe('some module that does complex shit with promises', function () { - * beforeEach(noDigestPromises.activate); - * - * }); - */ - -let active = false; - -uiModules.get('kibana').config(function ($provide) { - $provide.decorator('Promise', function ($delegate) { - return active ? Bluebird : $delegate; - }); -}); - -function activate() { - active = true; -} -function deactivate() { - active = false; -} - -export default { - activate: activate, - deactivate: deactivate, - activateForSuite: function () { - before(activate); - after(deactivate); - }, -}; diff --git a/src/test_utils/public/stub_get_active_injector.js b/src/test_utils/public/stub_get_active_injector.js deleted file mode 100644 index fb72cc5dabb7e..0000000000000 --- a/src/test_utils/public/stub_get_active_injector.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** - * This test file contains stubs for chrome.dangerouslyGetActiveInjector, that you will - * need to load if any part of the code you are testing relies on that method. - * You will need to call setupInjectorStub and teardownInjectorStub in specific - * places inside your test file to setup and teardown the stub. - * If you can call both of them at the same place you can also use the shortcut - * setupAndTeardownInjectorStub instead. - */ - -import ngMock from 'ng_mock'; - -import chrome from 'ui/chrome'; -import sinon from 'sinon'; - -/** - * This method setups the stub for chrome.dangerouslyGetActiveInjector. You must call it in - * a place where beforeEach is allowed to be called (read: inside your describe) - * method. You must call this AFTER you've called `ngMock.module` to setup the modules, - * but BEFORE you first execute code, that uses chrome.dangerouslyGetActiveInjector. - */ -export function setupInjectorStub() { - beforeEach( - ngMock.inject(($injector) => { - sinon.stub(chrome, 'dangerouslyGetActiveInjector').returns(Promise.resolve($injector)); - }) - ); -} - -/** - * This methods tears down the stub for chrome.dangerouslyGetActiveInjector. You must call it - * in a place where afterEach is allowed to be called. - */ -export function teardownInjectorStub() { - afterEach(() => { - chrome.dangerouslyGetActiveInjector.restore(); - }); -} - -/** - * This method combines setupInjectorStub and teardownInjectorStub in one method. - * It can be used if you can call the other two methods directly behind each other. - */ -export function setupAndTeardownInjectorStub() { - setupInjectorStub(); - teardownInjectorStub(); -} diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js deleted file mode 100644 index 7aa12ea7a1130..0000000000000 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_vis.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { SelfChangingEditor } from './self_changing_editor'; -import { SelfChangingComponent } from './self_changing_components'; - -import { npSetup } from '../../../../../../src/legacy/ui/public/new_platform'; - -npSetup.plugins.visualizations.createReactVisualization({ - name: 'self_changing_vis', - title: 'Self Changing Vis', - icon: 'controlsHorizontal', - description: - 'This visualization is able to change its own settings, that you could also set in the editor.', - visConfig: { - component: SelfChangingComponent, - defaults: { - counter: 0, - }, - }, - editorConfig: { - optionTabs: [ - { - name: 'options', - title: 'Options', - editor: SelfChangingEditor, - }, - ], - }, - requestHandler: 'none', -}); diff --git a/tsconfig.json b/tsconfig.json index 66906fb18bb80..819cb5dc4c1e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,9 +7,6 @@ "kibana/public": ["src/core/public"], "kibana/server": ["src/core/server"], "plugins/*": ["src/legacy/core_plugins/*/public/"], - "ui/*": [ - "src/legacy/ui/public/*" - ], "test_utils/*": [ "src/test_utils/public/*" ], diff --git a/x-pack/dev-tools/jest/create_jest_config.js b/x-pack/dev-tools/jest/create_jest_config.js index a0574dbdf36da..a693e008db6ea 100644 --- a/x-pack/dev-tools/jest/create_jest_config.js +++ b/x-pack/dev-tools/jest/create_jest_config.js @@ -13,7 +13,6 @@ export function createJestConfig({ kibanaDirectory, rootDir, xPackKibanaDirector moduleNameMapper: { '@elastic/eui$': `${kibanaDirectory}/node_modules/@elastic/eui/test-env`, '@elastic/eui/lib/(.*)?': `${kibanaDirectory}/node_modules/@elastic/eui/test-env/$1`, - '^ui/(.*)': `${kibanaDirectory}/src/legacy/ui/public/$1`, '^fixtures/(.*)': `${kibanaDirectory}/src/fixtures/$1`, 'uiExports/(.*)': fileMockPath, '^src/core/(.*)': `${kibanaDirectory}/src/core/$1`, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts index 882d1e2ea58b9..33260b5c9303f 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_lens.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); + import { savedLens } from './saved_lens'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; import { ExpressionValueFilter } from '../../../types'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts index 74e41a030de35..b2de0b6526d1e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_map.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); + import { savedMap } from './saved_map'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; import { ExpressionValueFilter } from '../../../types'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts index 9bd32202b563a..7d5952754aa90 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_search.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); + import { savedSearch } from './saved_search'; import { buildEmbeddableFilters } from '../../../public/lib/build_embeddable_filters'; import { ExpressionValueFilter } from '../../../types'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts index 8327c1433b9af..a64fb167dd19f 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/external/saved_visualization.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); + import { savedVisualization } from './saved_visualization'; import { getQueryFilters } from '../../../public/lib/build_embeddable_filters'; import { ExpressionValueFilter } from '../../../types'; diff --git a/x-pack/plugins/canvas/common/lib/autocomplete.test.ts b/x-pack/plugins/canvas/common/lib/autocomplete.test.ts index f7a773f6ca36a..777810cad05ba 100644 --- a/x-pack/plugins/canvas/common/lib/autocomplete.test.ts +++ b/x-pack/plugins/canvas/common/lib/autocomplete.test.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); import { functionSpecs } from '../../__tests__/fixtures/function_specs'; import { diff --git a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx index e8b772e0f2fbd..b037d30ada12f 100644 --- a/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx +++ b/x-pack/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx @@ -14,7 +14,6 @@ import { import { METRIC_TYPE } from '../../../lib/ui_metric'; import { ResolvedArgType } from '../../../../types'; -jest.mock('ui/new_platform'); const trackMetric = jest.fn(); const Component = withUnconnectedElementsLoadedTelemetry(() =>

, trackMetric); diff --git a/x-pack/plugins/canvas/public/state/middleware/__tests__/workpad_refresh.test.ts b/x-pack/plugins/canvas/public/state/middleware/__tests__/workpad_refresh.test.ts index bf69a862d5c30..e451a39df06db 100644 --- a/x-pack/plugins/canvas/public/state/middleware/__tests__/workpad_refresh.test.ts +++ b/x-pack/plugins/canvas/public/state/middleware/__tests__/workpad_refresh.test.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); // actions/elements has some dependencies on ui/new_platform. jest.mock('../../../lib/app_state'); import { workpadRefresh } from '../workpad_refresh'; diff --git a/x-pack/plugins/canvas/public/state/reducers/embeddables.test.ts b/x-pack/plugins/canvas/public/state/reducers/embeddables.test.ts index 5b1192630897a..08b59e5fedc44 100644 --- a/x-pack/plugins/canvas/public/state/reducers/embeddables.test.ts +++ b/x-pack/plugins/canvas/public/state/reducers/embeddables.test.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); + import { State } from '../../../types'; import { updateEmbeddableExpression } from '../actions/embeddable'; import { embeddableReducer } from './embeddable'; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_form.test.js b/x-pack/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_form.test.js index eda275ba50c1a..cdb107016e465 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_form.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/components/auto_follow_pattern_form.test.js @@ -11,8 +11,6 @@ jest.mock('../services/auto_follow_pattern_validators', () => ({ validateLeaderIndexPattern: jest.fn(), })); -jest.mock('ui/new_platform'); - describe(' { describe('updateFormErrors()', () => { it('should merge errors with existing fieldsErrors', () => { diff --git a/x-pack/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js b/x-pack/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js index 93da20a8ed93c..72c4832a631a7 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/components/follower_index_form/follower_index_form.test.js @@ -6,8 +6,6 @@ import { updateFields, updateFormErrors } from './follower_index_form'; -jest.mock('ui/new_platform'); - describe(' state transitions', () => { it('updateFormErrors() should merge errors with existing fieldsErrors', () => { const errors = { name: 'Some error' }; diff --git a/x-pack/plugins/cross_cluster_replication/public/app/services/auto_follow_pattern_validators.test.js b/x-pack/plugins/cross_cluster_replication/public/app/services/auto_follow_pattern_validators.test.js index 11ec125c17d59..924bbe708c73a 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/services/auto_follow_pattern_validators.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/services/auto_follow_pattern_validators.test.js @@ -6,8 +6,6 @@ import { validateAutoFollowPattern } from './auto_follow_pattern_validators'; -jest.mock('ui/new_platform'); - describe('Auto-follow pattern validators', () => { describe('validateAutoFollowPattern()', () => { it('returns empty object when autoFollowPattern is undefined', () => { diff --git a/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js b/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js index 01dccf70a21d6..a3655df1f8956 100644 --- a/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js +++ b/x-pack/plugins/cross_cluster_replication/public/app/store/reducers/api.test.js @@ -8,7 +8,6 @@ import { reducer, initialState } from './api'; import { API_STATUS } from '../../constants'; import { apiRequestStart, apiRequestEnd, setApiError } from '../actions'; -jest.mock('ui/new_platform'); jest.mock('../../constants', () => ({ API_STATUS: { IDLE: 'idle', diff --git a/x-pack/plugins/graph/public/components/search_bar.test.tsx b/x-pack/plugins/graph/public/components/search_bar.test.tsx index 100122af943e1..1b783df1b7d02 100644 --- a/x-pack/plugins/graph/public/components/search_bar.test.tsx +++ b/x-pack/plugins/graph/public/components/search_bar.test.tsx @@ -21,7 +21,6 @@ import { ReactWrapper } from 'enzyme'; import { createMockGraphStore } from '../state_management/mocks'; import { Provider } from 'react-redux'; -jest.mock('ui/new_platform'); jest.mock('../services/source_modal', () => ({ openSourceModal: jest.fn() })); const waitForIndexPatternFetch = () => new Promise((r) => setTimeout(r)); diff --git a/x-pack/plugins/graph/public/state_management/mocks.ts b/x-pack/plugins/graph/public/state_management/mocks.ts index d32bc9a175a47..f28f51544a6ed 100644 --- a/x-pack/plugins/graph/public/state_management/mocks.ts +++ b/x-pack/plugins/graph/public/state_management/mocks.ts @@ -17,8 +17,6 @@ import { GraphStoreDependencies, createRootReducer, GraphStore, GraphState } fro import { Workspace, GraphWorkspaceSavedObject, IndexPatternSavedObject } from '../types'; import { IndexPattern } from '../../../../../src/plugins/data/public'; -jest.mock('ui/new_platform'); - export interface MockedGraphEnvironment { store: GraphStore; mockedDeps: jest.Mocked; diff --git a/x-pack/plugins/index_lifecycle_management/public/application/services/ui_metric.test.ts b/x-pack/plugins/index_lifecycle_management/public/application/services/ui_metric.test.ts index 7c7c0b70c0eed..81eb1c8cad135 100644 --- a/x-pack/plugins/index_lifecycle_management/public/application/services/ui_metric.test.ts +++ b/x-pack/plugins/index_lifecycle_management/public/application/services/ui_metric.test.ts @@ -14,7 +14,6 @@ import { } from '../constants/'; import { getUiMetricsForPhases } from './ui_metric'; -jest.mock('ui/new_platform'); describe('getUiMetricsForPhases', () => { test('gets cold phase', () => { diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_details.test.ts b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_details.test.ts index a112d73230b82..2a0585d61d6f6 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_details.test.ts +++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_details.test.ts @@ -12,11 +12,6 @@ import { ComponentTemplateDeserialized } from '../../shared_imports'; const { setup } = pageHelpers.componentTemplateDetails; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - const COMPONENT_TEMPLATE: ComponentTemplateDeserialized = { name: 'comp-1', template: { diff --git a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_list.test.ts b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_list.test.ts index bd6ac27375836..9bf7df263ec26 100644 --- a/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_list.test.ts +++ b/x-pack/plugins/index_management/public/application/components/component_templates/__jest__/client_integration/component_template_list.test.ts @@ -14,11 +14,6 @@ import { API_BASE_PATH } from './helpers/constants'; const { setup } = pageHelpers.componentTemplateList; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - describe('', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: ComponentTemplateListTestBed; diff --git a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_list.test.ts b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_list.test.ts index 8d6a83a625651..c0acc39ca35a1 100644 --- a/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_list.test.ts +++ b/x-pack/plugins/ingest_pipelines/__jest__/client_integration/ingest_pipelines_list.test.ts @@ -13,11 +13,6 @@ import { PipelineListTestBed } from './helpers/pipelines_list.helpers'; const { setup } = pageHelpers.pipelinesList; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - describe('', () => { const { server, httpRequestsMockHelpers } = setupEnvironment(); let testBed: PipelineListTestBed; diff --git a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx index 52524d0c9a5fa..c5d6ced76b5c0 100644 --- a/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx +++ b/x-pack/plugins/maps/public/classes/sources/ems_file_source/ems_file_source.test.tsx @@ -6,7 +6,6 @@ import { EMSFileSource } from './ems_file_source'; -jest.mock('ui/new_platform'); jest.mock('../../layers/vector_layer/vector_layer', () => {}); function makeEMSFileSource(tooltipProperties: string[]) { diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.test.ts index 87abbedfdf50e..cf0170ab7f1bd 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.test.ts @@ -11,8 +11,6 @@ import _ from 'lodash'; import { AGG_TYPE } from '../../../../common/constants'; import { AggDescriptor } from '../../../../common/descriptor_types'; -jest.mock('ui/new_platform'); - const sumFieldName = 'myFieldGettingSummed'; const metricExamples = [ { diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 37193e148bdc7..43bfb74bf54b6 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -6,7 +6,6 @@ import { MapExtent, MapFilters } from '../../../../common/descriptor_types'; jest.mock('../../../kibana_services'); -jest.mock('ui/new_platform'); import { getIndexPatternService, getSearchService } from '../../../kibana_services'; import { ESGeoGridSource } from './es_geo_grid_source'; diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.test.js b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.test.js index f6779206868a5..060096157f578 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.test.js +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.test.js @@ -6,7 +6,6 @@ import { ESTermSource, extractPropertiesMap } from './es_term_source'; -jest.mock('ui/new_platform'); jest.mock('../../layers/vector_layer/vector_layer', () => {}); const indexPatternTitle = 'myIndex'; diff --git a/x-pack/plugins/maps/public/classes/styles/heatmap/components/heatmap_style_editor.test.tsx b/x-pack/plugins/maps/public/classes/styles/heatmap/components/heatmap_style_editor.test.tsx index aa4dbc67e8e4d..f082e67512099 100644 --- a/x-pack/plugins/maps/public/classes/styles/heatmap/components/heatmap_style_editor.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/heatmap/components/heatmap_style_editor.test.tsx @@ -9,8 +9,6 @@ import { shallow } from 'enzyme'; import { HeatmapStyleEditor } from './heatmap_style_editor'; -jest.mock('ui/new_platform'); - describe('HeatmapStyleEditor', () => { test('is rendered', () => { const component = shallow( diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx index 62a6a59dd091b..c3610cbc78e15 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_color_property.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); jest.mock('../components/vector_style_editor', () => ({ VectorStyleEditor: () => { return
mockVectorStyleEditor
; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx index af93c8e0c9d6d..06987ab8bcc48 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_icon_property.test.tsx @@ -6,7 +6,6 @@ import { shallow } from 'enzyme'; -jest.mock('ui/new_platform'); jest.mock('../components/vector_style_editor', () => ({ VectorStyleEditor: () => { return
mockVectorStyleEditor
; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx index db44ae0da562d..c5298067f6cbe 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_size_property.test.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('ui/new_platform'); jest.mock('../components/vector_style_editor', () => ({ VectorStyleEditor: () => { return
mockVectorStyleEditor
; diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js index a85cd0cc86407..28801a402ca14 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.test.js @@ -14,7 +14,6 @@ import { } from '../../../../common/constants'; jest.mock('../../../kibana_services'); -jest.mock('ui/new_platform'); class MockField { constructor({ fieldName }) { diff --git a/x-pack/plugins/security/server/authorization/authorization_service.ts b/x-pack/plugins/security/server/authorization/authorization_service.ts index 4190499cbd5f4..2dead301b298a 100644 --- a/x-pack/plugins/security/server/authorization/authorization_service.ts +++ b/x-pack/plugins/security/server/authorization/authorization_service.ts @@ -5,7 +5,7 @@ */ import { Subscription, Observable } from 'rxjs'; -import { UICapabilities } from 'ui/capabilities'; +import type { Capabilities as UICapabilities } from '../../../../../src/core/types'; import { LoggerFactory, KibanaRequest, diff --git a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts index c126be1b07f6e..41d596d570fb9 100644 --- a/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts +++ b/x-pack/plugins/security/server/authorization/disable_ui_capabilities.ts @@ -5,7 +5,7 @@ */ import { flatten, isObject, mapValues } from 'lodash'; -import { UICapabilities } from 'ui/capabilities'; +import type { Capabilities as UICapabilities } from '../../../../../src/core/types'; import { KibanaRequest, Logger } from '../../../../../src/core/server'; import { Feature } from '../../../features/server'; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts index 0abc47686a6b4..6021ab2a42c90 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/home.test.ts @@ -20,13 +20,6 @@ import { REPOSITORY_NAME } from './helpers/constant'; const { setup } = pageHelpers.home; -jest.mock('ui/new_platform'); - -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - // Mocking FormattedDate and FormattedTime due to timezone differences on CI jest.mock('@kbn/i18n/react', () => { const original = jest.requireActual('@kbn/i18n/react'); diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_add.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_add.test.ts index f480e937f162a..dc568161d4fb4 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_add.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_add.test.ts @@ -17,11 +17,6 @@ import { DEFAULT_POLICY_SCHEDULE } from '../../public/application/constants'; const { setup } = pageHelpers.policyAdd; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - // mock for EuiSelectable's virtualization jest.mock('react-virtualized-auto-sizer', () => { return ({ diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts index 7eec80890ca86..a5b2ec73b85cd 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/policy_edit.test.ts @@ -18,11 +18,6 @@ const { setup: setupPolicyAdd } = pageHelpers.policyAdd; const EXPIRE_AFTER_VALUE = '5'; const EXPIRE_AFTER_UNIT = TIME_UNITS.MINUTE; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - describe('', () => { let testBed: PolicyFormTestBed; let testBedPolicyAdd: PolicyFormTestBed; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts index 2a9e17a1c9c4d..e0c9aeffa09e8 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_add.test.ts @@ -11,16 +11,9 @@ import { RepositoryType } from '../../common/types'; import { setupEnvironment, pageHelpers, nextTick } from './helpers'; import { RepositoryAddTestBed } from './helpers/repository_add.helpers'; -jest.mock('ui/new_platform'); - const { setup } = pageHelpers.repositoryAdd; const repositoryTypes = ['fs', 'url', 'source', 'azure', 'gcs', 's3', 'hdfs']; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - describe('', () => { let testBed: RepositoryAddTestBed; diff --git a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts index bab276584966b..1606db07b57b4 100644 --- a/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts +++ b/x-pack/plugins/snapshot_restore/__jest__/client_integration/repository_edit.test.ts @@ -12,16 +12,9 @@ import { RepositoryEditTestSubjects } from './helpers/repository_edit.helpers'; import { RepositoryAddTestSubjects } from './helpers/repository_add.helpers'; import { REPOSITORY_EDIT } from './helpers/constant'; -jest.mock('ui/new_platform'); - const { setup } = pageHelpers.repositoryEdit; const { setup: setupRepositoryAdd } = pageHelpers.repositoryAdd; -jest.mock('ui/i18n', () => { - const I18nContext = ({ children }: any) => children; - return { I18nContext }; -}); - describe('', () => { let testBed: TestBed; let testBedRepositoryAdd: TestBed; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 83dac4912d4ac..89a020bd044cc 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -253,31 +253,6 @@ "charts.controls.rangeErrorMessage": "値は{min}と{max}の間でなければなりません", "charts.controls.vislibBasicOptions.legendPositionLabel": "凡例位置", "charts.controls.vislibBasicOptions.showTooltipLabel": "ツールヒントを表示", - "common.ui.flotCharts.aprLabel": "4 月", - "common.ui.flotCharts.augLabel": "8 月", - "common.ui.flotCharts.decLabel": "12 月", - "common.ui.flotCharts.febLabel": "2 月", - "common.ui.flotCharts.friLabel": "金", - "common.ui.flotCharts.janLabel": "1 月", - "common.ui.flotCharts.julLabel": "7 月", - "common.ui.flotCharts.junLabel": "6 月", - "common.ui.flotCharts.marLabel": "3 月", - "common.ui.flotCharts.mayLabel": "5 月", - "common.ui.flotCharts.monLabel": "月", - "common.ui.flotCharts.novLabel": "11 月", - "common.ui.flotCharts.octLabel": "10 月", - "common.ui.flotCharts.pie.unableToDrawLabelsInsideCanvasErrorMessage": "キャンバス内のラベルではパイを作成できません", - "common.ui.flotCharts.satLabel": "土", - "common.ui.flotCharts.sepLabel": "9 月", - "common.ui.flotCharts.sunLabel": "日", - "common.ui.flotCharts.thuLabel": "木", - "common.ui.flotCharts.tueLabel": "火", - "common.ui.flotCharts.wedLabel": "水", - "common.ui.stateManagement.unableToParseUrlErrorMessage": "URL をパースできません", - "common.ui.stateManagement.unableToRestoreUrlErrorMessage": "URL を完全に復元できません。共有機能を使用していることを確認してください。", - "common.ui.stateManagement.unableToStoreHistoryInSessionErrorMessage": "セッションがいっぱいで安全に削除できるアイテムが見つからないため、Kibana は履歴アイテムを保存できません。\n\nこれは大抵新規タブに移動することで解決されますが、より大きな問題が原因である可能性もあります。このメッセージが定期的に表示される場合は、{gitHubIssuesUrl} で問題を報告してください。", - "common.ui.url.replacementFailedErrorMessage": "置換に失敗、未解決の表現式: {expr}", - "common.ui.url.savedObjectIsMissingNotificationMessage": "保存されたオブジェクトがありません", "console.autocomplete.addMethodMetaText": "メソド", "console.consoleDisplayName": "コンソール", "console.consoleMenu.copyAsCurlMessage": "リクエストが URL としてコピーされました", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 8b3a1941f450a..68c1b5dab9295 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -253,31 +253,6 @@ "charts.controls.rangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", "charts.controls.vislibBasicOptions.legendPositionLabel": "图例位置", "charts.controls.vislibBasicOptions.showTooltipLabel": "显示工具提示", - "common.ui.flotCharts.aprLabel": "四月", - "common.ui.flotCharts.augLabel": "八月", - "common.ui.flotCharts.decLabel": "十二月", - "common.ui.flotCharts.febLabel": "二月", - "common.ui.flotCharts.friLabel": "周五", - "common.ui.flotCharts.janLabel": "一月", - "common.ui.flotCharts.julLabel": "七月", - "common.ui.flotCharts.junLabel": "六月", - "common.ui.flotCharts.marLabel": "三月", - "common.ui.flotCharts.mayLabel": "五月", - "common.ui.flotCharts.monLabel": "周一", - "common.ui.flotCharts.novLabel": "十一月", - "common.ui.flotCharts.octLabel": "十月", - "common.ui.flotCharts.pie.unableToDrawLabelsInsideCanvasErrorMessage": "无法用画布内包含的标签绘制饼图", - "common.ui.flotCharts.satLabel": "周六", - "common.ui.flotCharts.sepLabel": "九月", - "common.ui.flotCharts.sunLabel": "周日", - "common.ui.flotCharts.thuLabel": "周四", - "common.ui.flotCharts.tueLabel": "周二", - "common.ui.flotCharts.wedLabel": "周三", - "common.ui.stateManagement.unableToParseUrlErrorMessage": "无法解析 URL", - "common.ui.stateManagement.unableToRestoreUrlErrorMessage": "无法完整还原 URL,确保使用共享功能。", - "common.ui.stateManagement.unableToStoreHistoryInSessionErrorMessage": "Kibana 无法将历史记录项存储在您的会话中,因为其已满,并且似乎没有任何可安全删除的项。\n\n通常可通过移至新的标签页来解决此问题,但这会导致更大的问题。如果您有规律地看到此消息,请在 {gitHubIssuesUrl} 提交问题。", - "common.ui.url.replacementFailedErrorMessage": "替换失败,未解析的表达式:{expr}", - "common.ui.url.savedObjectIsMissingNotificationMessage": "已保存对象缺失", "console.autocomplete.addMethodMetaText": "方法", "console.consoleDisplayName": "控制台", "console.consoleMenu.copyAsCurlMessage": "请求已复制为 cURL", diff --git a/x-pack/plugins/ui_actions_enhanced/public/can_inherit_time_range.test.ts b/x-pack/plugins/ui_actions_enhanced/public/can_inherit_time_range.test.ts index 5f2e9d07c4bcb..9fb3a54d5a182 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/can_inherit_time_range.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/can_inherit_time_range.test.ts @@ -10,8 +10,6 @@ import { HelloWorldEmbeddable } from '../../../../examples/embeddable_examples/p /** eslint-enable */ import { TimeRangeEmbeddable, TimeRangeContainer } from './test_helpers'; -jest.mock('ui/new_platform'); - test('canInheritTimeRange returns false if embeddable is inside container without a time range', () => { const embeddable = new TimeRangeEmbeddable( { id: '1234', timeRange: { from: 'noxw-15m', to: 'now' } }, diff --git a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts index 7919d2d2482ae..9af79c9ac5259 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts @@ -21,8 +21,6 @@ import { import { nextTick } from 'test_utils/enzyme_helpers'; import { ReactElement } from 'react'; -jest.mock('ui/new_platform'); - const createOpenModalMock = () => { const mock = jest.fn(); mock.mockReturnValue({ close: jest.fn() }); diff --git a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts index 7f831973aea5c..e567828598b31 100644 --- a/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts +++ b/x-pack/test/ui_capabilities/common/services/ui_capabilities.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import axios, { AxiosInstance } from 'axios'; -import { UICapabilities } from 'ui/capabilities'; +import type { Capabilities as UICapabilities } from 'src/core/types'; import { format as formatUrl } from 'url'; import util from 'util'; import { ToolingLog } from '@kbn/dev-utils'; diff --git a/x-pack/test_utils/jest/config.js b/x-pack/test_utils/jest/config.js index 7bb073023b7f8..c94fe02d2f4bd 100644 --- a/x-pack/test_utils/jest/config.js +++ b/x-pack/test_utils/jest/config.js @@ -17,7 +17,6 @@ export default { ], collectCoverageFrom: ['legacy/plugins/**/*.js', 'legacy/common/**/*.js', 'legacy/server/**/*.js'], moduleNameMapper: { - '^ui/(.*)': '**/public/$1', '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/src/dev/jest/mocks/file_mock.js', '\\.(css|less|scss)$': '/../src/dev/jest/mocks/style_mock.js', diff --git a/x-pack/tsconfig.json b/x-pack/tsconfig.json index e978702a35634..35e1800c6fbd1 100644 --- a/x-pack/tsconfig.json +++ b/x-pack/tsconfig.json @@ -22,9 +22,6 @@ "paths": { "kibana/public": ["src/core/public"], "kibana/server": ["src/core/server"], - "ui/*": [ - "src/legacy/ui/public/*" - ], "plugins/xpack_main/*": [ "x-pack/legacy/plugins/xpack_main/public/*" ],