mirror of
https://github.com/getredash/redash.git
synced 2025-12-25 01:03:20 -05:00
@@ -67,4 +67,8 @@ strong {
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbox {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,95 +1,99 @@
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
|
||||
&:not(.table-striped) > thead > tr > th {
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
[class*="bg-"] {
|
||||
& > tr > th {
|
||||
color: #fff;
|
||||
border-bottom: 0;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
& + tbody > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
vertical-align: middle;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
border-width: 1px;
|
||||
text-transform: uppercase;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
& > thead > tr,
|
||||
& > tbody > tr,
|
||||
& > tfoot > tr {
|
||||
|
||||
& > th, & > td {
|
||||
|
||||
&:first-child {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr:last-child > td {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-bordered {
|
||||
border: 0;
|
||||
|
||||
& > tbody > tr {
|
||||
& > td, & > th {
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
border-left: 0;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-vmiddle {
|
||||
td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.tile .table {
|
||||
|
||||
& > thead:not([class*="bg-"]) > tr > th {
|
||||
border-top: 1px solid @table-border-color;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.table-hover > tbody > tr:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.table {
|
||||
margin-bottom: 0;
|
||||
|
||||
th.sortable-column {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:not(.table-striped) > thead > tr > th {
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
[class*="bg-"] {
|
||||
& > tr > th {
|
||||
color: #fff;
|
||||
border-bottom: 0;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
& + tbody > tr:first-child > td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
vertical-align: middle;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
border-width: 1px;
|
||||
text-transform: uppercase;
|
||||
padding: 15px 10px;
|
||||
}
|
||||
|
||||
& > thead > tr,
|
||||
& > tbody > tr,
|
||||
& > tfoot > tr {
|
||||
|
||||
& > th, & > td {
|
||||
|
||||
&:first-child {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr:last-child > td {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-bordered {
|
||||
border: 0;
|
||||
|
||||
& > tbody > tr {
|
||||
& > td, & > th {
|
||||
border-bottom: 0;
|
||||
border-left: 0;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
border-left: 0;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-vmiddle {
|
||||
td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.tile .table {
|
||||
|
||||
& > thead:not([class*="bg-"]) > tr > th {
|
||||
border-top: 1px solid @table-border-color;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.table-hover > tbody > tr:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,10 +68,14 @@ page-header, .page-header--new {
|
||||
padding: 3px 6px 4px;
|
||||
}
|
||||
|
||||
.tab-nav > li > a, .table > thead > tr > th {
|
||||
.tab-nav > li > a {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.table > thead > tr > th {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.dashboard__control {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
@@ -29,11 +29,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.scrollbox {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-header {
|
||||
padding: 0;
|
||||
|
||||
|
||||
16
client/app/components/dynamic-table/default-cell/index.js
Normal file
16
client/app/components/dynamic-table/default-cell/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import template from './template.html';
|
||||
|
||||
export default function init(ngModule) {
|
||||
ngModule.directive('dynamicTableDefaultCell', $sanitize => ({
|
||||
template,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
column: '=',
|
||||
value: '=',
|
||||
},
|
||||
link: ($scope) => {
|
||||
$scope.sanitize = value => $sanitize(value);
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<td ng-class="'content-align-' + column.alignContent">
|
||||
<div ng-if="column.allowHTML" ng-bind-html="sanitize(column.formatFunction(value))"></div>
|
||||
<div ng-if="!column.allowHTML" ng-bind="column.formatFunction(value)"></div>
|
||||
</td>
|
||||
25
client/app/components/dynamic-table/dynamic-table-row.js
Normal file
25
client/app/components/dynamic-table/dynamic-table-row.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { isFunction } from 'underscore';
|
||||
|
||||
export default function init(ngModule) {
|
||||
ngModule.directive('dynamicTableRow', () => ({
|
||||
template: '',
|
||||
// AngularJS has a strange love to table-related tags, therefore
|
||||
// we should use this directive as an attribute
|
||||
restrict: 'A',
|
||||
replace: false,
|
||||
scope: {
|
||||
columns: '=',
|
||||
row: '=',
|
||||
render: '=',
|
||||
},
|
||||
link: ($scope, $element) => {
|
||||
$scope.$watch('render', () => {
|
||||
if (isFunction($scope.render)) {
|
||||
$scope.render($scope, (clonedElement) => {
|
||||
$element.empty().append(clonedElement);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
th.sortable-column {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -1,30 +1,45 @@
|
||||
<div>
|
||||
<div class="dynamic-table-container">
|
||||
<table class="table table-condensed table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th ng-repeat="column in $ctrl.columns" ng-click="$ctrl.orderBy(column)" class="sortable-column">
|
||||
{{column.title}} <span ng-if="$ctrl.sortIcon(column)"><i class="fa fa-sort-{{$ctrl.sortIcon(column)}}"></i></span>
|
||||
<tr>
|
||||
<th ng-repeat="column in $ctrl.columns" ng-click="$ctrl.onColumnHeaderClick($event, column)"
|
||||
class="sortable-column" ng-class="'content-align-' + column.alignContent">
|
||||
<span ng-if="($ctrl.orderBy.length > 1) && ($ctrl.orderByColumnsIndex[column.name] > 0)"
|
||||
class="sort-order-indicator">{{ $ctrl.orderByColumnsIndex[column.name] }}</span>
|
||||
<span>{{column.title}}</span>
|
||||
<i ng-if="$ctrl.orderByColumnsIndex[column.name] > 0"
|
||||
ng-class="{
|
||||
'fa': true,
|
||||
'fa-caret-down': $ctrl.orderByColumnsDirection[column.name] > 0,
|
||||
'fa-caret-up': $ctrl.orderByColumnsDirection[column.name] < 0
|
||||
}"></i>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<thead ng-if="$ctrl.searchColumns.length > 0">
|
||||
<th class="p-t-10 p-b-10 p-l-15 p-r-15" colspan="{{ $ctrl.columns.length }}">
|
||||
<input type="text" class="form-control" placeholder="Search..."
|
||||
ng-model="$ctrl.searchTerm" ng-model-options="{ allowInvalid: true, debounce: 200 }"
|
||||
ng-change="$ctrl.onSearchTermChanged()">
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="row in $ctrl.rowsToDisplay">
|
||||
<td ng-repeat="column in $ctrl.columns" ng-bind-html="$ctrl.sanitize(column.formatFunction(row[column.name]))">
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="row in $ctrl.rowsToDisplay"
|
||||
dynamic-table-row columns="$ctrl.columns" row="row"
|
||||
render="$ctrl.renderSingleRow"
|
||||
></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="text-center" ng-if="$ctrl.rowsCount > $ctrl.itemsPerPage">
|
||||
<ul uib-pagination total-items="$ctrl.rowsCount"
|
||||
<div class="text-center" ng-if="$ctrl.preparedRows.length > $ctrl.itemsPerPage">
|
||||
<ul uib-pagination total-items="$ctrl.preparedRows.length"
|
||||
items-per-page="$ctrl.itemsPerPage"
|
||||
ng-model="$ctrl.page"
|
||||
ng-model="$ctrl.currentPage"
|
||||
max-size="6"
|
||||
class="pagination"
|
||||
boundary-link-numbers="true"
|
||||
rotate="false"
|
||||
next-text='→'
|
||||
previous-text='←'
|
||||
ng-change="$ctrl.pageChanged()"></ul>
|
||||
ng-change="$ctrl.onPageChanged()"></ul>
|
||||
</div>
|
||||
|
||||
38
client/app/components/dynamic-table/dynamic-table.less
Normal file
38
client/app/components/dynamic-table/dynamic-table.less
Normal file
@@ -0,0 +1,38 @@
|
||||
.dynamic-table-container {
|
||||
th {
|
||||
white-space: nowrap;
|
||||
span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sort-order-indicator {
|
||||
@size: 12px;
|
||||
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
min-width: @size;
|
||||
height: @size;
|
||||
font-size: @size * 3/4;
|
||||
border-radius: @size / 2;
|
||||
background: #c0c0c0;
|
||||
text-align: center;
|
||||
line-height: @size;
|
||||
color: #fff;
|
||||
padding: 0 @size * 1/4;
|
||||
}
|
||||
}
|
||||
|
||||
th, td {
|
||||
&.content-align-left {
|
||||
text-align: left;
|
||||
}
|
||||
&.content-align-right {
|
||||
text-align: right;
|
||||
}
|
||||
&.content-align-center {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +1,217 @@
|
||||
import { sortBy } from 'underscore';
|
||||
import { find, filter, map, each } from 'underscore';
|
||||
import template from './dynamic-table.html';
|
||||
import './dynamic-table.css';
|
||||
import './dynamic-table.less';
|
||||
|
||||
function DynamicTable($sanitize) {
|
||||
function filterRows(rows, searchTerm, columns) {
|
||||
if ((searchTerm === '') || (columns.length === 0) || (rows.length === 0)) {
|
||||
return rows;
|
||||
}
|
||||
searchTerm = searchTerm.toUpperCase();
|
||||
return filter(rows, (row) => {
|
||||
for (let i = 0; i < columns.length; i += 1) {
|
||||
const columnName = columns[i].name;
|
||||
const formatFunction = columns[i].formatFunction;
|
||||
if (row[columnName] !== undefined) {
|
||||
let value = formatFunction ? formatFunction(row[columnName]) : row[columnName];
|
||||
value = ('' + value).toUpperCase();
|
||||
if (value.indexOf(searchTerm) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function sortRows(rows, orderBy) {
|
||||
if ((orderBy.length === 0) || (rows.length === 0)) {
|
||||
return rows;
|
||||
}
|
||||
// Create a copy of array before sorting, because .sort() will modify original array
|
||||
return [].concat(rows).sort((a, b) => {
|
||||
let va;
|
||||
let vb;
|
||||
for (let i = 0; i < orderBy.length; i += 1) {
|
||||
va = a[orderBy[i].name];
|
||||
vb = b[orderBy[i].name];
|
||||
if (va < vb) {
|
||||
// if a < b - we should return -1, but take in account direction
|
||||
return orderBy[i].direction * -1;
|
||||
}
|
||||
if (va > vb) {
|
||||
// if a > b - we should return 1, but take in account direction
|
||||
return orderBy[i].direction * 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
function validateItemsPerPage(value, defaultValue) {
|
||||
defaultValue = defaultValue || 10;
|
||||
value = parseInt(value, 10) || defaultValue;
|
||||
return value > 0 ? value : defaultValue;
|
||||
}
|
||||
|
||||
// Optimized rendering
|
||||
// Instead of using two nested `ng-repeat`s by rows and columns,
|
||||
// we'll create a template for row (and update it when columns changed),
|
||||
// compile it, and then use `ng-repeat` by rows and bind this template
|
||||
// to each row's scope. The goal is to reduce amount of scopes and watchers
|
||||
// from `count(rows) * count(cols)` to `count(rows)`. The major disadvantage
|
||||
// is that cell markup should be specified here instead of template.
|
||||
function createRowRenderTemplate(columns, $compile) {
|
||||
const rowTemplate = map(columns, (column, index) => {
|
||||
switch (column.displayAs) {
|
||||
case 'json':
|
||||
return `
|
||||
<dynamic-table-json-cell column="columns[${index}]"
|
||||
value="row[columns[${index}].name]"></dynamic-table-json-cell>
|
||||
`;
|
||||
default:
|
||||
return `
|
||||
<dynamic-table-default-cell column="columns[${index}]"
|
||||
value="row[columns[${index}].name]"></dynamic-table-default-cell>
|
||||
`;
|
||||
}
|
||||
}).join('');
|
||||
return $compile(rowTemplate);
|
||||
}
|
||||
|
||||
function DynamicTable($compile) {
|
||||
'ngInject';
|
||||
|
||||
this.itemsPerPage = this.count = 15;
|
||||
this.page = 1;
|
||||
this.rowsCount = 0;
|
||||
this.orderByField = undefined;
|
||||
this.orderByReverse = false;
|
||||
this.itemsPerPage = validateItemsPerPage(this.itemsPerPage);
|
||||
this.currentPage = 1;
|
||||
this.searchTerm = '';
|
||||
|
||||
this.pageChanged = () => {
|
||||
const first = this.count * (this.page - 1);
|
||||
const last = this.count * (this.page);
|
||||
this.columns = [];
|
||||
this.rows = [];
|
||||
this.preparedRows = [];
|
||||
this.rowsToDisplay = [];
|
||||
this.orderBy = [];
|
||||
this.orderByColumnsIndex = {};
|
||||
this.orderByColumnsDirection = {};
|
||||
|
||||
this.rowsToDisplay = this.rows.slice(first, last);
|
||||
this.searchColumns = [];
|
||||
|
||||
const updateOrderByColumnsInfo = () => {
|
||||
this.orderByColumnsIndex = {};
|
||||
this.orderByColumnsDirection = {};
|
||||
each(this.orderBy, (column, index) => {
|
||||
this.orderByColumnsIndex[column.name] = index + 1;
|
||||
this.orderByColumnsDirection[column.name] = column.direction;
|
||||
});
|
||||
};
|
||||
|
||||
const updateRowsToDisplay = (performFilterAndSort) => {
|
||||
if (performFilterAndSort) {
|
||||
this.preparedRows = sortRows(
|
||||
filterRows(this.rows, this.searchTerm, this.searchColumns),
|
||||
this.orderBy,
|
||||
);
|
||||
}
|
||||
const first = (this.currentPage - 1) * this.itemsPerPage;
|
||||
const last = first + this.itemsPerPage;
|
||||
this.rowsToDisplay = this.preparedRows.slice(first, last);
|
||||
};
|
||||
|
||||
const setColumns = (columns) => {
|
||||
// 1. reset sorting
|
||||
// 2. reset current page
|
||||
// 3. reset search
|
||||
// 4. get columns for search
|
||||
// 5. update row rendering template
|
||||
// 6. prepare rows
|
||||
|
||||
this.columns = columns;
|
||||
updateOrderByColumnsInfo();
|
||||
this.orderBy = [];
|
||||
this.currentPage = 1;
|
||||
this.searchTerm = '';
|
||||
this.searchColumns = filter(this.columns, 'allowSearch');
|
||||
this.renderSingleRow = createRowRenderTemplate(this.columns, $compile);
|
||||
updateRowsToDisplay(true);
|
||||
};
|
||||
|
||||
const setRows = (rows) => {
|
||||
// 1. reset current page
|
||||
// 2. prepare rows
|
||||
|
||||
this.rows = rows;
|
||||
this.currentPage = 1;
|
||||
updateRowsToDisplay(true);
|
||||
};
|
||||
|
||||
this.renderSingleRow = null;
|
||||
|
||||
this.onColumnHeaderClick = ($event, column) => {
|
||||
const orderBy = find(this.orderBy, item => item.name === column.name);
|
||||
if (orderBy) {
|
||||
// ASC -> DESC -> off
|
||||
if (orderBy.direction === 1) {
|
||||
orderBy.direction = -1;
|
||||
if (!$event.shiftKey) {
|
||||
this.orderBy = [orderBy];
|
||||
}
|
||||
} else {
|
||||
if ($event.shiftKey) {
|
||||
this.orderBy = filter(this.orderBy, item => item.name !== column.name);
|
||||
} else {
|
||||
this.orderBy = [];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!$event.shiftKey) {
|
||||
this.orderBy = [];
|
||||
}
|
||||
this.orderBy.push({
|
||||
name: column.name,
|
||||
direction: 1,
|
||||
});
|
||||
}
|
||||
updateOrderByColumnsInfo();
|
||||
updateRowsToDisplay(true);
|
||||
|
||||
|
||||
// Remove text selection - may occur accidentally
|
||||
if ($event.shiftKey) {
|
||||
document.getSelection().removeAllRanges();
|
||||
}
|
||||
};
|
||||
|
||||
this.onPageChanged = () => {
|
||||
updateRowsToDisplay(false);
|
||||
};
|
||||
|
||||
this.onSearchTermChanged = () => {
|
||||
this.preparedRows = sortRows(
|
||||
filterRows(this.rows, this.searchTerm, this.searchColumns),
|
||||
this.orderBy,
|
||||
);
|
||||
this.currentPage = 1;
|
||||
updateRowsToDisplay(true);
|
||||
};
|
||||
|
||||
this.$onChanges = (changes) => {
|
||||
if (changes.columns) {
|
||||
this.columns = changes.columns.currentValue;
|
||||
if (changes.rows) {
|
||||
// if rows also changed - temporarily set if to empty array - to avoid
|
||||
// filtering and sorting
|
||||
this.rows = [];
|
||||
}
|
||||
setColumns(changes.columns.currentValue);
|
||||
}
|
||||
|
||||
if (changes.rows) {
|
||||
this.rows = changes.rows.currentValue;
|
||||
setRows(changes.rows.currentValue);
|
||||
}
|
||||
|
||||
this.rowsCount = this.rows.length;
|
||||
|
||||
this.pageChanged();
|
||||
};
|
||||
|
||||
this.orderBy = (column) => {
|
||||
if (column === this.orderByField) {
|
||||
this.orderByReverse = !this.orderByReverse;
|
||||
} else {
|
||||
this.orderByField = column;
|
||||
this.orderByReverse = false;
|
||||
if (changes.itemsPerPage) {
|
||||
this.itemsPerPage = validateItemsPerPage(this.itemsPerPage);
|
||||
this.currentPage = 1;
|
||||
updateRowsToDisplay(false);
|
||||
}
|
||||
|
||||
if (this.orderByField) {
|
||||
this.rows = sortBy(this.rows, this.orderByField.name);
|
||||
if (this.orderByReverse) {
|
||||
this.rows = this.rows.reverse();
|
||||
}
|
||||
this.pageChanged();
|
||||
}
|
||||
};
|
||||
|
||||
this.sanitize = value => $sanitize(value);
|
||||
|
||||
this.sortIcon = (column) => {
|
||||
if (column !== this.orderByField) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.orderByReverse) {
|
||||
return 'desc';
|
||||
}
|
||||
|
||||
return 'asc';
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,7 +222,7 @@ export default function init(ngModule) {
|
||||
bindings: {
|
||||
rows: '<',
|
||||
columns: '<',
|
||||
count: '<',
|
||||
itemsPerPage: '<',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
40
client/app/components/dynamic-table/json-cell/index.js
Normal file
40
client/app/components/dynamic-table/json-cell/index.js
Normal file
@@ -0,0 +1,40 @@
|
||||
import { isUndefined, isString } from 'underscore';
|
||||
import renderJsonView from './json-view-interactive';
|
||||
import template from './template.html';
|
||||
|
||||
const MAX_JSON_SIZE = 50000;
|
||||
|
||||
function parseValue(value) {
|
||||
if (isString(value) && (value.length <= MAX_JSON_SIZE)) {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function init(ngModule) {
|
||||
ngModule.directive('dynamicTableJsonCell', () => ({
|
||||
template,
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
column: '=',
|
||||
value: '=',
|
||||
},
|
||||
link: ($scope, $element) => {
|
||||
const container = $element.find('.json-cell-valid');
|
||||
|
||||
$scope.isValid = false;
|
||||
$scope.parsedValue = null;
|
||||
|
||||
$scope.$watch('value', () => {
|
||||
$scope.parsedValue = parseValue($scope.value);
|
||||
$scope.isValid = !isUndefined($scope.parsedValue);
|
||||
container.empty();
|
||||
renderJsonView(container, $scope.parsedValue);
|
||||
});
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
import { isFunction, isArray, isObject, isString, isNumber, isUndefined, each, keys, filter } from 'underscore';
|
||||
import $ from 'jquery';
|
||||
import './json-view-interactive.less';
|
||||
|
||||
function isPrimitive(value) {
|
||||
return (value === null) || (value === false) || (value === true) ||
|
||||
(isNumber(value) && isFinite(value));
|
||||
}
|
||||
|
||||
function combine(...functions) {
|
||||
functions = filter(functions, isFunction);
|
||||
return (...args) => {
|
||||
each(functions, (fn) => {
|
||||
fn(...args);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function initToggle(toggle, toggleBlockFn) {
|
||||
if (isFunction(toggleBlockFn)) {
|
||||
let visible = false;
|
||||
const icon = $('<i>').addClass('fa fa-caret-right').appendTo(toggle.empty());
|
||||
toggleBlockFn(visible);
|
||||
toggle.on('click', () => {
|
||||
visible = !visible;
|
||||
icon.toggleClass('fa-caret-right fa-caret-down');
|
||||
toggleBlockFn(visible);
|
||||
});
|
||||
} else {
|
||||
toggle.addClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function createRenderNestedBlock(block, ellipsis, values, renderKeys) {
|
||||
return (show) => {
|
||||
if (show) {
|
||||
ellipsis.addClass('hidden');
|
||||
block.removeClass('hidden').empty();
|
||||
|
||||
let firstItem = null;
|
||||
let lastItem = null;
|
||||
each(values, (val, key) => {
|
||||
const nestedBlock = $('<span>').addClass('jvi-item').appendTo(block);
|
||||
firstItem = firstItem || nestedBlock;
|
||||
lastItem = nestedBlock;
|
||||
|
||||
const toggle = $('<span>').addClass('jvi-toggle').appendTo(nestedBlock);
|
||||
|
||||
if (renderKeys) {
|
||||
const keyWrapper = $('<span>').addClass('jvi-object-key').appendTo(nestedBlock);
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
renderString(keyWrapper, key);
|
||||
$('<span>').addClass('jvi-punctuation').text(': ').appendTo(nestedBlock);
|
||||
}
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const toggleBlockFn = renderValue(nestedBlock, val, true);
|
||||
initToggle(toggle, toggleBlockFn);
|
||||
});
|
||||
|
||||
if (firstItem) {
|
||||
firstItem.addClass('jvi-nested-first');
|
||||
}
|
||||
if (lastItem) {
|
||||
lastItem.addClass('jvi-nested-last');
|
||||
}
|
||||
} else {
|
||||
block.addClass('hidden').empty();
|
||||
ellipsis.removeClass('hidden');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function renderComma($element) {
|
||||
return $('<span>').addClass('jvi-punctuation jvi-comma').text(',').appendTo($element);
|
||||
}
|
||||
|
||||
function renderEllipsis($element) {
|
||||
const result = $('<span>')
|
||||
.addClass('jvi-punctuation jvi-ellipsis')
|
||||
.html('…')
|
||||
.appendTo($element)
|
||||
.on('click', () => {
|
||||
result.parents('.jvi-item').eq(0).find('.jvi-toggle').trigger('click');
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderPrimitive($element, value, comma) {
|
||||
$('<span>').addClass('jvi-value jvi-primitive').text('' + value).appendTo($element);
|
||||
if (comma) {
|
||||
renderComma($element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function renderString($element, value, comma) {
|
||||
$('<span>').addClass('jvi-punctuation jvi-string').text('"').appendTo($element);
|
||||
$('<span>').addClass('jvi-value jvi-string').text(value).appendTo($element);
|
||||
$('<span>').addClass('jvi-punctuation jvi-string').text('"').appendTo($element);
|
||||
if (comma) {
|
||||
renderComma($element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function renderComment($element, count) {
|
||||
const text = ' // ' + count + ' ' + (count === 1 ? 'item' : 'items');
|
||||
const comment = $('<span>').addClass('jvi-comment').text(text).appendTo($element);
|
||||
return (show) => {
|
||||
if (show) {
|
||||
comment.addClass('hidden');
|
||||
} else {
|
||||
comment.removeClass('hidden');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function renderBrace($element, isForArray, isOpening) {
|
||||
const openingBrace = isForArray ? '[' : '{';
|
||||
const closingBrace = isForArray ? ']' : '}';
|
||||
const brace = isOpening ? openingBrace : closingBrace;
|
||||
return $('<span>').addClass('jvi-punctuation jvi-braces').text(brace).appendTo($element);
|
||||
}
|
||||
|
||||
function renderWithNested($element, values, comma, valuesIsArray) {
|
||||
const count = valuesIsArray ? values.length : keys(values).length;
|
||||
let result = null;
|
||||
|
||||
renderBrace($element, valuesIsArray, true);
|
||||
if (count > 0) {
|
||||
const ellipsis = renderEllipsis($element);
|
||||
const block = $('<span>').addClass('jvi-block hidden').appendTo($element);
|
||||
result = createRenderNestedBlock(block, ellipsis, values, !valuesIsArray);
|
||||
}
|
||||
renderBrace($element, valuesIsArray, false);
|
||||
|
||||
if (comma) {
|
||||
renderComma($element);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
result = combine(renderComment($element, count), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderArray($element, values, comma) {
|
||||
return renderWithNested($element, values, comma, true);
|
||||
}
|
||||
|
||||
function renderObject($element, value, comma) {
|
||||
return renderWithNested($element, value, comma, false);
|
||||
}
|
||||
|
||||
function renderValue($element, value, comma) {
|
||||
$element = $('<span>').appendTo($element);
|
||||
|
||||
if (isPrimitive(value)) {
|
||||
return renderPrimitive($element, value, comma);
|
||||
} else if (isString(value)) {
|
||||
return renderString($element, value, comma);
|
||||
} else if (isArray(value)) {
|
||||
return renderArray($element, value, comma);
|
||||
} else if (isObject(value)) {
|
||||
return renderObject($element, value, comma);
|
||||
}
|
||||
}
|
||||
|
||||
export default function renderJsonView(container, value) {
|
||||
if ((container instanceof $) && !isUndefined(value) && !isFunction(value)) {
|
||||
const block = $('<span>').addClass('jvi-item').appendTo(container);
|
||||
const toggle = $('<span>').addClass('jvi-toggle').appendTo(block);
|
||||
const toggleBlockFn = renderValue(block, value);
|
||||
initToggle(toggle, toggleBlockFn);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
@import (reference) "~bootstrap/less/variables.less";
|
||||
|
||||
@jvi-gutter: 20px;
|
||||
@jvi-spacing: 2px;
|
||||
|
||||
.jvi-block {
|
||||
display: block;
|
||||
border-left: 1px dotted @table-border-color;
|
||||
margin: 0 0 0 2px;
|
||||
font-family: @font-family-monospace;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-item {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0 0 0 @jvi-gutter;
|
||||
white-space: nowrap;
|
||||
|
||||
.jvi-item {
|
||||
margin: @jvi-spacing 0;
|
||||
}
|
||||
|
||||
&.jvi-nested-last > span > .jvi-punctuation.jvi-comma {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-toggle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: @jvi-gutter;
|
||||
height: @jvi-gutter;
|
||||
line-height: @jvi-gutter;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
color: @text-color;
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-punctuation {
|
||||
color: @text-color;
|
||||
|
||||
&.jvi-string {
|
||||
color: @state-success-text;
|
||||
}
|
||||
|
||||
&.jvi-braces {
|
||||
margin: 0 @jvi-spacing;
|
||||
}
|
||||
|
||||
&.jvi-ellipsis {
|
||||
padding: 0 @jvi-spacing;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-value {
|
||||
color: @state-success-text;
|
||||
|
||||
&.jvi-primitive {
|
||||
color: @state-warning-text;
|
||||
}
|
||||
|
||||
&.jvi-string {
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-object-key {
|
||||
.jvi-value, .jvi-punctuation {
|
||||
color: @brand-primary;
|
||||
}
|
||||
}
|
||||
|
||||
.jvi-comment {
|
||||
color: @text-muted;
|
||||
font-style: italic;
|
||||
margin: 0 0 0 2 * @jvi-spacing;
|
||||
opacity: 0.5;
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<td>
|
||||
<div ng-if="!isValid" class="json-cell-invalid">{{ value }}</div>
|
||||
<div ng-show="isValid" class="json-cell-valid"></div>
|
||||
</td>
|
||||
@@ -2,9 +2,6 @@ import moment from 'moment';
|
||||
import _capitalize from 'underscore.string/capitalize';
|
||||
import { isEmpty } from 'underscore';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const urlPattern = /(^|[\s\n]|<br\/?>)((?:https?|ftp):\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi;
|
||||
|
||||
export function durationHumanize(duration) {
|
||||
let humanized = '';
|
||||
|
||||
@@ -67,10 +64,6 @@ export function capitalize(text) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function linkify(text) {
|
||||
return text.replace(urlPattern, "$1<a href='$2' target='_blank'>$2</a>");
|
||||
}
|
||||
|
||||
export function remove(items, item) {
|
||||
if (items === undefined) {
|
||||
return items;
|
||||
|
||||
@@ -20,6 +20,7 @@ angular.module('ui.sortable', [])
|
||||
// ng-repeat items
|
||||
// if the user uses
|
||||
items: '> [ng-repeat],> [data-ng-repeat],> [x-ng-repeat]',
|
||||
cancel: 'input, textarea, button, select, option, .ui-sortable-bypass',
|
||||
})
|
||||
.directive('uiSortable', [
|
||||
'uiSortableConfig', '$timeout', '$log',
|
||||
|
||||
@@ -96,10 +96,13 @@
|
||||
<span class="zmdi zmdi-format-indent-increase"></span> Format Query
|
||||
</button>
|
||||
|
||||
<i class="fa fa-database"></i>
|
||||
<span class="text-muted">Data Source</span>
|
||||
<span class="m-l-10">
|
||||
<i class="fa fa-database"></i>
|
||||
<span class="text-muted">Data Source</span>
|
||||
</span>
|
||||
<select ng-disabled="!isQueryOwner" ng-model="query.data_source_id" ng-change="updateDataSource()"
|
||||
ng-options="ds.id as ds.name for ds in dataSources"></select>
|
||||
ng-options="ds.id as ds.name for ds in dataSources"
|
||||
class="form-control m-l-5" style="display: inline-block; width: auto;"></select>
|
||||
|
||||
<div class="pull-right">
|
||||
<button class="btn btn-s btn-default" ng-click="togglePublished()" ng-if="query.is_draft && query.id != undefined && (isQueryOwner || currentUser.hasPermission('admin'))">
|
||||
@@ -232,11 +235,12 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<ul class="tab-nav">
|
||||
<rd-tab tab-id="table" name="Table" base-path="query.getUrl(sourceMode)"></rd-tab>
|
||||
<rd-tab tab-id="{{vis.id}}" name="{{vis.name}}" ng-if="vis.type!='TABLE'" base-path="query.getUrl(sourceMode)"
|
||||
ng-repeat="vis in query.visualizations">
|
||||
<span class="remove" ng-click="deleteVisualization($event, vis)"
|
||||
ng-show="canEdit"> ×</span>
|
||||
<rd-tab ng-if="!query.visualizations.length"
|
||||
tab-id="table" name="Table" base-path="query.getUrl(sourceMode)"></rd-tab>
|
||||
<rd-tab tab-id="{{vis.id}}" name="{{vis.name}}" base-path="query.getUrl(sourceMode)"
|
||||
ng-repeat="vis in query.visualizations | orderBy:'id'">
|
||||
<span class="remove" ng-click="deleteVisualization($event, vis)"
|
||||
ng-if="canEdit && !($first && (vis.type === 'TABLE'))">×</span>
|
||||
</rd-tab>
|
||||
<li class="rd-tab"><a ng-click="openVisualizationEditor()" ng-if="sourceMode && canEdit">+ New Visualization</a></li>
|
||||
</ul>
|
||||
@@ -244,14 +248,11 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div ng-show="selectedTab == 'table'">
|
||||
<div ng-if="!query.visualizations.length">
|
||||
<filters filters="filters"></filters>
|
||||
<grid-renderer query-result="queryResult" items-per-page="50"></grid-renderer>
|
||||
<!-- the ng-repeat is a lame hack to find the table visualization... -->
|
||||
<div class="p-15" ng-repeat="vis in query.visualizations" ng-if="vis.type == 'TABLE'">
|
||||
<button class="btn btn-default" ng-if="!query.isNew()" ng-click="showEmbedDialog(query, vis)"><i class="zmdi zmdi-code"></i> Embed</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="selectedTab == vis.id" ng-repeat="vis in query.visualizations">
|
||||
<visualization-renderer visualization="vis" query-result="queryResult"></visualization-renderer>
|
||||
<div class="p-15">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { pick, any, some, find } from 'underscore';
|
||||
import { pick, any, some, find, min, isObject } from 'underscore';
|
||||
import template from './query.html';
|
||||
|
||||
function QueryViewCtrl(
|
||||
@@ -6,8 +6,6 @@ function QueryViewCtrl(
|
||||
KeyboardShortcuts, Title, AlertDialog, Notifications, clientConfig, toastr, $uibModal,
|
||||
currentUser, Query, DataSource,
|
||||
) {
|
||||
const DEFAULT_TAB = 'table';
|
||||
|
||||
function getQueryResult(maxAge) {
|
||||
if (maxAge === undefined) {
|
||||
maxAge = $location.search().maxAge;
|
||||
@@ -112,7 +110,7 @@ function QueryViewCtrl(
|
||||
Notifications.getPermissions();
|
||||
};
|
||||
|
||||
|
||||
$scope.selectedTab = 'table';
|
||||
$scope.currentUser = currentUser;
|
||||
$scope.dataSource = {};
|
||||
$scope.query = $route.current.locals.query;
|
||||
@@ -360,7 +358,15 @@ function QueryViewCtrl(
|
||||
|
||||
$scope.$watch(
|
||||
() => $location.hash(),
|
||||
(hash) => { $scope.selectedTab = hash || DEFAULT_TAB; },
|
||||
(hash) => {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
const exists = find($scope.query.visualizations, item => item.id == hash);
|
||||
let visualization = min($scope.query.visualizations, viz => viz.id);
|
||||
if (!isObject(visualization)) {
|
||||
visualization = {};
|
||||
}
|
||||
$scope.selectedTab = (exists ? hash : visualization.id) || 'table';
|
||||
},
|
||||
);
|
||||
|
||||
$scope.showManagePermissionsModal = () => {
|
||||
|
||||
60
client/app/visualizations/table/formats.js
Normal file
60
client/app/visualizations/table/formats.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import moment from 'moment/moment';
|
||||
import numeral from 'numeral';
|
||||
import _ from 'underscore';
|
||||
|
||||
// eslint-disable-next-line
|
||||
const urlPattern = /(^|[\s\n]|<br\/?>)((?:https?|ftp):\/\/[\-A-Z0-9+\u0026\u2019@#\/%?=()~_|!:,.;]*[\-A-Z0-9+\u0026@#\/%=~()_|])/gi;
|
||||
|
||||
function createDefaultFormatter(highlightLinks) {
|
||||
if (highlightLinks) {
|
||||
return (value) => {
|
||||
if (_.isString(value)) {
|
||||
value = value.replace(urlPattern, '$1<a href="$2" target="_blank">$2</a>');
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
return value => value;
|
||||
}
|
||||
|
||||
function createDateTimeFormatter(format) {
|
||||
if (_.isString(format) && (format !== '')) {
|
||||
return (value) => {
|
||||
if (value && moment.isMoment(value)) {
|
||||
return value.format(format);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
}
|
||||
return value => value;
|
||||
}
|
||||
|
||||
function createBooleanFormatter(values) {
|
||||
if (_.isArray(values)) {
|
||||
if (values.length >= 2) {
|
||||
// Both `true` and `false` specified
|
||||
return value => '' + values[value ? 1 : 0];
|
||||
} else if (values.length === 1) {
|
||||
// Only `true`
|
||||
return value => (value ? values[0] : '');
|
||||
}
|
||||
}
|
||||
return value => (value ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function createNumberFormatter(format) {
|
||||
if (_.isString(format) && (format !== '')) {
|
||||
const n = numeral(0); // cache `numeral` instance
|
||||
return value => n.set(value).format(format);
|
||||
}
|
||||
return value => value;
|
||||
}
|
||||
|
||||
export default function createFormatter(column) {
|
||||
switch (column.displayAs) {
|
||||
case 'number': return createNumberFormatter(column.numberFormat);
|
||||
case 'boolean': return createBooleanFormatter(column.booleanValues);
|
||||
case 'datetime': return createDateTimeFormatter(column.dateTimeFormat);
|
||||
default: return createDefaultFormatter(column.allowHTML && column.highlightLinks);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,124 @@
|
||||
import moment from 'moment';
|
||||
import { _, partial, isString } from 'underscore';
|
||||
import _ from 'underscore';
|
||||
import { getColumnCleanName } from '@/services/query-result';
|
||||
import createFormatter from './formats';
|
||||
import template from './table.html';
|
||||
import editorTemplate from './table-editor.html';
|
||||
import './table-editor.less';
|
||||
|
||||
function formatValue($filter, clientConfig, value, type) {
|
||||
let formattedValue = value;
|
||||
switch (type) {
|
||||
case 'integer':
|
||||
formattedValue = $filter('number')(value, 0);
|
||||
break;
|
||||
case 'float':
|
||||
formattedValue = $filter('number')(value, 2);
|
||||
break;
|
||||
case 'boolean':
|
||||
if (value !== undefined) {
|
||||
formattedValue = String(value);
|
||||
}
|
||||
break;
|
||||
case 'date':
|
||||
if (value && moment.isMoment(value)) {
|
||||
formattedValue = value.format(clientConfig.dateFormat);
|
||||
}
|
||||
break;
|
||||
case 'datetime':
|
||||
if (value && moment.isMoment(value)) {
|
||||
formattedValue = value.format(clientConfig.dateTimeFormat);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (isString(value)) {
|
||||
formattedValue = $filter('linkify')(value);
|
||||
}
|
||||
break;
|
||||
const ALLOWED_ITEM_PER_PAGE = [5, 10, 15, 20, 25];
|
||||
|
||||
const DISPLAY_AS_OPTIONS = [
|
||||
{ name: 'Text', value: 'string' },
|
||||
{ name: 'Number', value: 'number' },
|
||||
{ name: 'Date/Time', value: 'datetime' },
|
||||
{ name: 'Boolean', value: 'boolean' },
|
||||
{ name: 'JSON', value: 'json' },
|
||||
];
|
||||
|
||||
const DEFAULT_OPTIONS = {
|
||||
itemsPerPage: 15,
|
||||
defaultRows: 14,
|
||||
defaultColumns: 4,
|
||||
minColumns: 2,
|
||||
};
|
||||
|
||||
function getColumnContentAlignment(type) {
|
||||
return ['integer', 'float', 'boolean', 'date', 'datetime'].indexOf(type) >= 0 ? 'right' : 'left';
|
||||
}
|
||||
|
||||
function getDefaultColumnsOptions(columns) {
|
||||
const displayAs = {
|
||||
integer: 'number',
|
||||
float: 'number',
|
||||
boolean: 'boolean',
|
||||
date: 'datetime',
|
||||
datetime: 'datetime',
|
||||
};
|
||||
|
||||
return _.map(columns, (col, index) => ({
|
||||
name: col.name,
|
||||
type: col.type,
|
||||
displayAs: displayAs[col.type] || 'string',
|
||||
visible: true,
|
||||
order: 100000 + index,
|
||||
title: getColumnCleanName(col.name),
|
||||
allowSearch: false,
|
||||
allowHTML: false,
|
||||
highlightLinks: false,
|
||||
alignContent: getColumnContentAlignment(col.type),
|
||||
}));
|
||||
}
|
||||
|
||||
function getDefaultFormatOptions(column, clientConfig) {
|
||||
const dateTimeFormat = {
|
||||
date: clientConfig.dateFormat || 'DD/MM/YY',
|
||||
datetime: clientConfig.dateTimeFormat || 'DD/MM/YY HH:mm',
|
||||
};
|
||||
const numberFormat = {
|
||||
integer: clientConfig.integerFormat || '0,0',
|
||||
float: clientConfig.floatFormat || '0,0.00',
|
||||
};
|
||||
return {
|
||||
dateTimeFormat: dateTimeFormat[column.type],
|
||||
numberFormat: numberFormat[column.type],
|
||||
booleanValues: clientConfig.booleanValues || ['false', 'true'],
|
||||
};
|
||||
}
|
||||
|
||||
function wereColumnsReordered(queryColumns, visualizationColumns) {
|
||||
queryColumns = _.map(queryColumns, col => col.name);
|
||||
visualizationColumns = _.map(visualizationColumns, col => col.name);
|
||||
|
||||
// Some columns may be removed - so skip them (but keep original order)
|
||||
visualizationColumns = _.filter(visualizationColumns, col => _.includes(queryColumns, col));
|
||||
// Pick query columns that were previously saved with viz (but keep order too)
|
||||
queryColumns = _.filter(queryColumns, col => _.includes(visualizationColumns, col));
|
||||
|
||||
// Both array now have the same size as they both contains only common columns
|
||||
// (in fact, it was an intersection, that kept order of items on both arrays).
|
||||
// Now check for equality item-by-item; if common columns are in the same order -
|
||||
// they were not reordered in editor
|
||||
for (let i = 0; i < queryColumns.length; i += 1) {
|
||||
if (visualizationColumns[i] !== queryColumns[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function getColumnsOptions(columns, visualizationColumns) {
|
||||
const options = getDefaultColumnsOptions(columns);
|
||||
|
||||
if ((wereColumnsReordered(columns, visualizationColumns))) {
|
||||
visualizationColumns = _.object(_.map(
|
||||
visualizationColumns,
|
||||
(col, index) => [col.name, _.extend({}, col, { order: index })],
|
||||
));
|
||||
} else {
|
||||
visualizationColumns = _.object(_.map(
|
||||
visualizationColumns,
|
||||
col => [col.name, _.omit(col, 'order')],
|
||||
));
|
||||
}
|
||||
|
||||
return formattedValue;
|
||||
_.each(options, col => _.extend(col, visualizationColumns[col.name]));
|
||||
|
||||
return _.sortBy(options, 'order');
|
||||
}
|
||||
|
||||
function getColumnsToDisplay(columns, options, clientConfig) {
|
||||
columns = _.object(_.map(columns, col => [col.name, col]));
|
||||
let result = _.map(options, col => _.extend(
|
||||
getDefaultFormatOptions(col, clientConfig),
|
||||
col,
|
||||
columns[col.name],
|
||||
));
|
||||
|
||||
result = _.map(result, col => _.extend(col, {
|
||||
formatFunction: createFormatter(col),
|
||||
}));
|
||||
|
||||
return _.sortBy(_.filter(result, 'visible'), 'order');
|
||||
}
|
||||
|
||||
function GridRenderer(clientConfig) {
|
||||
@@ -42,33 +126,69 @@ function GridRenderer(clientConfig) {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
queryResult: '=',
|
||||
itemsPerPage: '=',
|
||||
options: '=',
|
||||
},
|
||||
template,
|
||||
replace: false,
|
||||
controller($scope, $filter) {
|
||||
controller($scope) {
|
||||
$scope.gridColumns = [];
|
||||
$scope.gridRows = [];
|
||||
|
||||
$scope.$watch('queryResult && queryResult.getData()', (queryResult) => {
|
||||
if (!queryResult) {
|
||||
return;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if ($scope.queryResult.getData() == null) {
|
||||
$scope.gridColumns = [];
|
||||
$scope.filters = [];
|
||||
} else {
|
||||
$scope.filters = $scope.queryResult.getFilters();
|
||||
|
||||
const columns = $scope.queryResult.getColumns();
|
||||
columns.forEach((col) => {
|
||||
col.title = getColumnCleanName(col.name);
|
||||
col.formatFunction = partial(formatValue, $filter, clientConfig, _, col.type);
|
||||
});
|
||||
|
||||
$scope.gridRows = $scope.queryResult.getData();
|
||||
$scope.gridColumns = columns;
|
||||
const columns = $scope.queryResult.getColumns();
|
||||
const columnsOptions = getColumnsOptions(columns, _.extend({}, $scope.options).columns);
|
||||
$scope.gridColumns = getColumnsToDisplay(columns, columnsOptions, clientConfig);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.$watch('queryResult && queryResult.getData()', (queryResult) => {
|
||||
if (queryResult) {
|
||||
update();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('options', (newValue, oldValue) => {
|
||||
if (newValue !== oldValue) {
|
||||
update();
|
||||
}
|
||||
}, true);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function GridEditor(clientConfig) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: editorTemplate,
|
||||
link: ($scope) => {
|
||||
$scope.allowedItemsPerPage = ALLOWED_ITEM_PER_PAGE;
|
||||
$scope.displayAsOptions = DISPLAY_AS_OPTIONS;
|
||||
|
||||
$scope.currentTab = 'columns';
|
||||
$scope.setCurrentTab = (tab) => {
|
||||
$scope.currentTab = tab;
|
||||
};
|
||||
|
||||
$scope.$watch('visualization', () => {
|
||||
if ($scope.visualization) {
|
||||
// For existing visualization - set default options
|
||||
$scope.visualization.options = _.extend({}, DEFAULT_OPTIONS, $scope.visualization.options);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$watch('queryResult && queryResult.getData()', (queryResult) => {
|
||||
if (queryResult) {
|
||||
const columns = $scope.queryResult.getData() !== null ? $scope.queryResult.getColumns() : [];
|
||||
$scope.visualization.options.columns = _.map(
|
||||
getColumnsOptions(columns, $scope.visualization.options.columns),
|
||||
col => _.extend(getDefaultFormatOptions(col, clientConfig), col),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -76,20 +196,18 @@ function GridRenderer(clientConfig) {
|
||||
}
|
||||
|
||||
export default function init(ngModule) {
|
||||
ngModule.directive('gridRenderer', GridRenderer);
|
||||
ngModule.directive('gridEditor', GridEditor);
|
||||
|
||||
ngModule.config((VisualizationProvider) => {
|
||||
const defaultOptions = {
|
||||
defaultRows: 14,
|
||||
defaultColumns: 4,
|
||||
minColumns: 2,
|
||||
};
|
||||
const defaultOptions = DEFAULT_OPTIONS;
|
||||
|
||||
VisualizationProvider.registerVisualization({
|
||||
type: 'TABLE',
|
||||
name: 'Table',
|
||||
renderTemplate: '<grid-renderer options="visualization.options" query-result="queryResult"></grid-renderer>',
|
||||
skipTypes: true,
|
||||
editorTemplate: '<grid-editor></grid-editor>',
|
||||
defaultOptions,
|
||||
});
|
||||
});
|
||||
ngModule.directive('gridRenderer', GridRenderer);
|
||||
}
|
||||
|
||||
103
client/app/visualizations/table/table-editor.html
Normal file
103
client/app/visualizations/table/table-editor.html
Normal file
@@ -0,0 +1,103 @@
|
||||
<div class="table-editor-container">
|
||||
<ul class="tab-nav">
|
||||
<li ng-class="{active: currentTab == 'columns'}" ng-if="options.globalSeriesType != 'custom'">
|
||||
<a ng-click="setCurrentTab('columns')">Columns</a>
|
||||
</li>
|
||||
<li ng-class="{active: currentTab == 'grid'}">
|
||||
<a ng-click="setCurrentTab('grid')">Grid</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div ng-if="currentTab == 'grid'" class="m-t-10 m-b-10">
|
||||
<div class="form-group">
|
||||
<label>Items per page</label>
|
||||
<select ng-options="value for value in allowedItemsPerPage"
|
||||
ng-model="visualization.options.itemsPerPage" class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="currentTab == 'columns'" class="table-editor-query-columns m-t-10 m-b-10"
|
||||
ui-sortable ng-model="visualization.options.columns">
|
||||
<div ng-repeat="column in visualization.options.columns">
|
||||
<div class="table-editor-column-header form-group">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon"><input type="checkbox" ng-model="column.visible"></span>
|
||||
<input class="form-control" ng-model="column.title" ng-model-options="{ allowInvalid: true, debounce: 200 }">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="btn-group btn-group-justified">
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="column.alignContent = 'left'"
|
||||
ng-class="{active: column.alignContent == 'left'}"><i class="fa fa-align-left"></i></button>
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="column.alignContent = 'center'"
|
||||
ng-class="{active: column.alignContent == 'center'}"><i class="fa fa-align-center"></i></button>
|
||||
<button type="button" class="btn btn-default btn-xs"
|
||||
ng-click="column.alignContent = 'right'"
|
||||
ng-class="{active: column.alignContent == 'right'}"><i class="fa fa-align-right"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label><input type="checkbox" ng-model="column.allowSearch"> Use for search</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Display as:</label>
|
||||
<select ng-options="item.value as item.name for item in displayAsOptions"
|
||||
ng-model="column.displayAs" class="form-control">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div ng-if="column.displayAs == 'string'">
|
||||
<div class="form-group">
|
||||
<label class="ui-sortable-bypass"><input type="checkbox" ng-model="column.allowHTML"> Allow HTML content</label>
|
||||
</div>
|
||||
<div ng-if="column.allowHTML" class="form-group">
|
||||
<label class="ui-sortable-bypass"><input type="checkbox" ng-model="column.highlightLinks"> Highlight links</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="column.displayAs == 'number'">
|
||||
<div class="form-group">
|
||||
<label for="table-editor-{{ column.name }}-number-format">
|
||||
Number format
|
||||
<span class="m-l-5"
|
||||
uib-popover-html="'Format <a href="http://numeraljs.com/" target="_blank">specs.</a>'"
|
||||
popover-trigger="'click outsideClick'"><i class="fa fa-question-circle"></i></span>
|
||||
</label>
|
||||
<input class="form-control" ng-model="column.numberFormat" ng-model-options="{ allowInvalid: true, debounce: 200 }"
|
||||
id="table-editor-{{ column.name }}-number-format">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="column.displayAs == 'datetime'">
|
||||
<div class="form-group">
|
||||
<label for="table-editor-{{ column.name }}-datetime-format">
|
||||
Date/Time format
|
||||
<span class="m-l-5"
|
||||
uib-popover-html="'Format <a href="http://momentjs.com/docs/#/displaying/format/" target="_blank">specs.</a>'"
|
||||
popover-trigger="'click outsideClick'"><i class="fa fa-question-circle"></i></span>
|
||||
</label>
|
||||
<input class="form-control" ng-model="column.dateTimeFormat" ng-model-options="{ allowInvalid: true, debounce: 200 }"
|
||||
id="table-editor-{{ column.name }}-datetime-format">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="column.displayAs == 'boolean'">
|
||||
<div class="form-group">
|
||||
<label for="table-editor-{{ column.name }}-boolean-false">Value for <code>false</code></label>
|
||||
<input class="form-control" ng-model="column.booleanValues[0]" ng-model-options="{ allowInvalid: true, debounce: 200 }"
|
||||
id="table-editor-{{ column.name }}-boolean-false">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="table-editor-{{ column.name }}-boolean-true">Value for <code>true</code></label>
|
||||
<input class="form-control" ng-model="column.booleanValues[1]" ng-model-options="{ allowInvalid: true, debounce: 200 }"
|
||||
id="table-editor-{{ column.name }}-boolean-true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
43
client/app/visualizations/table/table-editor.less
Normal file
43
client/app/visualizations/table/table-editor.less
Normal file
@@ -0,0 +1,43 @@
|
||||
.table-editor-container {
|
||||
.btn-group.btn-group-justified {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
|
||||
.btn {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-editor-query-columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
overflow: auto;
|
||||
|
||||
> div {
|
||||
min-width: 200px;
|
||||
width: 200px;
|
||||
padding: 0 10px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
cursor: move;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.table-editor-column-header {
|
||||
background: rgba(102, 136, 153, 0.05);
|
||||
padding: 10px;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<dynamic-table rows="gridRows" columns="gridColumns"></dynamic-table>
|
||||
<dynamic-table rows="gridRows" columns="gridColumns" items-per-page="options.itemsPerPage"></dynamic-table>
|
||||
|
||||
708
package-lock.json
generated
708
package-lock.json
generated
@@ -207,7 +207,7 @@
|
||||
"resolved": "https://registry.npmjs.org/angular-moment/-/angular-moment-1.1.0.tgz",
|
||||
"integrity": "sha512-aWrKEfQRtkqhxx31RjPhmGMWuF77uPlIWRNOiJU3+Q3wzossBqzBlJOkagUGt3N5or6OJP5Pq83dIjH9HHhhng==",
|
||||
"requires": {
|
||||
"moment": "2.18.1"
|
||||
"moment": "2.19.3"
|
||||
}
|
||||
},
|
||||
"angular-resizable": {
|
||||
@@ -289,54 +289,6 @@
|
||||
"normalize-path": "2.1.1"
|
||||
}
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
|
||||
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
|
||||
"dev": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz",
|
||||
"integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delegates": "1.0.0",
|
||||
"readable-stream": "2.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "1.0.7",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.0.3",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
|
||||
@@ -489,12 +441,6 @@
|
||||
"integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
|
||||
"dev": true
|
||||
},
|
||||
"async-foreach": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
|
||||
"integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -1481,15 +1427,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
|
||||
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz",
|
||||
@@ -2144,18 +2081,6 @@
|
||||
"integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=",
|
||||
"dev": true
|
||||
},
|
||||
"clone-deep": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.3.0.tgz",
|
||||
"integrity": "sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"for-own": "1.0.0",
|
||||
"is-plain-object": "2.0.4",
|
||||
"kind-of": "3.2.2",
|
||||
"shallow-clone": "0.1.2"
|
||||
}
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@@ -2409,12 +2334,6 @@
|
||||
"date-now": "0.1.4"
|
||||
}
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||
"dev": true
|
||||
},
|
||||
"constants-browserify": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
|
||||
@@ -2898,12 +2817,6 @@
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
|
||||
"dev": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
|
||||
@@ -4245,15 +4158,6 @@
|
||||
"integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
|
||||
"dev": true
|
||||
},
|
||||
"for-own": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
|
||||
"integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"for-in": "1.0.2"
|
||||
}
|
||||
},
|
||||
"foreach": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
|
||||
@@ -4329,18 +4233,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz",
|
||||
"integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"inherits": "2.0.3",
|
||||
"mkdirp": "0.5.1",
|
||||
"rimraf": "2.6.2"
|
||||
}
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
@@ -4356,62 +4248,6 @@
|
||||
"resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz",
|
||||
"integrity": "sha1-MxVkNAO/J5BsqAqzfDbs6UQO8zA="
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
|
||||
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aproba": "1.2.0",
|
||||
"console-control-strings": "1.1.0",
|
||||
"has-unicode": "2.0.1",
|
||||
"object-assign": "4.1.1",
|
||||
"signal-exit": "3.0.2",
|
||||
"string-width": "1.0.2",
|
||||
"strip-ansi": "3.0.1",
|
||||
"wide-align": "1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"gaze": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz",
|
||||
"integrity": "sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"globule": "1.2.0"
|
||||
}
|
||||
},
|
||||
"geojson-area": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/geojson-area/-/geojson-area-0.1.0.tgz",
|
||||
@@ -5669,17 +5505,6 @@
|
||||
"pinkie-promise": "2.0.1"
|
||||
}
|
||||
},
|
||||
"globule": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz",
|
||||
"integrity": "sha1-HcScaCLdnoovoAuiopUAboZkvQk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2",
|
||||
"lodash": "4.17.4",
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"glsl-inject-defines": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
|
||||
@@ -5925,12 +5750,6 @@
|
||||
"is-browser": "2.0.1"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
|
||||
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
|
||||
"dev": true
|
||||
},
|
||||
"hash-base": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
|
||||
@@ -6337,12 +6156,6 @@
|
||||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
|
||||
"dev": true
|
||||
},
|
||||
"in-publish": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
|
||||
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
|
||||
"dev": true
|
||||
},
|
||||
"incremental-convex-hull": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz",
|
||||
@@ -6680,15 +6493,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
|
||||
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isobject": "3.0.1"
|
||||
}
|
||||
},
|
||||
"is-posix-bracket": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz",
|
||||
@@ -6796,12 +6600,6 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"isobject": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||
"dev": true
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
@@ -7387,12 +7185,6 @@
|
||||
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.cond": {
|
||||
"version": "4.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
|
||||
@@ -7449,18 +7241,6 @@
|
||||
"integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.mergewith": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz",
|
||||
"integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.tail": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
|
||||
"integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.toarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
|
||||
@@ -8005,24 +7785,6 @@
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"mixin-object": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
|
||||
"integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"for-in": "0.1.8",
|
||||
"is-extendable": "0.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"for-in": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
|
||||
"integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
@@ -8041,9 +7803,9 @@
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.18.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
|
||||
"integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
|
||||
"version": "2.19.3",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.19.3.tgz",
|
||||
"integrity": "sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8="
|
||||
},
|
||||
"monotone-convex-hull-2d": {
|
||||
"version": "1.0.1",
|
||||
@@ -8146,12 +7908,6 @@
|
||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
|
||||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
|
||||
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@@ -8297,35 +8053,6 @@
|
||||
"integrity": "sha1-RjgRh59XPUUVWtap9D3ClujoXrw=",
|
||||
"dev": true
|
||||
},
|
||||
"node-gyp": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
|
||||
"integrity": "sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fstream": "1.0.11",
|
||||
"glob": "7.1.2",
|
||||
"graceful-fs": "4.1.11",
|
||||
"minimatch": "3.0.4",
|
||||
"mkdirp": "0.5.1",
|
||||
"nopt": "2.1.2",
|
||||
"npmlog": "4.1.2",
|
||||
"osenv": "0.1.4",
|
||||
"request": "2.83.0",
|
||||
"rimraf": "2.6.2",
|
||||
"semver": "5.3.0",
|
||||
"tar": "2.2.1",
|
||||
"which": "1.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
|
||||
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-libs-browser": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.0.0.tgz",
|
||||
@@ -8418,72 +8145,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-sass": {
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.5.3.tgz",
|
||||
"integrity": "sha1-0JydEXlkEjnRuX/8YjH9zsU+FWg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-foreach": "0.1.3",
|
||||
"chalk": "1.1.3",
|
||||
"cross-spawn": "3.0.1",
|
||||
"gaze": "1.1.2",
|
||||
"get-stdin": "4.0.1",
|
||||
"glob": "7.1.2",
|
||||
"in-publish": "2.0.0",
|
||||
"lodash.assign": "4.2.0",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"lodash.mergewith": "4.6.0",
|
||||
"meow": "3.7.0",
|
||||
"mkdirp": "0.5.1",
|
||||
"nan": "2.7.0",
|
||||
"node-gyp": "3.6.2",
|
||||
"npmlog": "4.1.2",
|
||||
"request": "2.83.0",
|
||||
"sass-graph": "2.2.4",
|
||||
"stdout-stream": "1.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
|
||||
"integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "4.1.1",
|
||||
"which": "1.3.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"nomnom": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
|
||||
@@ -8561,18 +8222,6 @@
|
||||
"path-key": "2.0.1"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
|
||||
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "1.1.4",
|
||||
"console-control-strings": "1.1.0",
|
||||
"gauge": "2.7.4",
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz",
|
||||
@@ -8594,6 +8243,11 @@
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"numeral": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
|
||||
"integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY="
|
||||
},
|
||||
"numeric": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz",
|
||||
@@ -8768,16 +8422,6 @@
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
|
||||
"dev": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz",
|
||||
"integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-homedir": "1.0.2",
|
||||
"os-tmpdir": "1.0.2"
|
||||
}
|
||||
},
|
||||
"p-finally": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
|
||||
@@ -10577,181 +10221,6 @@
|
||||
"resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-2.0.0.tgz",
|
||||
"integrity": "sha1-QOJXNqKMTM6qojP0W7hjc6J4W4Q="
|
||||
},
|
||||
"sass-graph": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
|
||||
"integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2",
|
||||
"lodash": "4.17.4",
|
||||
"scss-tokenizer": "0.2.3",
|
||||
"yargs": "7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
||||
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "1.0.2",
|
||||
"strip-ansi": "3.0.1",
|
||||
"wrap-ansi": "2.1.0"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
|
||||
"integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-exists": "2.1.0",
|
||||
"pinkie-promise": "2.0.1"
|
||||
}
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
"integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"parse-json": "2.2.0",
|
||||
"pify": "2.3.0",
|
||||
"pinkie-promise": "2.0.1",
|
||||
"strip-bom": "2.0.0"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"pinkie-promise": "2.0.1"
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
||||
"integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"pify": "2.3.0",
|
||||
"pinkie-promise": "2.0.1"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
"integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"load-json-file": "1.1.0",
|
||||
"normalize-package-data": "2.4.0",
|
||||
"path-type": "1.1.0"
|
||||
}
|
||||
},
|
||||
"read-pkg-up": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
|
||||
"integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "1.1.2",
|
||||
"read-pkg": "1.1.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"strip-bom": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
|
||||
"integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-utf8": "0.2.1"
|
||||
}
|
||||
},
|
||||
"yargs": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
|
||||
"integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "3.0.0",
|
||||
"cliui": "3.2.0",
|
||||
"decamelize": "1.2.0",
|
||||
"get-caller-file": "1.0.2",
|
||||
"os-locale": "1.4.0",
|
||||
"read-pkg-up": "1.0.1",
|
||||
"require-directory": "2.1.1",
|
||||
"require-main-filename": "1.0.1",
|
||||
"set-blocking": "2.0.0",
|
||||
"string-width": "1.0.2",
|
||||
"which-module": "1.0.0",
|
||||
"y18n": "3.2.1",
|
||||
"yargs-parser": "5.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sass-loader": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-6.0.6.tgz",
|
||||
"integrity": "sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "2.5.0",
|
||||
"clone-deep": "0.3.0",
|
||||
"loader-utils": "1.1.0",
|
||||
"lodash.tail": "4.1.1",
|
||||
"pify": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"pify": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
@@ -10767,27 +10236,6 @@
|
||||
"ajv": "5.2.3"
|
||||
}
|
||||
},
|
||||
"scss-tokenizer": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
|
||||
"integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-base64": "2.3.2",
|
||||
"source-map": "0.4.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
|
||||
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"amdefine": "1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"select-hose": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
|
||||
@@ -10919,35 +10367,6 @@
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
|
||||
"integrity": "sha1-WQnodLp3EG1zrEFM/sH/yofZcGA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "0.1.1",
|
||||
"kind-of": "2.0.1",
|
||||
"lazy-cache": "0.2.7",
|
||||
"mixin-object": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"kind-of": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz",
|
||||
"integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-buffer": "1.1.5"
|
||||
}
|
||||
},
|
||||
"lazy-cache": {
|
||||
"version": "0.2.7",
|
||||
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz",
|
||||
"integrity": "sha1-f+3fLctu23fRHvHRF6tf/fCrG2U=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"shallow-copy": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
|
||||
@@ -11426,47 +10845,6 @@
|
||||
"integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=",
|
||||
"dev": true
|
||||
},
|
||||
"stdout-stream": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.0.tgz",
|
||||
"integrity": "sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"readable-stream": "2.3.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
|
||||
"dev": true
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "1.0.7",
|
||||
"safe-buffer": "5.1.1",
|
||||
"string_decoder": "1.0.3",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"stream-browserify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
|
||||
@@ -11778,17 +11156,6 @@
|
||||
"through": "2.3.8"
|
||||
}
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz",
|
||||
"integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"block-stream": "0.0.9",
|
||||
"fstream": "1.0.11",
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"text-cache": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.1.0.tgz",
|
||||
@@ -12904,46 +12271,6 @@
|
||||
"resolved": "https://registry.npmjs.org/whoots-js/-/whoots-js-2.1.0.tgz",
|
||||
"integrity": "sha1-vLIBw04OrzNfzOWuLPh0V5qZxIc="
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz",
|
||||
"integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "1.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"window-size": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
|
||||
@@ -13051,23 +12378,6 @@
|
||||
"window-size": "0.1.0"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
|
||||
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
|
||||
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"zero-crossings": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz",
|
||||
|
||||
@@ -51,9 +51,10 @@
|
||||
"leaflet.markercluster": "^1.1.0",
|
||||
"markdown": "0.5.0",
|
||||
"material-design-iconic-font": "^2.2.0",
|
||||
"moment": "^2.18.1",
|
||||
"moment": "^2.19.3",
|
||||
"mousetrap": "^1.6.1",
|
||||
"mustache": "^2.3.0",
|
||||
"numeral": "^2.0.6",
|
||||
"pace-progress": "git+https://github.com/getredash/pace.git",
|
||||
"pivottable": "^2.15.0",
|
||||
"plotly.js": "1.30.1",
|
||||
|
||||
@@ -28,6 +28,8 @@ const config = {
|
||||
new webpack.DefinePlugin({
|
||||
ON_TEST: process.env.NODE_ENV === 'test'
|
||||
}),
|
||||
// Enforce angular to use jQuery instead of jqLite
|
||||
new webpack.ProvidePlugin({'window.jQuery': 'jquery'}),
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'vendor',
|
||||
minChunks: function (module, count) {
|
||||
|
||||
Reference in New Issue
Block a user