diff --git a/NOTICE.txt b/NOTICE.txt index 23def5d32730..d1903a471341 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -4,32 +4,6 @@ Copyright 2012-2019 Elasticsearch B.V. --- This product has relied on ASTExplorer that is licensed under MIT. ---- -This product includes code that was extracted from angular-ui-bootstrap@0.13.1 -which is available under an "MIT" license - -The MIT License - -Copyright (c) 2012-2016 the AngularUI Team, http://angular-ui.github.io/bootstrap/ - -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. - --- This product uses Noto fonts that are licensed under the SIL Open Font License, Version 1.1. diff --git a/x-pack/plugins/ml/public/app.js b/x-pack/plugins/ml/public/app.js index 16d0f877a6a4..f5f36bffbba3 100644 --- a/x-pack/plugins/ml/public/app.js +++ b/x-pack/plugins/ml/public/app.js @@ -16,7 +16,6 @@ import 'ui/autoload/all'; import 'ui/kbn_top_nav'; import 'plugins/ml/access_denied'; -import 'plugins/ml/lib/angular_bootstrap_patch'; import 'plugins/ml/jobs'; import 'plugins/ml/services/calendar_service'; import 'plugins/ml/components/messagebar'; diff --git a/x-pack/plugins/ml/public/components/job_select_list/_index.scss b/x-pack/plugins/ml/public/components/job_select_list/_index.scss deleted file mode 100644 index 800524055a68..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'job_select_list'; \ No newline at end of file diff --git a/x-pack/plugins/ml/public/components/job_select_list/_job_select_list.scss b/x-pack/plugins/ml/public/components/job_select_list/_job_select_list.scss deleted file mode 100644 index 5dc70430d4db..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/_job_select_list.scss +++ /dev/null @@ -1,343 +0,0 @@ -// SASSTODO: This file needs a rewrite. Needs to be variablized and have actual calculations rather than pixels. -// Lots of custom colors in here need to be replaced. - -// SASSTODO: EXTREMELY DANGEROUS OVERWRITE -// Little worried about touching it now -navbar { - padding: 10px; - background-color: #EFF0F1; -} - -// SASSTODO: EXTREMELY DANGEROUS OVERWRITE -// Little worried about touching it now -navbar button[disabled] { - color: #FFF; - background-color: #0079a5; -} - -.ml-job-selector { - margin: -9px -14px; - - .header { - padding: 9px 14px; - border-bottom: 1px solid #eeeeee; - } - .footer { - text-align: center; - padding: 9px; - border-top: 1px solid #eee; - - .kuiButton--primary:focus { - background-color: #0079a5; - } - } - - .select-all-controls { - margin-bottom: 0px; - padding-bottom: 4px; - border-bottom: 1px solid #eee; - & > div { - width: 280px; - display: inline-block; - font-weight: bold; - } - } - - // SASSTODO: Replace with proper selector - hr { - margin: 2px 0px; - } - - // SASSTODO: Replace with proper selector - label { - margin-bottom: 0px; - font-weight: normal; - } - - // SASSTODO: Replace with proper selector - input { - margin-right: 4px; - margin-left: 4px; - } - - .apply-time-range { - margin-top: 16px; - position: absolute; - right: 16px; - bottom: 12px; - - // SASSTODO: Replace with proper selector - input { - margin-right: 0px; - } - } - - .select-list { - max-height: 400px; - overflow-y: auto; - overflow-x: hidden; - - background-color: #F5F7FA; - - .list-section-title { - margin: 14px; - margin-bottom: 5px; - } - - .list-section { - border: 1px solid #e0e0e0; - background-color: #ffffff; - margin: 14px; - margin-top: 5px; - padding: 10px; - border-radius: 3px; - } - .list-section-single { - margin: -1px; - border-radius: 0px; - border: 0px; - } - - .group-title { - border-left: none; - border-right: none; - .expand-group-button { - padding-right: 2px; - width: 20px; - } - } - - .job-row { - white-space: nowrap; - vertical-align: top; - - .use-time-range { - margin-top: 4px; - } - - .job-row-inner { - display: inline-block; - font-size: 13px; - font-weight: normal; - color: #444444; - white-space: nowrap; - - margin: 0px; - padding: 0px; - line-height: 20px; - - // SASSTODO: Replace with proper selector - & > label > div { - width: 280px; - display: inline-block; - vertical-align: top; - height: 30px; - padding-top: 6px; - overflow: hidden; - text-overflow: ellipsis; - padding-right: 4px; - - .disabled-job { - color: #cccccc; - } - } - - // SASSTODO: Replace with proper selector - & > label > div:nth-child(2) { - width: 310px; - border-left: 1px solid #eee; - border-right: 1px solid #eee; - padding-left: 4px; - padding-top: 6px; - margin-left: -3px; - - .disabled-job { - background-color: #cccccc; - } - } - - .job-in-group { - padding-left: 10px; - } - } - - .button-container { - display: inline-block; - vertical-align: top; - padding-right: 2px; - } - - .gant-bar { - background-color: #79adda; - height: 14px; - border-radius: 2px; - } - - // SASSTODO: This needs to be rebuilt - .gant-bar-running { - background-image:-webkit-gradient(linear, - 0 100%, 100% 0, - color-stop(0.25, rgba(255, 255, 255, 0.15)), - color-stop(0.25, transparent), - color-stop(0.5, transparent), - color-stop(0.5, rgba(255, 255, 255, 0.15)), - color-stop(0.75, rgba(255, 255, 255, 0.15)), - color-stop(0.75, transparent), - to(transparent)); - background-image:-webkit-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:-moz-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:-o-linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - background-image:linear-gradient(45deg, - rgba(255, 255, 255, 0.15) 25%, - transparent 25%, - transparent 50%, - rgba(255, 255, 255, 0.15) 50%, - rgba(255, 255, 255, 0.15) 75%, - transparent 75%, - transparent); - -webkit-background-size:40px 40px; - -moz-background-size:40px 40px; - -o-background-size:40px 40px; - background-size:40px 40px; - - -webkit-animation:progress-bar-stripes 2s linear infinite; - -moz-animation:progress-bar-stripes 2s linear infinite; - -ms-animation:progress-bar-stripes 2s linear infinite; - -o-animation:progress-bar-stripes 2s linear infinite; - animation:progress-bar-stripes 2s linear infinite; - } - - .gant-back-edge { - height: 18px; - border-left: 1px solid #d6d6d6; - border-right: 1px solid #d6d6d6; - margin-bottom: -16px; - padding-top: 9px; - - // SASSTODO: Needs a poper selector - div { - height: 1px; - border-top: 1px dashed #d6d6d6; - } - } - } - .job-row:hover { - .job-row-inner { - - // SASSTODO: Needs a poper selector - & > label > div { - background-color: #f2f2f2; - } - } - } - } -} - -.ml-select-list-on { - color: #ffffff; - background-color: #000099; -} - -.ml-select-list-on { - color: #ffffff; - background-color: #000099; -} - -.ml-job-select-btn-container { - .popover { - min-width: 640px; - max-width: 660px !important; - left: 9px !important; - .arrow { - left: 150px !important; - } - } - - .ml-job-select-btn-label { - background-color:#9c9c9c; - color: #ffffff; - display:inline-block; - padding:0px 10px; - border: 0px solid #ecf0f1; - border-top-style: solid; - border-right-style: none; - border-bottom-style: solid; - border-left-style: solid; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - height: 34px; - line-height:33px; - font-size: 12px; - } - - .ml-job-select-btn { - width: 300px; - height: 34px; - text-align: left; - color: #444444; - background-color: white; - border: 1px solid #ffffff; - border-left-width: 0px; - border-radius: 0px 4px 4px 0px; - padding: 6px 16px; - padding-left: 10px; - transition: border-color ease-in-out .15s; - font-size: 14px; - line-height: 20px; - cursor: pointer; - - .ml-job-select-btn-text { - float: left; - width: 260px; - overflow-x: hidden; - display: inline-flex; - white-space: nowrap; - - span { - overflow-x: hidden; - text-overflow: ellipsis; - padding-right: 5px; - } - } - - .caret { - float: right; - margin-top: 6px; - display:inline-block; - } - } - - .ml-job-select-btn:hover, .ml-job-select-btn:focus, .ml-job-select-btn:active { - color: #444444; - background-color: #ffffff; - border-color: #0079a5; - border-left-width: 1px; - padding-left: 9px; - transform: none; - } - - .ml-job-select-btn:focus { - outline: none; - box-shadow: none; - } -} diff --git a/x-pack/plugins/ml/public/components/job_select_list/index.js b/x-pack/plugins/ml/public/components/job_select_list/index.js deleted file mode 100644 index bba2809644d8..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/index.js +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - - -import './job_select_list_directive'; -import './job_select_button_directive.js'; -import './job_select_service.js'; diff --git a/x-pack/plugins/ml/public/components/job_select_list/job_select_button.html b/x-pack/plugins/ml/public/components/job_select_list/job_select_button.html deleted file mode 100644 index 6381b2538027..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/job_select_button.html +++ /dev/null @@ -1,27 +0,0 @@ -
-
-
-
- {{description.txt}} - -
-
-
diff --git a/x-pack/plugins/ml/public/components/job_select_list/job_select_button_directive.js b/x-pack/plugins/ml/public/components/job_select_list/job_select_button_directive.js deleted file mode 100644 index 51511ab544e9..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/job_select_button_directive.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -/* - * ml-job-select-list directive for rendering a multi-select control for selecting - * one or more jobs from the list of configured jobs. - */ - -import template from './job_select_button.html'; - -import 'ui/accessibility/kbn_accessible_click'; -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/ml'); - -import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service'; - -module.directive('jobSelectButton', function (Private) { - - const mlJobSelectService = Private(JobSelectServiceProvider); - - function link(scope) { - scope.selectJobBtnJobIdLabel = ''; - scope.unsafeHtml = ''; - scope.description = scope.singleSelection ? mlJobSelectService.singleJobDescription : mlJobSelectService.description; - - scope.createMenu = function () { - let txt = ' -
- - -
-
-
- - {{ ::'xpack.ml.jobSelectList.groupsTitle' | i18n: { defaultMessage: 'Groups' } }} -
-
-
-
-
- -
-
-
-
-
- -
- - {{ ::'xpack.ml.jobSelectList.jobsTitle' | i18n: { defaultMessage: 'Jobs' } }} -
-
-
-
- -
-
-
-
- - - - -
- -
- - diff --git a/x-pack/plugins/ml/public/components/job_select_list/job_select_list_directive.js b/x-pack/plugins/ml/public/components/job_select_list/job_select_list_directive.js deleted file mode 100644 index 7fb3e07ab1aa..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/job_select_list_directive.js +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -/* - * ml-job-select-list directive for rendering a multi-select control for selecting - * one or more jobs from the list of configured jobs. - */ - -import _ from 'lodash'; -import $ from 'jquery'; -import moment from 'moment'; -import d3 from 'd3'; - -import template from './job_select_list.html'; -import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils'; -import { mlJobService } from 'plugins/ml/services/job_service'; -import { JobSelectServiceProvider } from 'plugins/ml/components/job_select_list/job_select_service'; - -import { timefilter } from 'ui/timefilter'; -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/ml'); - -module.directive('mlJobSelectList', function (Private) { - return { - restrict: 'AE', - replace: true, - transclude: true, - template, - controller: function ($scope, i18n) { - const mlJobSelectService = Private(JobSelectServiceProvider); - $scope.jobs = []; - $scope.groups = []; - $scope.homelessJobs = []; - $scope.singleSelection = false; - $scope.timeSeriesOnly = false; - $scope.noJobsCreated = undefined; - $scope.applyTimeRange = mlJobSelectService.jobSelectListState.applyTimeRange; - $scope.urlSelectedIds = {}; - $scope.selected = {}; - $scope.allGroupsSelected = false; - $scope.allJobsSelected = false; - $scope.selectedJobRadio = ''; - $scope.selectedCount = 0; - - mlJobService.loadJobs() - .then((resp) => { - if (resp.jobs.length > 0) { - $scope.noJobsCreated = false; - const jobs = []; - resp.jobs.forEach(job => { - if (job.groups && job.groups.length) { - job.groups.forEach(group => { - jobs.push(createJob(`${group}.${job.job_id}`, group, job)); - }); - } else { - jobs.push(createJob(job.job_id, null, job)); - } - }); - normalizeTimes(jobs); - $scope.jobs = jobs; - const { groups, homeless } = createGroups($scope.jobs); - $scope.groups = groups; - $scope.homelessJobs = homeless; - $scope.selected = { - groups: [], - jobs: [] - }; - - // count all jobs, including duplicates in groups. - // if it's the same as the number of ids passed in, tick all jobs - const jobCount = resp.jobs.reduce((sum, job) => (sum + ((job.groups === undefined) ? 1 : job.groups.length)), 0); - const selectAll = (jobCount === $scope.urlSelectedIds.jobs.length); - - // create the groups and jobs which are used in the menu - groups.forEach(group => { - $scope.selected.groups.push({ - id: group.id, - selected: group.selected, - // TODO: is the selectable property of a group still needed? - selectable: group.selectable, - timeRange: group.timeRange, - isGroup: true, - }); - }); - - jobs.forEach(job => { - if ($scope.selected.jobs.find(j => j.id === job.name) === undefined) { - $scope.selected.jobs.push({ - id: job.name, - selected: selectAll || job.selected, - disabled: job.disabled, - timeRange: job.timeRange, - running: job.running, - isGroup: false - }); - } - }); - - $scope.allJobsSelected = areAllJobsSelected(); - $scope.allGroupsSelected = areAllGroupsSelected(); - createSelectedCount(); - - // if in single selection mode, set the radio button controller ($scope.selectedJobRadio) - // to the selected job id - if ($scope.singleSelection === true) { - $scope.jobs.forEach(j => { - if (j.selected) { - $scope.selectedJobRadio = j.name; - } - }); - } - } else { - $scope.noJobsCreated = true; - } - $scope.$applyAsync(); - }).catch((resp) => { - console.log('mlJobSelectList controller - error getting job info from ES:', resp); - }); - - function createJob(jobId, groupId, job) { - return { - id: jobId, - name: job.job_id, - group: groupId, - isGroup: false, - selected: _.includes($scope.urlSelectedIds.jobs, job.job_id), - disabled: !($scope.timeSeriesOnly === false || isTimeSeriesViewJob(job) === true), - running: (job.datafeed_config && job.datafeed_config.state === 'started'), - timeRange: { - to: job.data_counts.latest_record_timestamp, - from: job.data_counts.earliest_record_timestamp, - fromPx: 0, - toPx: 0, - widthPx: 0, - label: '' - } - }; - } - - function createGroups(jobsIn) { - const jobGroups = {}; - const homeless = []; - // first pull all of the groups out of all of the jobs - // keeping homeless (groupless) jobs in a separate list - jobsIn.forEach(job => { - if (job.group !== null) { - if (jobGroups[job.group] === undefined) { - jobGroups[job.group] = [job]; - } else { - jobGroups[job.group].push(job); - } - } else { - homeless.push(job); - } - }); - - const groups = _.map(jobGroups, (jobs, id) => { - const group = { - id, - selected: false, - selectable: true, - expanded: false, - isGroup: true, - jobs - }; - // check to see whether all of the groups jobs have been selected, - // if they have, select the group - if ($scope.singleSelection === false) { - group.selected = _.includes($scope.urlSelectedIds.groups, id); - } - - // create an over all time range for the group - const timeRange = { - to: null, - toMoment: null, - from: null, - fromMoment: null, - fromPx: null, - toPx: null, - widthPx: null, - }; - - jobs.forEach(job => { - job.group = group; - - if (timeRange.to === null || job.timeRange.to > timeRange.to) { - timeRange.to = job.timeRange.to; - timeRange.toMoment = job.timeRange.toMoment; - } - if (timeRange.from === null || job.timeRange.from < timeRange.from) { - timeRange.from = job.timeRange.from; - timeRange.fromMoment = job.timeRange.fromMoment; - } - if (timeRange.toPx === null || job.timeRange.toPx > timeRange.toPx) { - timeRange.toPx = job.timeRange.toPx; - } - if (timeRange.fromPx === null || job.timeRange.fromPx < timeRange.fromPx) { - timeRange.fromPx = job.timeRange.fromPx; - } - }); - timeRange.widthPx = timeRange.toPx - timeRange.fromPx; - timeRange.toMoment = moment(timeRange.to); - timeRange.fromMoment = moment(timeRange.from); - - const fromString = timeRange.fromMoment.format('MMM Do YYYY, HH:mm'); - const toString = timeRange.toMoment.format('MMM Do YYYY, HH:mm'); - timeRange.label = i18n('xpack.ml.jobSelectList.groupTimeRangeLabel', { - defaultMessage: '{fromString} to {toString}', - values: { - fromString, - toString, - } - }); - - group.timeRange = timeRange; - return group; - }); - - return { - groups, - homeless - }; - } - - // apply the selected jobs - $scope.apply = function () { - // if in single selection mode, get the job id from $scope.selectedJobRadio - const selectedJobs = []; - if ($scope.singleSelection) { - selectedJobs.push(...$scope.selected.jobs.filter(j => j.id === $scope.selectedJobRadio)); - } else { - selectedJobs.push(...$scope.selected.jobs.filter(j => j.selected)); - selectedJobs.push(...$scope.selected.groups.filter(g => g.selected)); - } - - if (areAllJobsSelected()) { - // if all jobs have been selected, just store '*' in the url - mlJobSelectService.setJobIds(['*']); - } else { - const jobIds = selectedJobs.map(j => (j.isGroup ? `${j.id}.*` : j.id)); - mlJobSelectService.setJobIds(jobIds); - } - - // if the apply time range checkbox is ticked, - // find the min and max times for all selected jobs - // and apply them to the timefilter - if ($scope.applyTimeRange) { - const times = []; - selectedJobs.forEach(job => { - if (job.timeRange.from !== undefined) { - times.push(job.timeRange.from); - } - if (job.timeRange.to !== undefined) { - times.push(job.timeRange.to); - } - }); - if (times.length) { - const min = _.min(times); - const max = _.max(times); - timefilter.setTime({ - from: moment(min).toISOString(), - to: moment(max).toISOString() - }); - } - } - mlJobSelectService.jobSelectListState.applyTimeRange = $scope.applyTimeRange; - $scope.closePopover(); - }; - - // ticking a job - $scope.toggleSelection = function () { - // check to see if all jobs are now selected - $scope.allJobsSelected = areAllJobsSelected(); - $scope.allGroupsSelected = areAllGroupsSelected(); - createSelectedCount(); - }; - - // ticking the all jobs checkbox - $scope.toggleAllJobsSelection = function () { - const allJobsSelected = areAllJobsSelected(); - $scope.allJobsSelected = !allJobsSelected; - - $scope.selected.jobs.forEach(job => { - job.selected = $scope.allJobsSelected; - }); - - createSelectedCount(); - }; - - // ticking a group - $scope.toggleGroupSelection = function () { - $scope.allGroupsSelected = areAllGroupsSelected(); - createSelectedCount(); - }; - - // ticking the all jobs checkbox - $scope.toggleAllGroupsSelection = function () { - const allGroupsSelected = areAllGroupsSelected(); - $scope.allGroupsSelected = !allGroupsSelected; - - $scope.selected.groups.forEach(group => { - group.selected = $scope.allGroupsSelected; - }); - createSelectedCount(); - }; - - // check to see whether all jobs in the list have been selected - function areAllJobsSelected() { - let allSelected = true; - $scope.selected.jobs.forEach(job => { - if (job.selected === false) { - allSelected = false; - } - }); - return allSelected; - } - - // check to see whether all groups in the list have been selected - function areAllGroupsSelected() { - let allSelected = true; - $scope.selected.groups.forEach(group => { - if (group.selected === false) { - allSelected = false; - } - }); - return allSelected; - } - - function createSelectedCount() { - $scope.selectedCount = 0; - $scope.selected.jobs.forEach(job => { - if (job.selected) { - $scope.selectedCount++; - } - }); - $scope.selected.groups.forEach(group => { - if (group.selected) { - $scope.selectedCount++; - } - }); - } - - // create the data used for the gant charts - function normalizeTimes(jobs) { - const min = _.min(jobs, job => +job.timeRange.from); - const max = _.max(jobs, job => +job.timeRange.to); - - const gantScale = d3.scale.linear().domain([min.timeRange.from, max.timeRange.to]).range([1, 299]); - - jobs.forEach(job => { - if (job.timeRange.to !== undefined && job.timeRange.from !== undefined) { - job.timeRange.fromPx = gantScale(job.timeRange.from); - job.timeRange.toPx = gantScale(job.timeRange.to); - job.timeRange.widthPx = job.timeRange.toPx - job.timeRange.fromPx; - - job.timeRange.toMoment = moment(job.timeRange.to); - job.timeRange.fromMoment = moment(job.timeRange.from); - - const fromString = job.timeRange.fromMoment.format('MMM Do YYYY, HH:mm'); - const toString = job.timeRange.toMoment.format('MMM Do YYYY, HH:mm'); - job.timeRange.label = i18n('xpack.ml.jobSelectList.jobTimeRangeLabel', { - defaultMessage: '{fromString} to {toString}', - values: { - fromString, - toString, - } - }); - } - }); - - } - - $scope.useTimeRange = function (job) { - timefilter.setTime({ - from: job.timeRange.fromMoment.toISOString(), - to: job.timeRange.toMoment.toISOString() - }); - }; - }, - link: function (scope, element, attrs) { - const mlJobSelectService = Private(JobSelectServiceProvider); - scope.timeSeriesOnly = false; - if (attrs.timeseriesonly === 'true') { - scope.timeSeriesOnly = true; - } - - if (attrs.singleSelection === 'true') { - scope.singleSelection = true; - } - - // Make a copy of the list of jobs ids - // '*' is passed to indicate 'All jobs'. - scope.urlSelectedIds = { - groups: [...mlJobSelectService.groupIds], - jobs: [...mlJobSelectService.jobIdsWithGroup], - }; - - // Giving the parent div focus fixes checkbox tick UI selection on IE. - $('.ml-select-list', element).focus(); - } - }; -}); diff --git a/x-pack/plugins/ml/public/components/job_select_list/job_select_service.js b/x-pack/plugins/ml/public/components/job_select_list/job_select_service.js deleted file mode 100644 index 435ed4161345..000000000000 --- a/x-pack/plugins/ml/public/components/job_select_list/job_select_service.js +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - - - -// Service with functions used for broadcasting job picker changes - -import _ from 'lodash'; -import { toastNotifications } from 'ui/notify'; - -import { mlJobService } from 'plugins/ml/services/job_service'; - -let jobSelectService = undefined; - -export function JobSelectServiceProvider($rootScope, globalState, i18n) { - - function checkGlobalState() { - if (globalState.ml === undefined) { - globalState.ml = {}; - globalState.save(); - } - } - checkGlobalState(); - - function loadJobIdsFromGlobalState() { - const jobIds = []; - if (globalState.ml && globalState.ml.jobIds) { - let tempJobIds = []; - if (typeof globalState.ml.jobIds === 'string') { - tempJobIds.push(globalState.ml.jobIds); - } else { - tempJobIds = globalState.ml.jobIds; - } - tempJobIds = tempJobIds.map(id => String(id)); - const invalidIds = getInvalidJobIds(removeGroupIds(tempJobIds)); - warnAboutInvalidJobIds(invalidIds); - - let validIds = _.difference(tempJobIds, invalidIds); - - // if there are no valid ids, warn and then select the first job - if (validIds.length === 0) { - toastNotifications.addWarning(i18n('xpack.ml.jobSelect.noJobsSelectedWarningMessage', { - defaultMessage: 'No jobs selected, auto selecting first job', - })); - - if (mlJobService.jobs.length) { - validIds = [mlJobService.jobs[0].job_id]; - } - } - jobIds.push(...validIds); - - // replace the job ids in the url with the ones which are valid - storeJobIdsInGlobalState(jobIds); - } else { - checkGlobalState(); - - // no jobs selected, use the first in the list - if (mlJobService.jobs.length) { - jobIds.push(mlJobService.jobs[0].job_id); - } - storeJobIdsInGlobalState(jobIds); - } - - return jobIds; - } - - function storeJobIdsInGlobalState(jobIds) { - globalState.ml.jobIds = jobIds; - globalState.save(); - } - - // check that the ids read from the url exist by comparing them to the - // jobs loaded via mlJobsService. - function getInvalidJobIds(ids) { - return ids.filter(id => { - const job = _.find(mlJobService.jobs, { 'job_id': id }); - return (job === undefined && id !== '*'); - }); - } - - function removeGroupIds(jobIds) { - return jobIds.map(id => { - const splitId = id.split('.'); - return (splitId.length > 1) ? splitId[1] : splitId[0]; - }); - } - - function warnAboutInvalidJobIds(invalidIds) { - if (invalidIds.length > 0) { - toastNotifications.addWarning(i18n('xpack.ml.jobSelect.requestedJobsDoesNotExistWarningMessage', { - defaultMessage: `Requested -{invalidIdsLength, plural, one {job {invalidIds} does not exist} other {jobs {invalidIds} do not exist}}`, - values: { - invalidIdsLength: invalidIds.length, - invalidIds, - } - })); - } - } - - function createDescription(jobs) { - let txt = ''; - // add up the number of jobs including duplicates if they belong to multiple groups - const jobCount = mlJobService.jobs.length; - - // add up how many jobs belong to groups and how many don't - const selectedGroupJobs = []; - const groupCounts = {}; - let groupLessJobs = 0; - const splitJobs = jobs.map(job => { - const obj = splitJobId(job); - if (obj.group) { - // keep track of selected jobs from group selection - selectedGroupJobs.push(obj.job); - } - return obj; - }); - - splitJobs.forEach(jobObj => { - if (jobObj.group) { - groupCounts[jobObj.group] = (groupCounts[jobObj.group] || 0) + 1; - } else { - // if job has already been included via group selection don't add as groupless job - if (selectedGroupJobs.includes(jobObj.job) === false) { - groupLessJobs++; - } - } - }); - // All jobs have been selected - if ((_.uniq(selectedGroupJobs).length + groupLessJobs) === jobCount) { - txt = i18n('xpack.ml.jobSelect.allJobsDescription', { - defaultMessage: 'All jobs', - }); - } else { - const wholeGroups = []; - const groups = mlJobService.getJobGroups(); - // work out how many groups have all of their jobs selected - groups.forEach(group => { - const groupCount = groupCounts[group.id]; - if (groupCount !== undefined && groupCount === group.jobs.length) { - // this group has all of it's jobs selected - wholeGroups.push(group.id); - } else { - if (groupCount !== undefined) { - // this job doesn't so add it to the count of groupless jobs - groupLessJobs += groupCount; - } - } - }); - - // show the whole groups first - if (wholeGroups.length) { - txt = wholeGroups[0]; - if (wholeGroups.length > 1 || groupLessJobs > 0) { - const total = (wholeGroups.length - 1) + groupLessJobs; - txt = i18n('xpack.ml.jobSelect.wholeGroupDescription', { - defaultMessage: `{wholeGroup} (with {count, plural, zero {# job} one {# job} other {# jobs}}) and - {total, plural, zero {# other} one {# other} other {# others}}`, - values: { - count: groupCounts[wholeGroups[0]], - wholeGroup: wholeGroups[0], - total, - } - }); - } - } else { - // otherwise just list the job ids - txt = splitJobId(jobs[0]).job; - if (jobs.length > 1) { - txt = i18n('xpack.ml.jobSelect.jobDescription', { - defaultMessage: '{jobId} and {jobsAmount, plural, zero {# other} one {# other} other {# others}}', - values: { - jobId: splitJobId(jobs[0]).job, - jobsAmount: jobs.length - 1, - } - }); - } - } - } - return txt; - } - // function to split the group from the job and return both or just the job - function splitJobId(jobId) { - let obj = {}; - const splitId = jobId.split('.'); - if (splitId.length === 2) { - obj = { group: splitId[0], job: splitId[1] }; - } else { - obj = { job: jobId }; - } - return obj; - } - this.splitJobId = splitJobId; - - // expands `*` into groupId.jobId list - // expands `groupId.*` into `groupId.jobId` list - // returns list of expanded job ids - function expandGroups(jobIds) { - const newJobIds = []; - const groups = mlJobService.getJobGroups(); - jobIds.forEach(jobId => { - if (jobId === '*') { - mlJobService.jobs.forEach(job => { - if (job.groups === undefined) { - newJobIds.push(job.job_id); - } else { - newJobIds.push(...job.groups.map(g => `${g}.${job.job_id}`)); - } - }); - } else { - const splitId = splitJobId(jobId); - if (splitId.group !== undefined && splitId.job === '*') { - const groupId = splitId.group; - const group = groups.find(g => g.id === groupId); - group.jobs.forEach(j => { - newJobIds.push(`${groupId}.${j.job_id}`); - }); - } - else { - newJobIds.push(jobId); - } - } - }); - return newJobIds; - } - - function getGroupIds(jobIds) { - const groupIds = []; - jobIds.forEach(jobId => { - const splitId = splitJobId(jobId); - if (splitId.group !== undefined && splitId.job === '*') { - groupIds.push(splitId.group); - } - }); - return groupIds; - } - - // takes an array of ids. - // this could be a mixture of job ids, group ids or a *. - // stores an expanded list of job ids (i.e. groupId.jobId) and a list of jobs ids only. - // creates the description text used on the job picker button. - function processIds(service, ids) { - const expandedJobIds = expandGroups(ids); - service.jobIdsWithGroup.length = 0; - service.jobIdsWithGroup.push(...expandedJobIds); - service.groupIds = getGroupIds(ids); - service.jobIds.length = 0; - service.jobIds.push(...removeGroupIds(expandedJobIds)); - service.description.txt = createDescription(service.jobIdsWithGroup); - service.singleJobDescription.txt = ids[0]; - setBrowserTitle(service.description.txt); - } - - // display the job id in the tab title - function setBrowserTitle(title) { - document.title = `${title} - Kibana`; - } - - class JobSelectService { - constructor() { - this.jobIds = []; - this.groupIds = []; - this.description = { txt: '' }; - this.singleJobDescription = { txt: '' }; - this.jobSelectListState = { - applyTimeRange: true - }; - this.jobIdsWithGroup = []; - this.splitJobId = splitJobId; - } - - // Broadcasts that a change has been made to the selected jobs. - broadcastJobSelectionChange() { - $rootScope.$broadcast('jobSelectionChange', this.getSelectedJobIds()); - } - - // Add a listener for changes to the selected jobs. - listenJobSelectionChange(scope, callback) { - const handler = $rootScope.$on('jobSelectionChange', callback); - scope.$on('$destroy', handler); - } - - // called externally to retrieve the selected jobs ids. - // passing in `true` will load the jobs ids from the URL first - getSelectedJobIds(loadFromURL) { - if (loadFromURL) { - processIds(this, loadJobIdsFromGlobalState()); - } - return this.jobIds; - } - - // called externally to set the job ids. - // job ids are added to the URL and an event is broadcast for anything listening. - // e.g. the anomaly explorer or time series explorer. - // currently only called by the jobs selection menu. - setJobIds(jobIds) { - processIds(this, jobIds); - storeJobIdsInGlobalState(jobIds); - this.broadcastJobSelectionChange(); - } - } - - if (jobSelectService === undefined) { - jobSelectService = new JobSelectService(); - } - return jobSelectService; -} diff --git a/x-pack/plugins/ml/public/lib/angular_bootstrap_patch.js b/x-pack/plugins/ml/public/lib/angular_bootstrap_patch.js deleted file mode 100644 index a13b151c2655..000000000000 --- a/x-pack/plugins/ml/public/lib/angular_bootstrap_patch.js +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable @kbn/eslint/require-license-header */ - -/** - * @notice - * - * This product includes code that was extracted from angular-ui-bootstrap@0.13.1 - * which is available under an "MIT" license - * - * The MIT License - * - * Copyright (c) 2012-2016 the AngularUI Team, http://angular-ui.github.io/bootstrap/ - * - * 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. - */ - -// This file contains a section of code taken from angular-ui-bootstrap@0.13.1 -// and adds it to kibana's included version of 0.12.1 -// It adds the ability to allow html to be used as the content of the popover component - -import 'ui/angular-bootstrap'; - -import { uiModules } from 'ui/modules'; -const module = uiModules.get('apps/ml'); - -module - .directive('popover', [ '$tooltip', function ($tooltip) { - return $tooltip('popover', 'popover', 'click'); - }]) - .directive('popoverHtmlUnsafePopup', function ($compile) { - let template = '
'; - template += '
'; - template += '
'; - template += '

'; - template += '
'; - template += '
'; - return { - restrict: 'EA', - replace: true, - scope: { - title: '@', - content: '@', - placement: '@', - animation: '&', - isOpen: '&' - }, - template: template, - link: function (scope, element) { - // The content of the popup is added as a string and does not run through angular's templating system. - // therefore {{stuff}} substitutions don't happen. - // we have to manually apply the template, compile it with this scope and then set it as the html - scope.$apply(); - const cont = $compile(scope.content)(scope); - element.find('.popover-content').html(cont); - - // function to force the popover to close - scope.closePopover = function () { - scope.$parent.$parent.isOpen = false; - scope.$parent.$parent.$applyAsync(); - element.remove(); - }; - } - }; - }) - .directive('popoverHtmlUnsafe', ['$tooltip', function ($tooltip) { - return $tooltip('popoverHtmlUnsafe', 'popover', 'click'); - }]); diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 893804328717..f7ab1588c8a7 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6145,30 +6145,9 @@ "xpack.ml.jobsBreadcrumbs.populationLabel": "填充", "xpack.ml.jobsBreadcrumbs.selectIndexOrSearchLabel": "选择索引或搜索", "xpack.ml.jobsBreadcrumbs.singleMetricLabel": "单一指标", - "xpack.ml.jobSelect.allJobsDescription": "所有作业", - "xpack.ml.jobSelect.jobDescription": "{jobId} 和 {jobsAmount, plural, zero {# 个其他作业} one {# 个其他作业} other {# 个其他作业}}", "xpack.ml.jobSelect.noJobsSelectedWarningMessage": "未选择作业,将自动选择第一个作业", "xpack.ml.jobSelect.requestedJobsDoesNotExistWarningMessage": "已请求\n{invalidIdsLength, plural, one { 个作业 {invalidIds} 不存在} other { 个作业 {invalidIds} 不存在}}", - "xpack.ml.jobSelect.wholeGroupDescription": "{wholeGroup}(具有 {count, plural, zero {# 个作业} one {# 个作业} other {# 个作业}})以及\n {total, plural, zero {# 个其他} one {# 个其他} other {# 个其他}}", - "xpack.ml.jobSelectButton.jobSelectionMenuButtonAriaLabel": "作业选择菜单", - "xpack.ml.jobSelectButton.jobSelectionMenuButtonTooltip": "已选择 {description}", - "xpack.ml.jobSelectButton.jobTitle": "作业", - "xpack.ml.jobSelectList.applyButtonAriaLabel": "应用", - "xpack.ml.jobSelectList.applyButtonLabel": "应用", - "xpack.ml.jobSelectList.applyTimeRangeLabel": "同时应用时间范围", - "xpack.ml.jobSelectList.cancelButtonAriaLabel": "取消", - "xpack.ml.jobSelectList.cancelButtonLabel": "取消", - "xpack.ml.jobSelectList.groupIdAriaLabel": "组 ID {groupId}", - "xpack.ml.jobSelectList.groupsTitle": "组", "xpack.ml.jobSelectList.groupTimeRangeLabel": "{fromString} 到 {toString}", - "xpack.ml.jobSelectList.jobIdAriaLabel": "作业 ID {jobId}", - "xpack.ml.jobSelectList.jobSelectionTitle": "作业选择", - "xpack.ml.jobSelectList.jobsTitle": "作业", - "xpack.ml.jobSelectList.jobTimeRangeLabel": "{fromString} 到 {toString}", - "xpack.ml.jobSelectList.loadingJobsLabel": "正在加载作业", - "xpack.ml.jobSelectList.selectAllGroupsCheckboxAriaLabel": "选中所有组复选框。总计数 {selectedGroupsLength}。", - "xpack.ml.jobSelectList.selectAllJobsCheckboxAriaLabel": "选中所有作业复选框。总计数 {selectedJobsLength}。", - "xpack.ml.jobSelectList.timeRangeAriaLabel": "时间范围 {jobTimeRangeLabel}", "xpack.ml.jobSelector.applyFlyoutButton": "应用", "xpack.ml.jobSelector.applyTimerangeSwitchLabel": "应用时间范围", "xpack.ml.jobSelector.clearAllFlyoutButton": "全部清除",