mirror of https://github.com/apache/superset.git
[js linting] use airbnb eslint settings (#796)
* add airbnb eslint settings and lint all the code * fix linting erros
This commit is contained in:
parent
f43e5f18d5
commit
1101de5ae4
|
@ -1,3 +1,6 @@
|
|||
node_modules/*
|
||||
vendor/*
|
||||
javascripts/dist/*
|
||||
visualizations/*
|
||||
stylesheets/*
|
||||
spec/*
|
||||
|
|
|
@ -1,234 +1,12 @@
|
|||
{
|
||||
"root": true,
|
||||
|
||||
"globals": {
|
||||
"Symbol": false,
|
||||
"Map": false,
|
||||
"Set": false,
|
||||
"Reflect": false,
|
||||
},
|
||||
|
||||
"env": {
|
||||
"es6": false,
|
||||
"browser": true,
|
||||
"node": true,
|
||||
},
|
||||
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 5,
|
||||
"sourceType": "module"
|
||||
},
|
||||
|
||||
"extends": "airbnb",
|
||||
"rules": {
|
||||
"array-bracket-spacing": [2, "never", {
|
||||
"singleValue": false,
|
||||
"objectsInArrays": false,
|
||||
"arraysInArrays": false
|
||||
}],
|
||||
"array-callback-return": [2],
|
||||
"block-spacing": [2, "always"],
|
||||
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
|
||||
"callback-return": [2, ["callback"]],
|
||||
"camelcase": [0],
|
||||
"comma-dangle": [2, "always-multiline"],
|
||||
"comma-spacing": [2],
|
||||
"comma-style": [2, "last"],
|
||||
"curly": [2, "all"],
|
||||
"eqeqeq": 2,
|
||||
"func-names": [0],
|
||||
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
|
||||
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
|
||||
"keyword-spacing": [2, {
|
||||
"before": true,
|
||||
"after": true,
|
||||
"overrides": {
|
||||
"return": { "after": true },
|
||||
"throw": { "after": true },
|
||||
"case": { "after": true }
|
||||
}
|
||||
}],
|
||||
"linebreak-style": [2, "unix"],
|
||||
"lines-around-comment": [2, {
|
||||
"beforeBlockComment": false,
|
||||
"afterBlockComment": false,
|
||||
"beforeLineComment": false,
|
||||
"allowBlockStart": true,
|
||||
"allowBlockEnd": true
|
||||
}],
|
||||
"max-depth": [2, 5],
|
||||
"max-len": [0, 80, 4],
|
||||
"max-nested-callbacks": [1, 3],
|
||||
"max-params": [1, 4],
|
||||
"new-parens": [2],
|
||||
"newline-after-var": [0],
|
||||
"no-bitwise": [0],
|
||||
"no-cond-assign": [2],
|
||||
"no-console": [1, { allow: ["warn", "error"] }],
|
||||
"no-const-assign": [2],
|
||||
"no-constant-condition": [2],
|
||||
"no-control-regex": [2],
|
||||
"no-debugger": [2],
|
||||
"no-delete-var": [2],
|
||||
"no-dupe-args": [2],
|
||||
"no-dupe-class-members": [2],
|
||||
"no-dupe-keys": [2],
|
||||
"no-duplicate-case": [2],
|
||||
"no-else-return": [0],
|
||||
"no-empty": [2],
|
||||
"no-eq-null": [0],
|
||||
"no-eval": [2],
|
||||
"no-ex-assign": [2],
|
||||
"no-extend-native": [2],
|
||||
"no-extra-bind": [2],
|
||||
"no-extra-boolean-cast": [2],
|
||||
"no-extra-label": [2],
|
||||
"no-extra-parens": [0], // needed for clearer #math eg (a - b) / c
|
||||
"no-extra-semi": [2],
|
||||
"no-fallthrough": [2],
|
||||
"no-floating-decimal": [2],
|
||||
"no-func-assign": [2],
|
||||
"no-implied-eval": [2],
|
||||
"no-implicit-coercion": [2, {
|
||||
"boolean": false,
|
||||
"number": true,
|
||||
"string": true
|
||||
}],
|
||||
"no-implicit-globals": [2],
|
||||
"no-inline-comments": [0],
|
||||
"no-invalid-regexp": [2],
|
||||
"no-irregular-whitespace": [2],
|
||||
"no-iterator": [2],
|
||||
"no-label-var": [2],
|
||||
"no-labels": [2, { "allowLoop": false, "allowSwitch": false }],
|
||||
"no-lone-blocks": [2],
|
||||
"no-lonely-if": [2],
|
||||
"no-loop-func": [2],
|
||||
"no-magic-numbers": [0], // doesn't work well with vis cosmetic constant
|
||||
"no-mixed-requires": [1, false],
|
||||
"no-mixed-spaces-and-tabs": [2, false],
|
||||
"no-multi-spaces": [2, {
|
||||
"exceptions": {
|
||||
"ImportDeclaration": true,
|
||||
"Property": true,
|
||||
"VariableDeclarator": true
|
||||
}
|
||||
}],
|
||||
"no-multi-str": [2],
|
||||
"no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1 }],
|
||||
"no-native-reassign": [2],
|
||||
"no-negated-condition": [2],
|
||||
"no-negated-in-lhs": [2],
|
||||
"no-nested-ternary": [0],
|
||||
"no-new": [2],
|
||||
"no-new-func": [2],
|
||||
"no-new-object": [2],
|
||||
"no-new-require": [0],
|
||||
"no-new-symbol": [2],
|
||||
"no-new-wrappers": [2],
|
||||
"no-obj-calls": [2],
|
||||
"no-octal": [2],
|
||||
"no-octal-escape": [2],
|
||||
"no-path-concat": [0],
|
||||
"no-process-env": [0],
|
||||
"no-process-exit": [2],
|
||||
"no-proto": [2],
|
||||
"no-redeclare": [2],
|
||||
"no-regex-spaces": [2],
|
||||
"no-restricted-modules": [0],
|
||||
"no-restricted-imports": [0],
|
||||
"no-restricted-syntax": [2,
|
||||
"DebuggerStatement",
|
||||
"LabeledStatement",
|
||||
"WithStatement"
|
||||
],
|
||||
"no-return-assign": [2, "always"],
|
||||
"no-script-url": [2],
|
||||
"no-self-assign": [2],
|
||||
"no-self-compare": [0],
|
||||
"no-sequences": [2],
|
||||
"no-shadow-restricted-names": [2],
|
||||
"no-spaced-func": [2],
|
||||
"no-sparse-arrays": [2],
|
||||
"no-sync": [0],
|
||||
"no-ternary": [0],
|
||||
"no-this-before-super": [2],
|
||||
"no-throw-literal": [2],
|
||||
"no-trailing-spaces": [2, { "skipBlankLines": false }],
|
||||
"no-undef": [2, { "typeof": true }],
|
||||
"no-undef-init": [2],
|
||||
"no-undefined": [0],
|
||||
"no-underscore-dangle": [0], // __data__ sometimes
|
||||
"no-unexpected-multiline": [2],
|
||||
"no-unmodified-loop-condition": [2],
|
||||
"no-unneeded-ternary": [2],
|
||||
"no-unreachable": [2],
|
||||
"no-unused-expressions": [2],
|
||||
"no-unused-labels": [2],
|
||||
"no-unused-vars": [2, {
|
||||
"vars": "all",
|
||||
"args": "none", // (d, i) pattern d3 func makes difficult to enforce
|
||||
"varsIgnorePattern": "jQuery"
|
||||
}],
|
||||
"no-use-before-define": [0],
|
||||
"no-useless-call": [2],
|
||||
"no-useless-concat": [2],
|
||||
"no-useless-constructor": [2],
|
||||
"no-void": [0],
|
||||
"no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],
|
||||
"no-with": [2],
|
||||
"no-whitespace-before-property": [2],
|
||||
"object-curly-spacing": [2, "always"],
|
||||
"object-shorthand": [2, "never"],
|
||||
"one-var": [0],
|
||||
"one-var-declaration-per-line": [2, "initializations"],
|
||||
"operator-assignment": [0, "always"],
|
||||
"padded-blocks": [0],
|
||||
"prefer-arrow-callback": [0],
|
||||
"prefer-const": [0],
|
||||
"prefer-reflect": [0],
|
||||
"prefer-rest-params": [0],
|
||||
"prefer-spread": [0],
|
||||
"prefer-template": [0],
|
||||
"quote-props": [2, "as-needed", { "keywords": true }],
|
||||
"radix": [2],
|
||||
"require-yield": [2],
|
||||
"semi": [2],
|
||||
"semi-spacing": [2, { "before": false, "after": true }],
|
||||
"sort-vars": [0],
|
||||
"sort-imports": [0],
|
||||
"space-before-function-paren": [2, { "anonymous": "always", "named": "never" }],
|
||||
"space-before-blocks": [2, { "functions": "always", "keywords": "always" }],
|
||||
"space-in-brackets": [0, "never", {
|
||||
"singleValue": true,
|
||||
"arraysInArrays": false,
|
||||
"arraysInObjects": false,
|
||||
"objectsInArrays": true,
|
||||
"objectsInObjects": true,
|
||||
"propertyName": false
|
||||
}],
|
||||
},
|
||||
// Temporarily not enforced
|
||||
"new-cap": [2], // @TODO more tricky for the moment
|
||||
"newline-per-chained-call": [2, { "ignoreChainWithDepth": 6 }],
|
||||
"no-param-reassign": [0], // turn on once default args supported
|
||||
"no-shadow": [2, { // @TODO more tricky for the moment with eg 'data'
|
||||
"builtinGlobals": false,
|
||||
"hoist": "functions",
|
||||
"allow": ["i", "d"]
|
||||
}],
|
||||
"space-in-parens": [2, "never"],
|
||||
"space-infix-ops": [2],
|
||||
"space-unary-ops": [2, { "words": true, "nonwords": false }],
|
||||
"spaced-comment": [2, "always", { "markers": ["!"] }],
|
||||
"spaced-line-comment": [0, "always"],
|
||||
"strict": [2, "global"],
|
||||
"template-curly-spacing": [2, "never"],
|
||||
"use-isnan": [2],
|
||||
"valid-jsdoc": [0],
|
||||
"valid-typeof": [2],
|
||||
"vars-on-top": [0],
|
||||
"wrap-iife": [2],
|
||||
"wrap-regex": [2],
|
||||
"yield-star-spacing": [2, { "before": false, "after": true }],
|
||||
"yoda": [2, "never", { "exceptRange": true, "onlyEquality": false }]
|
||||
"prefer-template": 0,
|
||||
"new-cap": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
"guard-for-in": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"func-names": 0,
|
||||
"react/jsx-no-bind": 0,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
var $ = require('jquery');
|
||||
var utils = require('./modules/utils');
|
||||
|
||||
const $ = require('jquery');
|
||||
const utils = require('./modules/utils');
|
||||
$(document).ready(function () {
|
||||
$(':checkbox[data-checkbox-api-prefix]').change(function () {
|
||||
var $this = $(this);
|
||||
var prefix = $this.data('checkbox-api-prefix');
|
||||
var id = $this.attr('id');
|
||||
utils.toggleCheckbox(prefix, "#" + id);
|
||||
});
|
||||
$(':checkbox[data-checkbox-api-prefix]').change(function () {
|
||||
const $this = $(this);
|
||||
const prefix = $this.data('checkbox-api-prefix');
|
||||
const id = $this.attr('id');
|
||||
utils.toggleCheckbox(prefix, '#' + id);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,39 +1,61 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('../modules/caravel.js');
|
||||
var d3 = require('d3');
|
||||
var urlLib = require('url');
|
||||
var showModal = require('../modules/utils.js').showModal;
|
||||
const $ = window.$ = require('jquery');
|
||||
const jQuery = window.jQuery = $;
|
||||
const px = require('../modules/caravel.js');
|
||||
const d3 = require('d3');
|
||||
const urlLib = require('url');
|
||||
const showModal = require('../modules/utils.js').showModal;
|
||||
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import SliceAdder from './components/SliceAdder.jsx';
|
||||
import GridLayout from './components/GridLayout.jsx';
|
||||
|
||||
var ace = require('brace');
|
||||
const ace = require('brace');
|
||||
require('bootstrap');
|
||||
require('brace/mode/css');
|
||||
require('brace/theme/crimson_editor');
|
||||
require('./main.css');
|
||||
require('../caravel-select2.js');
|
||||
|
||||
// Injects the passed css string into a style sheet with the specified className
|
||||
// If a stylesheet doesn't exist with the passed className, one will be injected into <head>
|
||||
function injectCss(className, css) {
|
||||
const head = document.head || document.getElementsByTagName('head')[0];
|
||||
let style = document.querySelector('.' + className);
|
||||
|
||||
var Dashboard = function (dashboardData) {
|
||||
var dashboard = $.extend(dashboardData, {
|
||||
if (!style) {
|
||||
if (className.split(' ').length > 1) {
|
||||
throw new Error('This method only supports selections with a single class name.');
|
||||
}
|
||||
style = document.createElement('style');
|
||||
style.className = className;
|
||||
style.type = 'text/css';
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.innerHTML = css;
|
||||
}
|
||||
}
|
||||
|
||||
function dashboardContainer(dashboardData) {
|
||||
let dashboard = $.extend(dashboardData, {
|
||||
filters: {},
|
||||
init: function () {
|
||||
init() {
|
||||
this.initDashboardView();
|
||||
this.firstLoad = true;
|
||||
px.initFavStars();
|
||||
var sliceObjects = [],
|
||||
dash = this;
|
||||
dashboard.slices.forEach(function (data) {
|
||||
const sliceObjects = [];
|
||||
const dash = this;
|
||||
dashboard.slices.forEach((data) => {
|
||||
if (data.error) {
|
||||
var html = '<div class="alert alert-danger">' + data.error + '</div>';
|
||||
$('#slice_' + data.slice_id).find('.token').html(html);
|
||||
const html = '<div class="alert alert-danger">' + data.error + '</div>';
|
||||
$('#slice_' + data.sliceId).find('.token').html(html);
|
||||
} else {
|
||||
var slice = px.Slice(data, dash);
|
||||
$('#slice_' + data.slice_id).find('a.refresh').click(function () {
|
||||
const slice = px.Slice(data, dash);
|
||||
$('#slice_' + data.sliceId).find('a.refresh').click(() => {
|
||||
slice.render(true);
|
||||
});
|
||||
sliceObjects.push(slice);
|
||||
|
@ -45,81 +67,81 @@ var Dashboard = function (dashboardData) {
|
|||
this.startPeriodicRender(0);
|
||||
this.bindResizeToWindowResize();
|
||||
},
|
||||
loadPreSelectFilters: function () {
|
||||
loadPreSelectFilters() {
|
||||
try {
|
||||
var filters = JSON.parse(px.getParam("preselect_filters") || "{}");
|
||||
for (var slice_id in filters) {
|
||||
for (var col in filters[slice_id]) {
|
||||
this.setFilter(slice_id, col, filters[slice_id][col], false, false);
|
||||
const filters = JSON.parse(px.getParam('preselect_filters') || '{}');
|
||||
for (const sliceId in filters) {
|
||||
for (const col in filters[sliceId]) {
|
||||
this.setFilter(sliceId, col, filters[sliceId][col], false, false);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
// console.error(e);
|
||||
}
|
||||
},
|
||||
setFilter: function (slice_id, col, vals, refresh) {
|
||||
this.addFilter(slice_id, col, vals, false, refresh);
|
||||
setFilter(sliceId, col, vals, refresh) {
|
||||
this.addFilter(sliceId, col, vals, false, refresh);
|
||||
},
|
||||
addFilter: function (slice_id, col, vals, merge = true, refresh = true) {
|
||||
if (!(slice_id in this.filters)) {
|
||||
this.filters[slice_id] = {};
|
||||
addFilter(sliceId, col, vals, merge = true, refresh = true) {
|
||||
if (!(sliceId in this.filters)) {
|
||||
this.filters[sliceId] = {};
|
||||
}
|
||||
if (!(col in this.filters[slice_id]) || !merge) {
|
||||
this.filters[slice_id][col] = vals;
|
||||
if (!(col in this.filters[sliceId]) || !merge) {
|
||||
this.filters[sliceId][col] = vals;
|
||||
} else {
|
||||
this.filters[slice_id][col] = d3.merge([this.filters[slice_id][col], vals]);
|
||||
this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]);
|
||||
}
|
||||
if (refresh) {
|
||||
this.refreshExcept(slice_id);
|
||||
this.refreshExcept(sliceId);
|
||||
}
|
||||
this.updateFilterParamsInUrl();
|
||||
},
|
||||
readFilters: function () {
|
||||
readFilters() {
|
||||
// Returns a list of human readable active filters
|
||||
return JSON.stringify(this.filters, null, 4);
|
||||
},
|
||||
updateFilterParamsInUrl: function () {
|
||||
var urlObj = urlLib.parse(location.href, true);
|
||||
updateFilterParamsInUrl() {
|
||||
const urlObj = urlLib.parse(location.href, true);
|
||||
urlObj.query = urlObj.query || {};
|
||||
urlObj.query.preselect_filters = this.readFilters();
|
||||
urlObj.search = null;
|
||||
history.pushState(urlObj.query, window.title, urlLib.format(urlObj));
|
||||
},
|
||||
bindResizeToWindowResize: function () {
|
||||
var resizeTimer;
|
||||
var dash = this;
|
||||
$(window).on('resize', function (e) {
|
||||
bindResizeToWindowResize() {
|
||||
let resizeTimer;
|
||||
const dash = this;
|
||||
$(window).on('resize', () => {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function () {
|
||||
dash.slices.forEach(function (slice) {
|
||||
resizeTimer = setTimeout(() => {
|
||||
dash.slices.forEach((slice) => {
|
||||
slice.resize();
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
stopPeriodicRender: function () {
|
||||
stopPeriodicRender() {
|
||||
if (this.refreshTimer) {
|
||||
clearTimeout(this.refreshTimer);
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
},
|
||||
startPeriodicRender: function (interval) {
|
||||
startPeriodicRender(interval) {
|
||||
this.stopPeriodicRender();
|
||||
var dash = this;
|
||||
var maxRandomDelay = Math.min(interval * 0.2, 5000);
|
||||
var refreshAll = function () {
|
||||
const dash = this;
|
||||
const maxRandomDelay = Math.min(interval * 0.2, 5000);
|
||||
const refreshAll = function () {
|
||||
dash.slices.forEach(function (slice) {
|
||||
var force = !dash.firstLoad;
|
||||
const force = !dash.firstLoad;
|
||||
setTimeout(function () {
|
||||
slice.render(force);
|
||||
},
|
||||
//Randomize to prevent all widgets refreshing at the same time
|
||||
// Randomize to prevent all widgets refreshing at the same time
|
||||
maxRandomDelay * Math.random());
|
||||
});
|
||||
dash.firstLoad = false;
|
||||
};
|
||||
|
||||
var fetchAndRender = function () {
|
||||
const fetchAndRender = function () {
|
||||
refreshAll();
|
||||
if (interval > 0) {
|
||||
dash.refreshTimer = setTimeout(function () {
|
||||
|
@ -129,122 +151,130 @@ var Dashboard = function (dashboardData) {
|
|||
};
|
||||
fetchAndRender();
|
||||
},
|
||||
refreshExcept: function (slice_id) {
|
||||
var immune = this.metadata.filter_immune_slices || [];
|
||||
refreshExcept(sliceId) {
|
||||
const immune = this.metadata.filter_immune_slices || [];
|
||||
this.slices.forEach(function (slice) {
|
||||
if (slice.data.slice_id !== slice_id && immune.indexOf(slice.data.slice_id) === -1) {
|
||||
if (slice.data.sliceId !== sliceId && immune.indexOf(slice.data.sliceId) === -1) {
|
||||
slice.render();
|
||||
}
|
||||
});
|
||||
},
|
||||
clearFilters: function (slice_id) {
|
||||
delete this.filters[slice_id];
|
||||
this.refreshExcept(slice_id);
|
||||
clearFilters(sliceId) {
|
||||
delete this.filters[sliceId];
|
||||
this.refreshExcept(sliceId);
|
||||
this.updateFilterParamsInUrl();
|
||||
},
|
||||
removeFilter: function (slice_id, col, vals) {
|
||||
if (slice_id in this.filters) {
|
||||
if (col in this.filters[slice_id]) {
|
||||
var a = [];
|
||||
this.filters[slice_id][col].forEach(function (v) {
|
||||
removeFilter(sliceId, col, vals) {
|
||||
if (sliceId in this.filters) {
|
||||
if (col in this.filters[sliceId]) {
|
||||
const a = [];
|
||||
this.filters[sliceId][col].forEach(function (v) {
|
||||
if (vals.indexOf(v) < 0) {
|
||||
a.push(v);
|
||||
}
|
||||
});
|
||||
this.filters[slice_id][col] = a;
|
||||
this.filters[sliceId][col] = a;
|
||||
}
|
||||
}
|
||||
this.refreshExcept(slice_id);
|
||||
this.refreshExcept(sliceId);
|
||||
this.updateFilterParamsInUrl();
|
||||
},
|
||||
getSlice: function (slice_id) {
|
||||
slice_id = parseInt(slice_id, 10);
|
||||
for (var i=0; i < this.slices.length; i++) {
|
||||
if (this.slices[i].data.slice_id === slice_id) {
|
||||
return this.slices[i];
|
||||
getSlice(sliceId) {
|
||||
const id = parseInt(sliceId, 10);
|
||||
let i = 0;
|
||||
let slice = null;
|
||||
while (i < this.slices.length) {
|
||||
// when the slice is found, assign to slice and break;
|
||||
if (this.slices[i].data.slice_id === id) {
|
||||
slice = this.slices[i];
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return slice;
|
||||
},
|
||||
showAddSlice: function () {
|
||||
var slicesOnDashMap = {};
|
||||
this.reactGridLayout.serialize().forEach(function (position) {
|
||||
showAddSlice() {
|
||||
const slicesOnDashMap = {};
|
||||
const layoutPositions = this.reactGridLayout.serialize();
|
||||
layoutPositions.forEach((position) => {
|
||||
slicesOnDashMap[position.slice_id] = true;
|
||||
}, this);
|
||||
|
||||
});
|
||||
render(
|
||||
<SliceAdder dashboard={dashboard} slicesOnDashMap={slicesOnDashMap} caravel={px} />,
|
||||
document.getElementById("add-slice-container")
|
||||
document.getElementById('add-slice-container')
|
||||
);
|
||||
},
|
||||
getAjaxErrorMsg: function (error) {
|
||||
var respJSON = error.responseJSON;
|
||||
getAjaxErrorMsg(error) {
|
||||
const respJSON = error.responseJSON;
|
||||
return (respJSON && respJSON.message) ? respJSON.message :
|
||||
error.responseText;
|
||||
},
|
||||
addSlicesToDashboard: function (sliceIds) {
|
||||
addSlicesToDashboard(sliceIds) {
|
||||
const getAjaxErrorMsg = this.getAjaxErrorMsg;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
type: 'POST',
|
||||
url: '/caravel/add_slices/' + dashboard.id + '/',
|
||||
data: {
|
||||
data: JSON.stringify({ slice_ids: sliceIds })
|
||||
data: JSON.stringify({ slice_ids: sliceIds }),
|
||||
},
|
||||
success: function () {
|
||||
success() {
|
||||
// Refresh page to allow for slices to re-render
|
||||
window.location.reload();
|
||||
},
|
||||
error: function (error) {
|
||||
var errorMsg = this.getAjaxErrorMsg(error);
|
||||
error(error) {
|
||||
const errorMsg = getAjaxErrorMsg(error);
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, there was an error adding slices to this dashboard: </ br>" + errorMsg
|
||||
title: 'Error',
|
||||
body: 'Sorry, there was an error adding slices to this dashboard: </ br>' + errorMsg,
|
||||
});
|
||||
}.bind(this)
|
||||
},
|
||||
});
|
||||
},
|
||||
saveDashboard: function () {
|
||||
var expandedSlices = {};
|
||||
$.each($(".slice_info"), function (i, d) {
|
||||
var widget = $(this).parents('.widget');
|
||||
var sliceDescription = widget.find('.slice_description');
|
||||
if (sliceDescription.is(":visible")) {
|
||||
saveDashboard() {
|
||||
const expandedSlices = {};
|
||||
$.each($('.slice_info'), function () {
|
||||
const widget = $(this).parents('.widget');
|
||||
const sliceDescription = widget.find('.slice_description');
|
||||
if (sliceDescription.is(':visible')) {
|
||||
expandedSlices[$(widget).attr('data-slice-id')] = true;
|
||||
}
|
||||
});
|
||||
var data = {
|
||||
positions: this.reactGridLayout.serialize(),
|
||||
const positions = this.reactGridLayout.serialize();
|
||||
const data = {
|
||||
positions,
|
||||
css: this.editor.getValue(),
|
||||
expanded_slices: expandedSlices
|
||||
expanded_slices: expandedSlices,
|
||||
};
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
type: 'POST',
|
||||
url: '/caravel/save_dash/' + dashboard.id + '/',
|
||||
data: {
|
||||
data: JSON.stringify(data)
|
||||
data: JSON.stringify(data),
|
||||
},
|
||||
success: function () {
|
||||
success() {
|
||||
showModal({
|
||||
title: "Success",
|
||||
body: "This dashboard was saved successfully."
|
||||
title: 'Success',
|
||||
body: 'This dashboard was saved successfully.',
|
||||
});
|
||||
},
|
||||
error: function (error) {
|
||||
var errorMsg = this.getAjaxErrorMsg(error);
|
||||
error(error) {
|
||||
const errorMsg = this.getAjaxErrorMsg(error);
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, there was an error saving this dashboard: </ br>" + errorMsg
|
||||
title: 'Error',
|
||||
body: 'Sorry, there was an error saving this dashboard: </ br>' + errorMsg,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
initDashboardView: function () {
|
||||
initDashboardView() {
|
||||
this.posDict = {};
|
||||
this.position_json.forEach(function (position) {
|
||||
this.posDict[position.slice_id] = position;
|
||||
}, this);
|
||||
|
||||
this.reactGridLayout = render(
|
||||
<GridLayout slices={this.slices} posDict={this.posDict} dashboard={dashboard}/>,
|
||||
document.getElementById("grid-container")
|
||||
<GridLayout slices={this.slices} posDict={this.posDict} dashboard={dashboard} />,
|
||||
document.getElementById('grid-container')
|
||||
);
|
||||
|
||||
this.curUserId = $('.dashboard').data('user');
|
||||
|
@ -260,101 +290,77 @@ var Dashboard = function (dashboardData) {
|
|||
$(this).find('.chart-controls').fadeOut(300);
|
||||
}
|
||||
);
|
||||
$("div.grid-container").css('visibility', 'visible');
|
||||
$("#savedash").click(this.saveDashboard.bind(this));
|
||||
$("#add-slice").click(this.showAddSlice.bind(this));
|
||||
$('div.grid-container').css('visibility', 'visible');
|
||||
$('#savedash').click(this.saveDashboard.bind(this));
|
||||
$('#add-slice').click(this.showAddSlice.bind(this));
|
||||
|
||||
var editor = ace.edit("dash_css");
|
||||
const editor = ace.edit('dash_css');
|
||||
this.editor = editor;
|
||||
editor.$blockScrolling = Infinity;
|
||||
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setTheme('ace/theme/crimson_editor');
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity,
|
||||
useWorker: false
|
||||
useWorker: false,
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/css");
|
||||
editor.getSession().setMode('ace/mode/css');
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth: true,
|
||||
});
|
||||
$("#css_template").on("change", function () {
|
||||
var css = $(this).find('option:selected').data('css');
|
||||
$('#css_template').on('change', function () {
|
||||
const css = $(this).find('option:selected').data('css');
|
||||
editor.setValue(css);
|
||||
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
injectCss('dashboard-template', css);
|
||||
});
|
||||
$('#filters').click(function () {
|
||||
$('#filters').click(() => {
|
||||
showModal({
|
||||
title: "<span class='fa fa-info-circle'></span> Current Global Filters",
|
||||
body: "The following global filters are currently applied:<br/>" + dashboard.readFilters()
|
||||
title: '<span class="fa fa-info-circle"></span> Current Global Filters',
|
||||
body: 'The following global filters are currently applied:<br/>' +
|
||||
dashboard.readFilters(),
|
||||
});
|
||||
});
|
||||
$("#refresh_dash_interval").on("change", function () {
|
||||
var interval = $(this).find('option:selected').val() * 1000;
|
||||
$('#refresh_dash_interval').on('change', function () {
|
||||
const interval = $(this).find('option:selected').val() * 1000;
|
||||
dashboard.startPeriodicRender(interval);
|
||||
});
|
||||
$('#refresh_dash').click(function () {
|
||||
dashboard.slices.forEach(function (slice) {
|
||||
$('#refresh_dash').click(() => {
|
||||
dashboard.slices.forEach((slice) => {
|
||||
slice.render(true);
|
||||
});
|
||||
});
|
||||
|
||||
$("div.widget").click(function (e) {
|
||||
var $this = $(this);
|
||||
var $target = $(e.target);
|
||||
$('div.widget').click(function (e) {
|
||||
const $this = $(this);
|
||||
const $target = $(e.target);
|
||||
|
||||
if ($target.hasClass("slice_info")) {
|
||||
$this.find(".slice_description").slideToggle(0, function () {
|
||||
if ($target.hasClass('slice_info')) {
|
||||
$this.find('.slice_description').slideToggle(0, function () {
|
||||
$this.find('.refresh').click();
|
||||
});
|
||||
} else if ($target.hasClass("controls-toggle")) {
|
||||
$this.find(".chart-controls").toggle();
|
||||
} else if ($target.hasClass('controls-toggle')) {
|
||||
$this.find('.chart-controls').toggle();
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("change", function () {
|
||||
var css = editor.getValue();
|
||||
editor.on('change', function () {
|
||||
const css = editor.getValue();
|
||||
$('#dash_css').val(css);
|
||||
injectCss("dashboard-template", css);
|
||||
injectCss('dashboard-template', css);
|
||||
});
|
||||
|
||||
var css = $('.dashboard').data('css');
|
||||
injectCss("dashboard-template", css);
|
||||
|
||||
// Injects the passed css string into a style sheet with the specified className
|
||||
// If a stylesheet doesn't exist with the passed className, one will be injected into <head>
|
||||
function injectCss(className, css) {
|
||||
|
||||
var head = document.head || document.getElementsByTagName('head')[0];
|
||||
var style = document.querySelector('.' + className);
|
||||
|
||||
if (!style) {
|
||||
if (className.split(' ').length > 1) {
|
||||
throw new Error("This method only supports selections with a single class name.");
|
||||
}
|
||||
style = document.createElement('style');
|
||||
style.className = className;
|
||||
style.type = 'text/css';
|
||||
head.appendChild(style);
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css;
|
||||
} else {
|
||||
style.innerHTML = css;
|
||||
}
|
||||
}
|
||||
}
|
||||
const css = $('.dashboard').data('css');
|
||||
injectCss('dashboard-template', css);
|
||||
},
|
||||
});
|
||||
dashboard.init();
|
||||
return dashboard;
|
||||
};
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
Dashboard($('.dashboard').data('dashboard'));
|
||||
$(document).ready(() => {
|
||||
dashboardContainer($('.dashboard').data('dashboard'));
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
});
|
||||
|
|
|
@ -1,108 +1,69 @@
|
|||
import $ from 'jquery';
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Responsive, WidthProvider } from 'react-grid-layout';
|
||||
const ResponsiveReactGridLayout = WidthProvider(Responsive);
|
||||
import SliceCell from './SliceCell';
|
||||
|
||||
require('../../../node_modules/react-grid-layout/css/styles.css');
|
||||
require('../../../node_modules/react-resizable/css/styles.css');
|
||||
|
||||
const sliceCellPropTypes = {
|
||||
slice: PropTypes.object.isRequired,
|
||||
removeSlice: PropTypes.func.isRequired,
|
||||
expandedSlices: PropTypes.object
|
||||
};
|
||||
|
||||
const gridLayoutPropTypes = {
|
||||
const propTypes = {
|
||||
dashboard: PropTypes.object.isRequired,
|
||||
slices: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
posDict: PropTypes.object.isRequired
|
||||
posDict: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
class SliceCell extends React.Component {
|
||||
render() {
|
||||
const slice = this.props.slice,
|
||||
createMarkup = function () {
|
||||
return { __html: slice.description_markeddown };
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="chart-header">
|
||||
<div className="row">
|
||||
<div className="col-md-12 text-center header">
|
||||
{slice.slice_name}
|
||||
</div>
|
||||
<div className="col-md-12 chart-controls">
|
||||
<div className="pull-left">
|
||||
<a title="Move chart" data-toggle="tooltip">
|
||||
<i className="fa fa-arrows drag"/>
|
||||
</a>
|
||||
<a className="refresh" title="Force refresh data" data-toggle="tooltip">
|
||||
<i className="fa fa-repeat"/>
|
||||
</a>
|
||||
</div>
|
||||
<div className="pull-right">
|
||||
{slice.description ?
|
||||
<a title="Toggle chart description">
|
||||
<i className="fa fa-info-circle slice_info" title={slice.description} data-toggle="tooltip"/>
|
||||
</a>
|
||||
: ""}
|
||||
<a href={slice.edit_url} title="Edit chart" data-toggle="tooltip">
|
||||
<i className="fa fa-pencil"/>
|
||||
</a>
|
||||
<a href={slice.slice_url} title="Explore chart" data-toggle="tooltip">
|
||||
<i className="fa fa-share"/>
|
||||
</a>
|
||||
<a className="remove-chart" title="Remove chart from dashboard" data-toggle="tooltip">
|
||||
<i className="fa fa-close" onClick={this.props.removeSlice.bind(null, slice.slice_id)}/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="slice_description bs-callout bs-callout-default"
|
||||
style={this.props.expandedSlices && this.props.expandedSlices[String(slice.slice_id)] ? {} : { display: "none" }}
|
||||
dangerouslySetInnerHTML={createMarkup()}>
|
||||
</div>
|
||||
<div className="row chart-container">
|
||||
<input type="hidden" value="false"/>
|
||||
<div id={slice.token} className="token col-md-12">
|
||||
<img src="/static/assets/images/loading.gif" className="loading" alt="loading"/>
|
||||
<div className="slice_container" id={slice.token + "_con"}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GridLayout extends React.Component {
|
||||
componentWillMount() {
|
||||
const layout = [];
|
||||
|
||||
this.props.slices.forEach((slice, index) => {
|
||||
let pos = this.props.posDict[slice.slice_id];
|
||||
if (!pos) {
|
||||
pos = {
|
||||
col: (index * 4 + 1) % 12,
|
||||
row: Math.floor((index) / 3) * 4,
|
||||
size_x: 4,
|
||||
size_y: 4,
|
||||
};
|
||||
}
|
||||
|
||||
layout.push({
|
||||
i: String(slice.slice_id),
|
||||
x: pos.col - 1,
|
||||
y: pos.row,
|
||||
w: pos.size_x,
|
||||
minW: 2,
|
||||
h: pos.size_y,
|
||||
});
|
||||
});
|
||||
|
||||
this.setState({
|
||||
layout,
|
||||
slices: this.props.slices,
|
||||
});
|
||||
}
|
||||
|
||||
onResizeStop(layout, oldItem, newItem) {
|
||||
const newSlice = this.props.dashboard.getSlice(newItem.i);
|
||||
if (oldItem.w !== newItem.w || oldItem.h !== newItem.h) {
|
||||
this.setState({ layout }, () => { newSlice.resize(); });
|
||||
}
|
||||
}
|
||||
|
||||
onDragStop(layout) {
|
||||
this.setState({ layout });
|
||||
}
|
||||
|
||||
removeSlice(sliceId) {
|
||||
$('[data-toggle="tooltip"]').tooltip("hide");
|
||||
$('[data-toggle=tooltip]').tooltip('hide');
|
||||
this.setState({
|
||||
layout: this.state.layout.filter(function (reactPos) {
|
||||
return reactPos.i !== String(sliceId);
|
||||
}),
|
||||
slices: this.state.slices.filter(function (slice) {
|
||||
return slice.slice_id !== sliceId;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
onResizeStop(layout, oldItem, newItem) {
|
||||
if (oldItem.w !== newItem.w || oldItem.h !== newItem.h) {
|
||||
this.setState({
|
||||
layout: layout
|
||||
}, function () {
|
||||
this.props.dashboard.getSlice(newItem.i).resize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDragStop(layout) {
|
||||
this.setState({
|
||||
layout: layout
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -113,41 +74,11 @@ class GridLayout extends React.Component {
|
|||
col: reactPos.x + 1,
|
||||
row: reactPos.y,
|
||||
size_x: reactPos.w,
|
||||
size_y: reactPos.h
|
||||
size_y: reactPos.h,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
var layout = [];
|
||||
|
||||
this.props.slices.forEach(function (slice, index) {
|
||||
var pos = this.props.posDict[slice.slice_id];
|
||||
if (!pos) {
|
||||
pos = {
|
||||
col: (index * 4 + 1) % 12,
|
||||
row: Math.floor((index) / 3) * 4,
|
||||
size_x: 4,
|
||||
size_y: 4
|
||||
};
|
||||
}
|
||||
|
||||
layout.push({
|
||||
i: String(slice.slice_id),
|
||||
x: pos.col - 1,
|
||||
y: pos.row,
|
||||
w: pos.size_x,
|
||||
minW: 2,
|
||||
h: pos.size_y
|
||||
});
|
||||
}, this);
|
||||
|
||||
this.setState({
|
||||
layout: layout,
|
||||
slices: this.props.slices
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ResponsiveReactGridLayout
|
||||
|
@ -157,30 +88,35 @@ class GridLayout extends React.Component {
|
|||
onDragStop={this.onDragStop.bind(this)}
|
||||
cols={{ lg: 12, md: 12, sm: 10, xs: 8, xxs: 6 }}
|
||||
rowHeight={100}
|
||||
autoSize={true}
|
||||
autoSize
|
||||
margin={[20, 20]}
|
||||
useCSSTransforms={false}
|
||||
draggableHandle=".drag">
|
||||
{this.state.slices.map((slice) => {
|
||||
return (
|
||||
<div
|
||||
id={'slice_' + slice.slice_id}
|
||||
key={slice.slice_id}
|
||||
data-slice-id={slice.slice_id}
|
||||
className={"widget " + slice.viz_name}>
|
||||
<SliceCell
|
||||
slice={slice}
|
||||
removeSlice={this.removeSlice.bind(this)}
|
||||
expandedSlices={this.props.dashboard.metadata.expanded_slices}/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
draggableHandle=".drag"
|
||||
>
|
||||
{
|
||||
/* eslint arrow-body-style: 0 */
|
||||
this.state.slices.map((slice) => {
|
||||
return (
|
||||
<div
|
||||
id={'slice_' + slice.slice_id}
|
||||
key={slice.slice_id}
|
||||
data-slice-id={slice.slice_id}
|
||||
className={`widget ${slice.viz_name}`}
|
||||
>
|
||||
<SliceCell
|
||||
slice={slice}
|
||||
removeSlice={(sliceId) => this.removeSlice(sliceId)}
|
||||
expandedSlices={this.props.dashboard.metadata.expanded_slices}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ResponsiveReactGridLayout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SliceCell.propTypes = sliceCellPropTypes;
|
||||
GridLayout.propTypes = gridLayoutPropTypes;
|
||||
GridLayout.propTypes = propTypes;
|
||||
|
||||
export default GridLayout;
|
||||
|
|
|
@ -4,37 +4,37 @@ const propTypes = {
|
|||
modalId: PropTypes.string.isRequired,
|
||||
title: PropTypes.string,
|
||||
modalContent: PropTypes.node,
|
||||
customButtons: PropTypes.node
|
||||
customButton: PropTypes.node,
|
||||
};
|
||||
|
||||
class Modal extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="modal fade" id={this.props.modalId} role="dialog">
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 className="modal-title">{this.props.title}</h4>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{this.props.modalContent}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button type="button"
|
||||
className="btn btn-default"
|
||||
data-dismiss="modal">
|
||||
Cancel
|
||||
</button>
|
||||
{this.props.customButtons}
|
||||
</div>
|
||||
</div>
|
||||
function Modal({ modalId, title, modalContent, customButton }) {
|
||||
return (
|
||||
<div className="modal fade" id={modalId} role="dialog">
|
||||
<div className="modal-dialog" role="document">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 className="modal-title">{title}</h4>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{modalContent}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-default"
|
||||
data-dismiss="modal"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
{customButton}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Modal.propTypes = propTypes;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import $ from 'jquery';
|
||||
import React, { PropTypes } from 'react';
|
||||
import update from 'immutability-helper';
|
||||
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
|
||||
|
@ -6,15 +7,14 @@ require('../../../node_modules/react-bootstrap-table/css/react-bootstrap-table.c
|
|||
|
||||
const propTypes = {
|
||||
dashboard: PropTypes.object.isRequired,
|
||||
caravel: PropTypes.object.isRequired
|
||||
caravel: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
class SliceAdder extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
slices: []
|
||||
slices: [],
|
||||
};
|
||||
|
||||
this.addSlices = this.addSlices.bind(this);
|
||||
|
@ -22,46 +22,47 @@ class SliceAdder extends React.Component {
|
|||
this.toggleAllSlices = this.toggleAllSlices.bind(this);
|
||||
this.slicesLoaded = false;
|
||||
this.selectRowProp = {
|
||||
mode: "checkbox",
|
||||
mode: 'checkbox',
|
||||
clickToSelect: true,
|
||||
onSelect: this.toggleSlice,
|
||||
onSelectAll: this.toggleAllSlices
|
||||
onSelectAll: this.toggleAllSlices,
|
||||
};
|
||||
this.options = {
|
||||
defaultSortOrder: "desc",
|
||||
defaultSortName: "modified",
|
||||
sizePerPage: 10
|
||||
defaultSortOrder: 'desc',
|
||||
defaultSortName: 'modified',
|
||||
sizePerPage: 10,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
var uri = "/sliceaddview/api/read?_flt_0_created_by=" + this.props.dashboard.curUserId;
|
||||
const uri = '/sliceaddview/api/read?_flt_0_created_by=' + this.props.dashboard.curUserId;
|
||||
this.slicesRequest = $.ajax({
|
||||
url: uri,
|
||||
type: 'GET',
|
||||
success: function (response) {
|
||||
this.slicesLoaded = true;
|
||||
|
||||
// Prepare slice data for table
|
||||
let slices = response.result;
|
||||
slices.forEach(function (slice) {
|
||||
slice.id = slice.data.slice_id;
|
||||
slice.sliceName = slice.data.slice_name;
|
||||
slice.vizType = slice.viz_type;
|
||||
slice.modified = slice.modified;
|
||||
const slices = response.result.map(function (slice) {
|
||||
return {
|
||||
id: slice.data.slice_id,
|
||||
sliceName: slice.data.slice_name,
|
||||
vizType: slice.viz_type,
|
||||
modified: slice.modified,
|
||||
data: slice.data,
|
||||
};
|
||||
});
|
||||
|
||||
this.setState({
|
||||
slices: slices,
|
||||
selectionMap: {}
|
||||
slices,
|
||||
selectionMap: {},
|
||||
});
|
||||
}.bind(this),
|
||||
error: function (error) {
|
||||
this.errored = true;
|
||||
this.setState({
|
||||
errorMsg: this.props.dashboard.getAjaxErrorMsg(error)
|
||||
errorMsg: this.props.dashboard.getAjaxErrorMsg(error),
|
||||
});
|
||||
}.bind(this)
|
||||
}.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -70,13 +71,12 @@ class SliceAdder extends React.Component {
|
|||
}
|
||||
|
||||
addSlices() {
|
||||
var slices = this.state.slices.filter(function (slice) {
|
||||
const slices = this.state.slices.filter(function (slice) {
|
||||
return this.state.selectionMap[slice.id];
|
||||
}, this);
|
||||
|
||||
slices.forEach(function (slice) {
|
||||
var sliceObj = this.props.caravel.Slice(slice.data, this.props.dashboard);
|
||||
$("#slice_" + slice.data.slice_id).find('a.refresh').click(function () {
|
||||
const sliceObj = this.props.caravel.Slice(slice.data, this.props.dashboard);
|
||||
$('#slice_' + slice.data.slice_id).find('a.refresh').click(function () {
|
||||
sliceObj.render(true);
|
||||
});
|
||||
this.props.dashboard.slices.push(sliceObj);
|
||||
|
@ -89,23 +89,23 @@ class SliceAdder extends React.Component {
|
|||
this.setState({
|
||||
selectionMap: update(this.state.selectionMap, {
|
||||
[slice.id]: {
|
||||
$set: !this.state.selectionMap[slice.id]
|
||||
}
|
||||
})
|
||||
$set: !this.state.selectionMap[slice.id],
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
toggleAllSlices(value) {
|
||||
let updatePayload = {};
|
||||
const updatePayload = {};
|
||||
|
||||
this.state.slices.forEach(function (slice) {
|
||||
updatePayload[slice.id] = {
|
||||
$set: value
|
||||
$set: value,
|
||||
};
|
||||
}, this);
|
||||
|
||||
this.setState({
|
||||
selectionMap: update(this.state.selectionMap, updatePayload)
|
||||
selectionMap: update(this.state.selectionMap, updatePayload),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -134,13 +134,15 @@ class SliceAdder extends React.Component {
|
|||
}, this);
|
||||
const modalContent = (
|
||||
<div>
|
||||
<img src="/static/assets/images/loading.gif"
|
||||
className={"loading " + (hideLoad ? "hidden" : "")}
|
||||
alt={hideLoad ? "" : "loading"}/>
|
||||
<div className={this.errored ? "" : "hidden"}>
|
||||
<img
|
||||
src="/static/assets/images/loading.gif"
|
||||
className={'loading ' + (hideLoad ? 'hidden' : '')}
|
||||
alt={hideLoad ? '' : 'loading'}
|
||||
/>
|
||||
<div className={this.errored ? '' : 'hidden'}>
|
||||
{this.state.errorMsg}
|
||||
</div>
|
||||
<div className={this.slicesLoaded ? "" : "hidden"}>
|
||||
<div className={this.slicesLoaded ? '' : 'hidden'}>
|
||||
<BootstrapTable
|
||||
ref="table"
|
||||
data={this.state.slices}
|
||||
|
@ -149,37 +151,52 @@ class SliceAdder extends React.Component {
|
|||
hover
|
||||
search
|
||||
pagination
|
||||
height="auto">
|
||||
<TableHeaderColumn dataField="sliceName" isKey={true} dataSort={true}>Name</TableHeaderColumn>
|
||||
<TableHeaderColumn dataField="vizType" dataSort={true}>Viz</TableHeaderColumn>
|
||||
height="auto"
|
||||
>
|
||||
<TableHeaderColumn
|
||||
dataField="sliceName"
|
||||
isKey
|
||||
dataSort
|
||||
>
|
||||
Name
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn
|
||||
dataField="vizType"
|
||||
dataSort
|
||||
>
|
||||
Viz
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn
|
||||
dataField="modified"
|
||||
dataSort={true}
|
||||
dataSort
|
||||
sortFunc={this.modifiedDateComparator}
|
||||
// Will cause react-bootstrap-table to interpret the HTML returned
|
||||
dataFormat={modified => modified}>
|
||||
dataFormat={modified => modified}
|
||||
>
|
||||
Modified
|
||||
</TableHeaderColumn>
|
||||
</BootstrapTable>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const customButtons = [
|
||||
<button key={0}
|
||||
type="button"
|
||||
className="btn btn-default"
|
||||
data-dismiss="modal"
|
||||
onClick={this.addSlices}
|
||||
disabled={!enableAddSlice}>
|
||||
Add Slices
|
||||
const customButton = (
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-default"
|
||||
data-dismiss="modal"
|
||||
onClick={this.addSlices}
|
||||
disabled={!enableAddSlice}
|
||||
>
|
||||
Add Slices
|
||||
</button>
|
||||
];
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal modalId='add_slice_modal'
|
||||
modalContent={modalContent}
|
||||
title='Add New Slices'
|
||||
customButtons={customButtons}
|
||||
<Modal
|
||||
modalId="add_slice_modal"
|
||||
modalContent={modalContent}
|
||||
title="Add New Slices"
|
||||
customButton={customButton}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
|
||||
const propTypes = {
|
||||
slice: PropTypes.object.isRequired,
|
||||
removeSlice: PropTypes.func.isRequired,
|
||||
expandedSlices: PropTypes.object,
|
||||
};
|
||||
|
||||
function SliceCell({ expandedSlices, removeSlice, slice }) {
|
||||
return (
|
||||
<div>
|
||||
<div className="chart-header">
|
||||
<div className="row">
|
||||
<div className="col-md-12 text-center header">
|
||||
{slice.slice_name}
|
||||
</div>
|
||||
<div className="col-md-12 chart-controls">
|
||||
<div className="pull-left">
|
||||
<a title="Move chart" data-toggle="tooltip">
|
||||
<i className="fa fa-arrows drag" />
|
||||
</a>
|
||||
<a className="refresh" title="Force refresh data" data-toggle="tooltip">
|
||||
<i className="fa fa-repeat" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="pull-right">
|
||||
{slice.description &&
|
||||
<a title="Toggle chart description">
|
||||
<i
|
||||
className="fa fa-info-circle slice_info"
|
||||
title={slice.description}
|
||||
data-toggle="tooltip"
|
||||
/>
|
||||
</a>
|
||||
}
|
||||
<a
|
||||
href={slice.edit_url}
|
||||
title="Edit chart"
|
||||
data-toggle="tooltip"
|
||||
>
|
||||
<i className="fa fa-pencil" />
|
||||
</a>
|
||||
<a href={slice.slice_url} title="Explore chart" data-toggle="tooltip">
|
||||
<i className="fa fa-share" />
|
||||
</a>
|
||||
<a
|
||||
className="remove-chart"
|
||||
title="Remove chart from dashboard"
|
||||
data-toggle="tooltip"
|
||||
>
|
||||
<i
|
||||
className="fa fa-close"
|
||||
onClick={() => { removeSlice(slice.slice_id); }}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="slice_description bs-callout bs-callout-default"
|
||||
style={
|
||||
expandedSlices &&
|
||||
expandedSlices[String(slice.slice_id)] ? {} : { display: 'none' }
|
||||
}
|
||||
dangerouslySetInnerHTML={{ __html: slice.description_markeddown }}
|
||||
>
|
||||
|
||||
</div>
|
||||
<div className="row chart-container">
|
||||
<input type="hidden" value="false" />
|
||||
<div id={slice.token} className="token col-md-12">
|
||||
<img
|
||||
src="/static/assets/images/loading.gif"
|
||||
className="loading"
|
||||
alt="loading"
|
||||
/>
|
||||
<div className="slice_container" id={slice.token + '_con'}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
SliceCell.propTypes = propTypes;
|
||||
|
||||
export default SliceCell;
|
|
@ -16,10 +16,11 @@ export default function QueryAndSaveBtns({ canAdd, onQuery }) {
|
|||
<button type="button" className="btn btn-primary" onClick={onQuery}>
|
||||
<i className="fa fa-bolt"></i>Query
|
||||
</button>
|
||||
<button type="button"
|
||||
className={saveClasses}
|
||||
data-target="#save_modal"
|
||||
data-toggle="modal"
|
||||
<button
|
||||
type="button"
|
||||
className={saveClasses}
|
||||
data-target="#save_modal"
|
||||
data-toggle="modal"
|
||||
>
|
||||
<i className="fa fa-plus-circle"></i>Save as
|
||||
</button>
|
||||
|
|
|
@ -28,31 +28,31 @@ require('../../node_modules/bootstrap-toggle/css/bootstrap-toggle.min.css');
|
|||
var slice;
|
||||
|
||||
var getPanelClass = function (fieldPrefix) {
|
||||
return (fieldPrefix === "flt" ? "filter" : "having") + "_panel";
|
||||
return (fieldPrefix === 'flt' ? 'filter' : 'having') + '_panel';
|
||||
};
|
||||
|
||||
function prepForm() {
|
||||
// Assigning the right id to form elements in filters
|
||||
var fixId = function ($filter, fieldPrefix, i) {
|
||||
$filter.attr("id", function () {
|
||||
return fieldPrefix + "_" + i;
|
||||
$filter.attr('id', function () {
|
||||
return fieldPrefix + '_' + i;
|
||||
});
|
||||
|
||||
["col", "op", "eq"].forEach(function (fieldMiddle) {
|
||||
var fieldName = fieldPrefix + "_" + fieldMiddle;
|
||||
$filter.find("[id^='" + fieldName + "_']")
|
||||
.attr("id", function () {
|
||||
return fieldName + "_" + i;
|
||||
['col', 'op', 'eq'].forEach(function (fieldMiddle) {
|
||||
var fieldName = fieldPrefix + '_' + fieldMiddle;
|
||||
$filter.find('[id^=' + fieldName + '_]')
|
||||
.attr('id', function () {
|
||||
return fieldName + '_' + i;
|
||||
})
|
||||
.attr("name", function () {
|
||||
return fieldName + "_" + i;
|
||||
.attr('name', function () {
|
||||
return fieldName + '_' + i;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
["flt", "having"].forEach(function (fieldPrefix) {
|
||||
['flt', 'having'].forEach(function (fieldPrefix) {
|
||||
var i = 1;
|
||||
$("#" + getPanelClass(fieldPrefix) + " #filters > div").each(function () {
|
||||
$('#' + getPanelClass(fieldPrefix) + ' #filters > div').each(function () {
|
||||
fixId($(this), fieldPrefix, i);
|
||||
i++;
|
||||
});
|
||||
|
@ -79,11 +79,10 @@ function query(force, pushState) {
|
|||
}
|
||||
|
||||
function initExploreView() {
|
||||
|
||||
function get_collapsed_fieldsets() {
|
||||
var collapsed_fieldsets = $("#collapsed_fieldsets").val();
|
||||
var collapsed_fieldsets = $('#collapsed_fieldsets').val();
|
||||
|
||||
if (collapsed_fieldsets !== undefined && collapsed_fieldsets !== "") {
|
||||
if (collapsed_fieldsets !== undefined && collapsed_fieldsets !== '') {
|
||||
collapsed_fieldsets = collapsed_fieldsets.split('||');
|
||||
} else {
|
||||
collapsed_fieldsets = [];
|
||||
|
@ -93,18 +92,18 @@ function initExploreView() {
|
|||
|
||||
function toggle_fieldset(legend, animation) {
|
||||
var parent = legend.parent();
|
||||
var fieldset = parent.find(".legend_label").text();
|
||||
var fieldset = parent.find('.legend_label').text();
|
||||
var collapsed_fieldsets = get_collapsed_fieldsets();
|
||||
var index;
|
||||
|
||||
if (parent.hasClass("collapsed")) {
|
||||
if (parent.hasClass('collapsed')) {
|
||||
if (animation) {
|
||||
parent.find(".panel-body").slideDown();
|
||||
parent.find('.panel-body').slideDown();
|
||||
} else {
|
||||
parent.find(".panel-body").show();
|
||||
parent.find('.panel-body').show();
|
||||
}
|
||||
parent.removeClass("collapsed");
|
||||
parent.find("span.collapser").text("[-]");
|
||||
parent.removeClass('collapsed');
|
||||
parent.find('span.collapser').text('[-]');
|
||||
|
||||
// removing from array, js is overcomplicated
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
|
@ -113,20 +112,20 @@ function initExploreView() {
|
|||
}
|
||||
} else { // not collapsed
|
||||
if (animation) {
|
||||
parent.find(".panel-body").slideUp();
|
||||
parent.find('.panel-body').slideUp();
|
||||
} else {
|
||||
parent.find(".panel-body").hide();
|
||||
parent.find('.panel-body').hide();
|
||||
}
|
||||
|
||||
parent.addClass("collapsed");
|
||||
parent.find("span.collapser").text("[+]");
|
||||
parent.addClass('collapsed');
|
||||
parent.find('span.collapser').text('[+]');
|
||||
index = collapsed_fieldsets.indexOf(fieldset);
|
||||
if (index === -1 && fieldset !== "" && fieldset !== undefined) {
|
||||
if (index === -1 && fieldset !== '' && fieldset !== undefined) {
|
||||
collapsed_fieldsets.push(fieldset);
|
||||
}
|
||||
}
|
||||
|
||||
$("#collapsed_fieldsets").val(collapsed_fieldsets.join("||"));
|
||||
$('#collapsed_fieldsets').val(collapsed_fieldsets.join('||'));
|
||||
}
|
||||
|
||||
px.initFavStars();
|
||||
|
@ -137,7 +136,7 @@ function initExploreView() {
|
|||
});
|
||||
|
||||
function copyURLToClipboard(url) {
|
||||
var textArea = document.createElement("textarea");
|
||||
var textArea = document.createElement('textarea');
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-1000px';
|
||||
textArea.value = url;
|
||||
|
@ -148,10 +147,10 @@ function initExploreView() {
|
|||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if (!successful) {
|
||||
throw new Error("Not successful");
|
||||
throw new Error('Not successful');
|
||||
}
|
||||
} catch (err) {
|
||||
window.alert("Sorry, your browser does not support copying. Use Ctrl / Cmd + C!");
|
||||
window.alert('Sorry, your browser does not support copying. Use Ctrl / Cmd + C!');
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return successful;
|
||||
|
@ -159,12 +158,12 @@ function initExploreView() {
|
|||
|
||||
$('#shortner').click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
type: 'POST',
|
||||
url: '/r/shortner/',
|
||||
data: {
|
||||
data: '/' + window.location.pathname + slice.querystring()
|
||||
data: '/' + window.location.pathname + slice.querystring(),
|
||||
},
|
||||
success: function (data) {
|
||||
success(data) {
|
||||
var close = '<a style="cursor: pointer;"><i class="fa fa-close" id="close_shortner"></i></a>';
|
||||
var copy = '<a style="cursor: pointer;"><i class="fa fa-clipboard" title="Copy to clipboard" id="copy_url"></i></a>';
|
||||
var spaces = ' ';
|
||||
|
@ -175,14 +174,14 @@ function initExploreView() {
|
|||
content: popover,
|
||||
placement: 'left',
|
||||
html: true,
|
||||
trigger: 'manual'
|
||||
trigger: 'manual',
|
||||
})
|
||||
.popover('show');
|
||||
|
||||
$('#copy_url').tooltip().click(function () {
|
||||
var success = copyURLToClipboard(data);
|
||||
if (success) {
|
||||
$(this).attr("data-original-title", "Copied!").tooltip('fixTitle').tooltip('show');
|
||||
$(this).attr('data-original-title', 'Copied!').tooltip('fixTitle').tooltip('show');
|
||||
window.setTimeout(destroyPopover, 1200);
|
||||
}
|
||||
});
|
||||
|
@ -192,13 +191,13 @@ function initExploreView() {
|
|||
$shortner.popover('destroy');
|
||||
}
|
||||
},
|
||||
error: function (error) {
|
||||
error(error) {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, an error occurred during this operation:<br/>" + error
|
||||
title: 'Error',
|
||||
body: 'Sorry, an error occurred during this operation:<br/>' + error,
|
||||
});
|
||||
console.warn("Short URL error", error);
|
||||
}
|
||||
console.warn('Short URL error', error);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -219,13 +218,13 @@ function initExploreView() {
|
|||
title: 'embed html',
|
||||
placement: 'left',
|
||||
html: true,
|
||||
trigger: 'manual'
|
||||
trigger: 'manual',
|
||||
})
|
||||
.popover('show');
|
||||
$('#copy_embed').tooltip().click(function () {
|
||||
var success = copyURLToClipboard(dataToCopy);
|
||||
if (success) {
|
||||
$(this).attr("data-original-title", "Copied!").tooltip('fixTitle').tooltip('show');
|
||||
$(this).attr('data-original-title', 'Copied!').tooltip('fixTitle').tooltip('show');
|
||||
window.setTimeout(destroyPopover, 1200);
|
||||
}
|
||||
});
|
||||
|
@ -250,17 +249,17 @@ function initExploreView() {
|
|||
function generateEmbedHTML() {
|
||||
var width = $standalone_width.val();
|
||||
var height = $standalone_height.val();
|
||||
dataToCopy = '<iframe src="' + src_link + '" width="' + width + '" height="' + height +'"';
|
||||
dataToCopy = '<iframe src="' + src_link + '" width="' + width + '" height="' + height + '"';
|
||||
dataToCopy = dataToCopy + ' seamless frameBorder="0" scrolling="no"></iframe>';
|
||||
$standalone_text.val(dataToCopy);
|
||||
}
|
||||
});
|
||||
|
||||
$("#viz_type").change(function () {
|
||||
$("#query").submit();
|
||||
$('#viz_type').change(function () {
|
||||
$('#query').submit();
|
||||
});
|
||||
|
||||
$("#datasource_id").change(function () {
|
||||
$('#datasource_id').change(function () {
|
||||
var url = $(this).find('option:selected').attr('url');
|
||||
window.location = url;
|
||||
});
|
||||
|
@ -278,28 +277,28 @@ function initExploreView() {
|
|||
);
|
||||
}
|
||||
|
||||
$(".select2").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2Sortable").select2({
|
||||
dropdownAutoWidth: true
|
||||
});
|
||||
$(".select2-with-images").select2({
|
||||
$('.select2').select2({
|
||||
dropdownAutoWidth: true,
|
||||
dropdownCssClass: "bigdrop",
|
||||
formatResult: formatViz
|
||||
});
|
||||
$(".select2Sortable").select2Sortable({
|
||||
bindOrder: 'sortableStop'
|
||||
$('.select2Sortable').select2({
|
||||
dropdownAutoWidth: true,
|
||||
});
|
||||
$("form").show();
|
||||
$('.select2-with-images').select2({
|
||||
dropdownAutoWidth: true,
|
||||
dropdownCssClass: 'bigdrop',
|
||||
formatResult: formatViz,
|
||||
});
|
||||
$('.select2Sortable').select2Sortable({
|
||||
bindOrder: 'sortableStop',
|
||||
});
|
||||
$('form').show();
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
$(".ui-helper-hidden-accessible").remove(); // jQuery-ui 1.11+ creates a div for every tooltip
|
||||
$('.ui-helper-hidden-accessible').remove(); // jQuery-ui 1.11+ creates a div for every tooltip
|
||||
|
||||
function set_filters() {
|
||||
["flt", "having"].forEach(function (prefix) {
|
||||
['flt', 'having'].forEach(function (prefix) {
|
||||
for (var i = 1; i < 10; i++) {
|
||||
var col = px.getParam(prefix + "_col_" + i);
|
||||
var col = px.getParam(prefix + '_col_' + i);
|
||||
if (col !== '') {
|
||||
add_filter(i, prefix);
|
||||
}
|
||||
|
@ -309,13 +308,13 @@ function initExploreView() {
|
|||
set_filters();
|
||||
|
||||
function add_filter(i, fieldPrefix) {
|
||||
var cp = $("#"+fieldPrefix+"0").clone();
|
||||
$(cp).appendTo("#" + getPanelClass(fieldPrefix) + " #filters");
|
||||
var cp = $('#' + fieldPrefix + '0').clone();
|
||||
$(cp).appendTo('#' + getPanelClass(fieldPrefix) + ' #filters');
|
||||
$(cp).show();
|
||||
if (i !== undefined) {
|
||||
$(cp).find("#"+fieldPrefix+"_eq_0").val(px.getParam(fieldPrefix+"_eq_" + i));
|
||||
$(cp).find("#"+fieldPrefix+"_op_0").val(px.getParam(fieldPrefix+"_op_" + i));
|
||||
$(cp).find("#"+fieldPrefix+"_col_0").val(px.getParam(fieldPrefix+"_col_" + i));
|
||||
$(cp).find('#' + fieldPrefix + '_eq_0').val(px.getParam(fieldPrefix + '_eq_' + i));
|
||||
$(cp).find('#' + fieldPrefix + '_op_0').val(px.getParam(fieldPrefix + '_op_' + i));
|
||||
$(cp).find('#' + fieldPrefix + '_col_0').val(px.getParam(fieldPrefix + '_col_' + i));
|
||||
}
|
||||
$(cp).find('select').select2();
|
||||
$(cp).find('.remove').click(function () {
|
||||
|
@ -323,7 +322,7 @@ function initExploreView() {
|
|||
});
|
||||
}
|
||||
|
||||
$(window).bind("popstate", function (event) {
|
||||
$(window).bind('popstate', function (event) {
|
||||
// Browser back button
|
||||
var returnLocation = history.location || document.location;
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
|
@ -331,11 +330,11 @@ function initExploreView() {
|
|||
returnLocation.reload();
|
||||
});
|
||||
|
||||
$("#filter_panel #plus").click(function () {
|
||||
add_filter(undefined, "flt");
|
||||
$('#filter_panel #plus').click(function () {
|
||||
add_filter(undefined, 'flt');
|
||||
});
|
||||
$("#having_panel #plus").click(function () {
|
||||
add_filter(undefined, "having");
|
||||
$('#having_panel #plus').click(function () {
|
||||
add_filter(undefined, 'having');
|
||||
});
|
||||
|
||||
const queryAndSaveBtnsEl = document.getElementById('js-query-and-save-btns');
|
||||
|
@ -354,7 +353,7 @@ function initExploreView() {
|
|||
if (filtered.length === 0) {
|
||||
return {
|
||||
id: term,
|
||||
text: term
|
||||
text: term,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -362,11 +361,11 @@ function initExploreView() {
|
|||
function initSelectionToValue(element, callback) {
|
||||
callback({
|
||||
id: element.val(),
|
||||
text: element.val()
|
||||
text: element.val(),
|
||||
});
|
||||
}
|
||||
|
||||
$(".select2_freeform").each(function () {
|
||||
$('.select2_freeform').each(function () {
|
||||
var parent = $(this).parent();
|
||||
var name = $(this).attr('name');
|
||||
var l = [];
|
||||
|
@ -374,7 +373,7 @@ function initExploreView() {
|
|||
for (var i = 0; i < this.options.length; i++) {
|
||||
l.push({
|
||||
id: this.options[i].value,
|
||||
text: this.options[i].text
|
||||
text: this.options[i].text,
|
||||
});
|
||||
if (this.options[i].selected) {
|
||||
selected = this.options[i].value;
|
||||
|
@ -388,14 +387,14 @@ function initExploreView() {
|
|||
initSelection: initSelectionToValue,
|
||||
dropdownAutoWidth: true,
|
||||
multiple: false,
|
||||
data: l
|
||||
data: l,
|
||||
});
|
||||
$(this).remove();
|
||||
});
|
||||
|
||||
function prepSaveDialog() {
|
||||
var setButtonsState = function () {
|
||||
var add_to_dash = $("input[name=add_to_dash]:checked").val();
|
||||
var add_to_dash = $('input[name=add_to_dash]:checked').val();
|
||||
if (add_to_dash === 'existing' || add_to_dash === 'new') {
|
||||
$('.gotodash').removeAttr('disabled');
|
||||
} else {
|
||||
|
@ -406,25 +405,25 @@ function initExploreView() {
|
|||
url += '?_flt_0_owners=' + $('#userid').val();
|
||||
$.get(url, function (data) {
|
||||
var choices = [];
|
||||
for (var i=0; i< data.pks.length; i++) {
|
||||
for (var i = 0; i < data.pks.length; i++) {
|
||||
choices.push({ id: data.pks[i], text: data.result[i].dashboard_title });
|
||||
}
|
||||
$('#save_to_dashboard_id').select2({
|
||||
data: choices,
|
||||
dropdownAutoWidth: true
|
||||
}).on("select2-selecting", function () {
|
||||
$("#add_to_dash_existing").prop("checked", true);
|
||||
dropdownAutoWidth: true,
|
||||
}).on('select2-selecting', function () {
|
||||
$('#add_to_dash_existing').prop('checked', true);
|
||||
setButtonsState();
|
||||
});
|
||||
});
|
||||
|
||||
$("input[name=add_to_dash]").change(setButtonsState);
|
||||
$('input[name=add_to_dash]').change(setButtonsState);
|
||||
$("input[name='new_dashboard_name']").on('focus', function () {
|
||||
$("#add_to_new_dash").prop("checked", true);
|
||||
$('#add_to_new_dash').prop('checked', true);
|
||||
setButtonsState();
|
||||
});
|
||||
$("input[name='new_slice_name']").on('focus', function () {
|
||||
$("#save_as_new").prop("checked", true);
|
||||
$('#save_as_new').prop('checked', true);
|
||||
setButtonsState();
|
||||
});
|
||||
|
||||
|
@ -444,30 +443,30 @@ function saveSlice() {
|
|||
var slice_name = $('input[name=new_slice_name]').val();
|
||||
if (slice_name === '') {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "You must pick a name for the new slice"
|
||||
title: 'Error',
|
||||
body: 'You must pick a name for the new slice',
|
||||
});
|
||||
return;
|
||||
}
|
||||
document.getElementById("slice_name").value = slice_name;
|
||||
document.getElementById('slice_name').value = slice_name;
|
||||
}
|
||||
var add_to_dash = $('input[name=add_to_dash]:checked').val();
|
||||
if (add_to_dash === 'existing' && $('#save_to_dashboard_id').val() === '') {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "You must pick an existing dashboard"
|
||||
title: 'Error',
|
||||
body: 'You must pick an existing dashboard',
|
||||
});
|
||||
return;
|
||||
} else if (add_to_dash === 'new' && $('input[name=new_dashboard_name]').val() === '') {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Please enter a name for the new dashboard"
|
||||
title: 'Error',
|
||||
body: 'Please enter a name for the new dashboard',
|
||||
});
|
||||
return;
|
||||
}
|
||||
$('#action').val(action);
|
||||
prepForm();
|
||||
$("#query").submit();
|
||||
$('#query').submit();
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
|
@ -489,9 +488,9 @@ $(document).ready(function () {
|
|||
// make checkbox inputs display as toggles
|
||||
$(':checkbox')
|
||||
.addClass('pull-right')
|
||||
.attr("data-onstyle", "default")
|
||||
.attr('data-onstyle', 'default')
|
||||
.bootstrapToggle({
|
||||
size: 'mini'
|
||||
size: 'mini',
|
||||
});
|
||||
|
||||
$('div.toggle').addClass('pull-right');
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
import React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import { Jumbotron } from 'react-bootstrap';
|
||||
|
||||
class App extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<Jumbotron>
|
||||
<h1>Caravel</h1>
|
||||
<p>Extensible visualization tool for exploring data from any database.</p>
|
||||
</Jumbotron>
|
||||
);
|
||||
}
|
||||
function App() {
|
||||
return (
|
||||
<Jumbotron>
|
||||
<h1>Caravel</h1>
|
||||
<p>Extensible visualization tool for exploring data from any database.</p>
|
||||
</Jumbotron>
|
||||
);
|
||||
}
|
||||
|
||||
render(<App />, document.getElementById('app'));
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
var $ = require('jquery');
|
||||
var jQuery = $;
|
||||
var d3 = require('d3');
|
||||
var Mustache = require('mustache');
|
||||
var utils = require('./utils');
|
||||
|
||||
import $ from 'jquery';
|
||||
const d3 = require('d3');
|
||||
const Mustache = require('mustache');
|
||||
const utils = require('./utils');
|
||||
// vis sources
|
||||
var sourceMap = {
|
||||
/* eslint camel-case: 0 */
|
||||
const sourceMap = {
|
||||
area: 'nvd3_vis.js',
|
||||
bar: 'nvd3_vis.js',
|
||||
bubble: 'nvd3_vis.js',
|
||||
|
@ -34,185 +33,247 @@ var sourceMap = {
|
|||
horizon: 'horizon.js',
|
||||
mapbox: 'mapbox.jsx',
|
||||
};
|
||||
|
||||
var color = function () {
|
||||
const color = function () {
|
||||
// Color related utility functions go in this object
|
||||
var bnbColors = [
|
||||
//rausch hackb kazan babu lima beach barol
|
||||
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c',
|
||||
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a',
|
||||
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e',
|
||||
const bnbColors = [
|
||||
'#ff5a5f', // rausch
|
||||
'#7b0051', // hackb
|
||||
'#007A87', // kazan
|
||||
'#00d1c1', // babu
|
||||
'#8ce071', // lima
|
||||
'#ffb400', // beach
|
||||
'#b4a76c', // barol
|
||||
'#ff8083',
|
||||
'#cc0086',
|
||||
'#00a1b3',
|
||||
'#00ffeb',
|
||||
'#bbedab',
|
||||
'#ffd266',
|
||||
'#cbc29a',
|
||||
'#ff3339',
|
||||
'#ff1ab1',
|
||||
'#005c66',
|
||||
'#00b3a5',
|
||||
'#55d12e',
|
||||
'#b37e00',
|
||||
'#988b4e',
|
||||
];
|
||||
var spectrums = {
|
||||
blue_white_yellow: ['#00d1c1', 'white', '#ffb400'],
|
||||
fire: ['white', 'yellow', 'red', 'black'],
|
||||
white_black: ['white', 'black'],
|
||||
black_white: ['black', 'white'],
|
||||
const spectrums = {
|
||||
blue_white_yellow: [
|
||||
'#00d1c1',
|
||||
'white',
|
||||
'#ffb400',
|
||||
],
|
||||
fire: [
|
||||
'white',
|
||||
'yellow',
|
||||
'red',
|
||||
'black',
|
||||
],
|
||||
white_black: [
|
||||
'white',
|
||||
'black',
|
||||
],
|
||||
black_white: [
|
||||
'black',
|
||||
'white',
|
||||
],
|
||||
};
|
||||
var colorBnb = function () {
|
||||
const colorBnb = function () {
|
||||
// Color factory
|
||||
var seen = {};
|
||||
const seen = {};
|
||||
return function (s) {
|
||||
if (!s) { return; }
|
||||
s = String(s);
|
||||
// next line is for caravel series that should have the same color
|
||||
s = s.replace('---', '');
|
||||
if (seen[s] === undefined) {
|
||||
seen[s] = Object.keys(seen).length;
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
return this.bnbColors[seen[s] % this.bnbColors.length];
|
||||
let stringifyS = String(s);
|
||||
// next line is for caravel series that should have the same color
|
||||
stringifyS = stringifyS.replace('---', '');
|
||||
if (seen[stringifyS] === undefined) {
|
||||
seen[stringifyS] = Object.keys(seen).length;
|
||||
}
|
||||
/* eslint consistent-return: 0 */
|
||||
return this.bnbColors[seen[stringifyS] % this.bnbColors.length];
|
||||
};
|
||||
};
|
||||
var colorScalerFactory = function (colors, data, accessor) {
|
||||
const colorScalerFactory = function (colors, data, accessor) {
|
||||
// Returns a linear scaler our of an array of color
|
||||
if (!Array.isArray(colors)) {
|
||||
/* eslint no-param-reassign: 0 */
|
||||
colors = spectrums[colors];
|
||||
}
|
||||
|
||||
var ext = [0, 1];
|
||||
let ext = [
|
||||
0,
|
||||
1,
|
||||
];
|
||||
if (data !== undefined) {
|
||||
ext = d3.extent(data, accessor);
|
||||
}
|
||||
|
||||
var points = [];
|
||||
var chunkSize = (ext[1] - ext[0]) / colors.length;
|
||||
$.each(colors, function (i, c) {
|
||||
const points = [];
|
||||
const chunkSize = (ext[1] - ext[0]) / colors.length;
|
||||
$.each(colors, function (i) {
|
||||
points.push(i * chunkSize);
|
||||
});
|
||||
return d3.scale.linear().domain(points).range(colors);
|
||||
};
|
||||
return {
|
||||
bnbColors: bnbColors,
|
||||
bnbColors,
|
||||
category21: colorBnb(),
|
||||
colorScalerFactory: colorScalerFactory,
|
||||
colorScalerFactory,
|
||||
};
|
||||
};
|
||||
|
||||
var px = (function () {
|
||||
|
||||
var visualizations = {};
|
||||
var slice;
|
||||
|
||||
/* eslint wrap-iife: 0*/
|
||||
const px = function () {
|
||||
const visualizations = {};
|
||||
let slice;
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
||||
const results = regex.exec(location.search);
|
||||
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function UTC(dttm) {
|
||||
return new Date(dttm.getUTCFullYear(), dttm.getUTCMonth(), dttm.getUTCDate(), dttm.getUTCHours(), dttm.getUTCMinutes(), dttm.getUTCSeconds());
|
||||
return new Date(
|
||||
dttm.getUTCFullYear(),
|
||||
dttm.getUTCMonth(),
|
||||
dttm.getUTCDate(),
|
||||
dttm.getUTCHours(),
|
||||
dttm.getUTCMinutes(),
|
||||
dttm.getUTCSeconds()
|
||||
);
|
||||
}
|
||||
var tickMultiFormat = d3.time.format.multi([
|
||||
[".%L", function (d) {
|
||||
return d.getMilliseconds();
|
||||
}], // If there are millisections, show only them
|
||||
[":%S", function (d) {
|
||||
return d.getSeconds();
|
||||
}], // If there are seconds, show only them
|
||||
["%a %b %d, %I:%M %p", function (d) {
|
||||
return d.getMinutes() !== 0;
|
||||
}], // If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
|
||||
["%a %b %d, %I %p", function (d) {
|
||||
return d.getHours() !== 0;
|
||||
}], // If there are hours that are multiples of 3, show date and AM/PM
|
||||
["%a %b %d", function (d) {
|
||||
return d.getDate() !== 1;
|
||||
}], // If not the first of the month, do "month day, year."
|
||||
["%B %Y", function (d) {
|
||||
return d.getMonth() !== 0 && d.getDate() === 1;
|
||||
}], // If the first of the month, do "month day, year."
|
||||
["%Y", function (d) {
|
||||
return true;
|
||||
}], // fall back on month, year
|
||||
const tickMultiFormat = d3.time.format.multi([
|
||||
[
|
||||
'.%L',
|
||||
function (d) {
|
||||
return d.getMilliseconds();
|
||||
},
|
||||
],
|
||||
// If there are millisections, show only them
|
||||
[
|
||||
':%S',
|
||||
function (d) {
|
||||
return d.getSeconds();
|
||||
},
|
||||
],
|
||||
// If there are seconds, show only them
|
||||
[
|
||||
'%a %b %d, %I:%M %p',
|
||||
function (d) {
|
||||
return d.getMinutes() !== 0;
|
||||
},
|
||||
],
|
||||
// If there are non-zero minutes, show Date, Hour:Minute [AM/PM]
|
||||
[
|
||||
'%a %b %d, %I %p',
|
||||
function (d) {
|
||||
return d.getHours() !== 0;
|
||||
},
|
||||
],
|
||||
// If there are hours that are multiples of 3, show date and AM/PM
|
||||
[
|
||||
'%a %b %d',
|
||||
function (d) {
|
||||
return d.getDate() !== 1;
|
||||
},
|
||||
],
|
||||
// If not the first of the month, do "month day, year."
|
||||
[
|
||||
'%B %Y',
|
||||
function (d) {
|
||||
return d.getMonth() !== 0 && d.getDate() === 1;
|
||||
},
|
||||
],
|
||||
// If the first of the month, do "month day, year."
|
||||
[
|
||||
'%Y',
|
||||
function () {
|
||||
return true;
|
||||
},
|
||||
], // fall back on month, year
|
||||
]);
|
||||
|
||||
function formatDate(dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
//d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
|
||||
const d = UTC(new Date(dttm));
|
||||
// d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
|
||||
return tickMultiFormat(d);
|
||||
}
|
||||
|
||||
function timeFormatFactory(d3timeFormat) {
|
||||
var f = d3.time.format(d3timeFormat);
|
||||
const f = d3.time.format(d3timeFormat);
|
||||
return function (dttm) {
|
||||
var d = UTC(new Date(dttm));
|
||||
const d = UTC(new Date(dttm));
|
||||
return f(d);
|
||||
};
|
||||
}
|
||||
|
||||
function initFavStars() {
|
||||
var baseUrl = '/caravel/favstar/';
|
||||
// Init star behavihor for favorite
|
||||
function show() {
|
||||
if ($(this).hasClass('selected')) {
|
||||
$(this).html('<i class="fa fa-star"></i>');
|
||||
} else {
|
||||
$(this).html('<i class="fa fa-star-o"></i>');
|
||||
}
|
||||
const baseUrl = '/caravel/favstar/';
|
||||
// Init star behavihor for favorite
|
||||
function show() {
|
||||
if ($(this).hasClass('selected')) {
|
||||
$(this).html('<i class="fa fa-star"></i>');
|
||||
} else {
|
||||
$(this).html('<i class="fa fa-star-o"></i>');
|
||||
}
|
||||
$('.favstar')
|
||||
.attr('title', 'Click to favorite/unfavorite')
|
||||
.each(show)
|
||||
.each(function () {
|
||||
var url = baseUrl + $(this).attr("class_name");
|
||||
var star = this;
|
||||
url += '/' + $(this).attr("obj_id") + '/';
|
||||
$.getJSON(url + 'count/', function (data) {
|
||||
if (data.count > 0) {
|
||||
$(star)
|
||||
.addClass('selected')
|
||||
.each(show);
|
||||
}
|
||||
});
|
||||
})
|
||||
.click(function () {
|
||||
$(this).toggleClass('selected');
|
||||
var url = baseUrl + $(this).attr("class_name");
|
||||
url += '/' + $(this).attr("obj_id") + '/';
|
||||
if ($(this).hasClass('selected')) {
|
||||
url += 'select/';
|
||||
} else {
|
||||
url += 'unselect/';
|
||||
}
|
||||
$('.favstar')
|
||||
.attr('title', 'Click to favorite/unfavorite')
|
||||
.each(show)
|
||||
.each(function () {
|
||||
let url = baseUrl + $(this).attr('class_name');
|
||||
const star = this;
|
||||
url += '/' + $(this).attr('obj_id') + '/';
|
||||
$.getJSON(url + 'count/', function (data) {
|
||||
if (data.count > 0) {
|
||||
$(star).addClass('selected').each(show);
|
||||
}
|
||||
$.get(url);
|
||||
$(this).each(show);
|
||||
})
|
||||
.tooltip();
|
||||
});
|
||||
})
|
||||
.click(function () {
|
||||
$(this).toggleClass('selected');
|
||||
let url = baseUrl + $(this).attr('class_name');
|
||||
url += '/' + $(this).attr('obj_id') + '/';
|
||||
if ($(this).hasClass('selected')) {
|
||||
url += 'select/';
|
||||
} else {
|
||||
url += 'unselect/';
|
||||
}
|
||||
$.get(url);
|
||||
$(this).each(show);
|
||||
})
|
||||
.tooltip();
|
||||
}
|
||||
|
||||
var Slice = function (data, dashboard) {
|
||||
var timer;
|
||||
var token = $('#' + data.token);
|
||||
var container_id = data.token + '_con';
|
||||
var selector = '#' + container_id;
|
||||
var container = $(selector);
|
||||
var slice_id = data.slice_id;
|
||||
var dttm = 0;
|
||||
var stopwatch = function () {
|
||||
const Slice = function (data, dashboard) {
|
||||
let timer;
|
||||
const token = $('#' + data.token);
|
||||
const containerId = data.token + '_con';
|
||||
const selector = '#' + containerId;
|
||||
const container = $(selector);
|
||||
const sliceId = data.sliceId;
|
||||
let dttm = 0;
|
||||
const stopwatch = function () {
|
||||
dttm += 10;
|
||||
var num = dttm / 1000;
|
||||
$('#timer').text(num.toFixed(2) + " sec");
|
||||
const num = dttm / 1000;
|
||||
$('#timer').text(num.toFixed(2) + ' sec');
|
||||
};
|
||||
var qrystr = '';
|
||||
var always = function (data) {
|
||||
//Private f, runs after done and error
|
||||
let qrystr = '';
|
||||
const always = function () {
|
||||
// Private f, runs after done and error
|
||||
clearInterval(timer);
|
||||
$('#timer').removeClass('btn-warning');
|
||||
};
|
||||
slice = {
|
||||
data: data,
|
||||
container: container,
|
||||
container_id: container_id,
|
||||
selector: selector,
|
||||
querystring: function (params) {
|
||||
data,
|
||||
container,
|
||||
containerId,
|
||||
selector,
|
||||
querystring(params) {
|
||||
params = params || {};
|
||||
var parser = document.createElement('a');
|
||||
const parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
if (dashboard !== undefined) {
|
||||
var flts = params.extraFilters === false ?
|
||||
'' : encodeURIComponent(JSON.stringify(dashboard.filters));
|
||||
qrystr = parser.search + "&extra_filters=" + flts;
|
||||
const flts =
|
||||
params.extraFilters === false ? '' :
|
||||
encodeURIComponent(JSON.stringify(dashboard.filters));
|
||||
qrystr = parser.search + '&extra_filters=' + flts;
|
||||
} else if ($('#query').length === 0) {
|
||||
qrystr = parser.search;
|
||||
} else {
|
||||
|
@ -220,69 +281,66 @@ var px = (function () {
|
|||
}
|
||||
return qrystr;
|
||||
},
|
||||
getWidgetHeader: function () {
|
||||
return this.container.parents("div.widget").find(".chart-header");
|
||||
getWidgetHeader() {
|
||||
return this.container.parents('div.widget').find('.chart-header');
|
||||
},
|
||||
render_template: function (s) {
|
||||
var context = {
|
||||
render_template(s) {
|
||||
const context = {
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
};
|
||||
return Mustache.render(s, context);
|
||||
},
|
||||
jsonEndpoint: function (params) {
|
||||
jsonEndpoint(params) {
|
||||
params = params || {};
|
||||
var parser = document.createElement('a');
|
||||
const parser = document.createElement('a');
|
||||
parser.href = data.json_endpoint;
|
||||
var endpoint = parser.pathname + this.querystring({
|
||||
extraFilters: params.extraFilters,
|
||||
});
|
||||
endpoint += "&json=true";
|
||||
endpoint += "&force=" + this.force;
|
||||
let endpoint = parser.pathname + this.querystring({ extraFilters: params.extraFilters });
|
||||
endpoint += '&json=true';
|
||||
endpoint += '&force=' + this.force;
|
||||
return endpoint;
|
||||
},
|
||||
d3format: function (col, number) {
|
||||
d3format(col, number) {
|
||||
// uses the utils memoized d3format function and formats based on
|
||||
// column level defined preferences
|
||||
var format = this.data.column_formats[col];
|
||||
const format = this.data.column_formats[col];
|
||||
return utils.d3format(format, number);
|
||||
},
|
||||
done: function (data) {
|
||||
/* eslint no-shadow: 0 */
|
||||
done(data) {
|
||||
clearInterval(timer);
|
||||
token.find("img.loading").hide();
|
||||
token.find('img.loading').hide();
|
||||
container.show();
|
||||
|
||||
var cachedSelector = null;
|
||||
let cachedSelector = null;
|
||||
if (dashboard === undefined) {
|
||||
cachedSelector = $('#is_cached');
|
||||
if (data !== undefined && data.is_cached) {
|
||||
cachedSelector
|
||||
.attr('title', 'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
|
||||
.attr('title',
|
||||
'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
|
||||
.show()
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
cachedSelector.hide();
|
||||
}
|
||||
} else {
|
||||
var refresh = this.getWidgetHeader().find('.refresh');
|
||||
const refresh = this.getWidgetHeader().find('.refresh');
|
||||
if (data !== undefined && data.is_cached) {
|
||||
refresh
|
||||
.addClass('danger')
|
||||
.attr(
|
||||
'title',
|
||||
'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
.addClass('danger')
|
||||
.attr('title',
|
||||
'Served from data cached at ' + data.cached_dttm +
|
||||
'. Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
} else {
|
||||
refresh
|
||||
.removeClass('danger')
|
||||
.attr(
|
||||
'title',
|
||||
'Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
.removeClass('danger')
|
||||
.attr('title', 'Click to force-refresh')
|
||||
.tooltip('fixTitle');
|
||||
}
|
||||
}
|
||||
if (data !== undefined) {
|
||||
$("#query_container").html(data.query);
|
||||
$('#query_container').html(data.query);
|
||||
}
|
||||
$('#timer').removeClass('btn-warning');
|
||||
$('#timer').addClass('btn-success');
|
||||
|
@ -297,26 +355,26 @@ var px = (function () {
|
|||
$('.query-and-save button').removeAttr('disabled');
|
||||
always(data);
|
||||
},
|
||||
getErrorMsg: function (xhr) {
|
||||
if (xhr.statusText === "timeout") {
|
||||
return "The request timed out";
|
||||
getErrorMsg(xhr) {
|
||||
if (xhr.statusText === 'timeout') {
|
||||
return 'The request timed out';
|
||||
}
|
||||
var msg = "";
|
||||
let msg = '';
|
||||
if (!xhr.responseText) {
|
||||
var status = xhr.status;
|
||||
msg += "An unknown error occurred. (Status: " + status + ")";
|
||||
const status = xhr.status;
|
||||
msg += 'An unknown error occurred. (Status: ' + status + ')';
|
||||
if (status === 0) {
|
||||
// This may happen when the worker in gunicorn times out
|
||||
msg += " Maybe the request timed out?";
|
||||
msg += ' Maybe the request timed out?';
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
},
|
||||
error: function (msg, xhr) {
|
||||
token.find("img.loading").hide();
|
||||
var err = msg ? ('<div class="alert alert-danger">' + msg + '</div>') : "";
|
||||
error(msg, xhr) {
|
||||
token.find('img.loading').hide();
|
||||
let err = msg ? '<div class="alert alert-danger">' + msg + '</div>' : '';
|
||||
if (xhr) {
|
||||
var extendedMsg = this.getErrorMsg(xhr);
|
||||
const extendedMsg = this.getErrorMsg(xhr);
|
||||
if (extendedMsg) {
|
||||
err += '<div class="alert alert-danger">' + extendedMsg + '</div>';
|
||||
}
|
||||
|
@ -329,35 +387,35 @@ var px = (function () {
|
|||
$('.query-and-save button').removeAttr('disabled');
|
||||
always(data);
|
||||
},
|
||||
width: function () {
|
||||
width() {
|
||||
return token.width();
|
||||
},
|
||||
height: function () {
|
||||
var others = 0;
|
||||
var widget = container.parents('.widget');
|
||||
var slice_description = widget.find('.slice_description');
|
||||
if (slice_description.is(":visible")) {
|
||||
others += widget.find('.slice_description').height() + 25;
|
||||
height() {
|
||||
let others = 0;
|
||||
const widget = container.parents('.widget');
|
||||
const sliceDescription = widget.find('.sliceDescription');
|
||||
if (sliceDescription.is(':visible')) {
|
||||
others += widget.find('.sliceDescription').height() + 25;
|
||||
}
|
||||
others += widget.find('.chart-header').height();
|
||||
return widget.height() - others - 10;
|
||||
},
|
||||
bindResizeToWindowResize: function () {
|
||||
var resizeTimer;
|
||||
var slice = this;
|
||||
$(window).on('resize', function (e) {
|
||||
bindResizeToWindowResize() {
|
||||
let resizeTimer;
|
||||
const slice = this;
|
||||
$(window).on('resize', function () {
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(function () {
|
||||
slice.resize();
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
render: function (force) {
|
||||
render(force) {
|
||||
if (force === undefined) {
|
||||
force = false;
|
||||
}
|
||||
this.force = force;
|
||||
token.find("img.loading").show();
|
||||
token.find('img.loading').show();
|
||||
container.css('height', this.height());
|
||||
dttm = 0;
|
||||
timer = setInterval(stopwatch, 10);
|
||||
|
@ -365,67 +423,65 @@ var px = (function () {
|
|||
$('#timer').addClass('btn-warning');
|
||||
this.viz.render();
|
||||
},
|
||||
resize: function () {
|
||||
token.find("img.loading").show();
|
||||
resize() {
|
||||
token.find('img.loading').show();
|
||||
container.css('height', this.height());
|
||||
this.viz.render();
|
||||
this.viz.resize();
|
||||
},
|
||||
addFilter: function (col, vals) {
|
||||
addFilter(col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.addFilter(slice_id, col, vals);
|
||||
dashboard.addFilter(sliceId, col, vals);
|
||||
}
|
||||
},
|
||||
setFilter: function (col, vals) {
|
||||
setFilter(col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.setFilter(slice_id, col, vals);
|
||||
dashboard.setFilter(sliceId, col, vals);
|
||||
}
|
||||
},
|
||||
getFilters: function (col, vals) {
|
||||
getFilters() {
|
||||
if (dashboard !== undefined) {
|
||||
return dashboard.filters[slice_id];
|
||||
return dashboard.filters[sliceId];
|
||||
}
|
||||
return false;
|
||||
},
|
||||
clearFilter() {
|
||||
if (dashboard !== undefined) {
|
||||
dashboard.clearFilter(sliceId);
|
||||
}
|
||||
},
|
||||
clearFilter: function () {
|
||||
removeFilter(col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.clearFilter(slice_id);
|
||||
}
|
||||
},
|
||||
removeFilter: function (col, vals) {
|
||||
if (dashboard !== undefined) {
|
||||
delete dashboard.removeFilter(slice_id, col, vals);
|
||||
dashboard.removeFilter(sliceId, col, vals);
|
||||
}
|
||||
},
|
||||
};
|
||||
var visType = data.form_data.viz_type;
|
||||
const visType = data.form_data.viz_type;
|
||||
px.registerViz(visType);
|
||||
slice.viz = visualizations[data.form_data.viz_type](slice);
|
||||
return slice;
|
||||
};
|
||||
|
||||
function registerViz(name) {
|
||||
var visSource = sourceMap[name];
|
||||
|
||||
const visSource = sourceMap[name];
|
||||
if (visSource) {
|
||||
var visFactory = require('../../visualizations/' + visSource);
|
||||
/* eslint global-require: 0 */
|
||||
const visFactory = require('../../visualizations/' + visSource);
|
||||
if (typeof visFactory === 'function') {
|
||||
visualizations[name] = visFactory;
|
||||
}
|
||||
} else {
|
||||
throw new Error("require(" + name + ") failed.");
|
||||
throw new Error('require(' + name + ') failed.');
|
||||
}
|
||||
}
|
||||
|
||||
// Export public functions
|
||||
return {
|
||||
registerViz: registerViz,
|
||||
Slice: Slice,
|
||||
formatDate: formatDate,
|
||||
timeFormatFactory: timeFormatFactory,
|
||||
color: color(),
|
||||
getParam: getParam,
|
||||
initFavStars: initFavStars,
|
||||
formatDate,
|
||||
getParam,
|
||||
initFavStars,
|
||||
registerViz,
|
||||
Slice,
|
||||
timeFormatFactory,
|
||||
};
|
||||
})();
|
||||
|
||||
}();
|
||||
module.exports = px;
|
||||
|
|
|
@ -1,56 +1,51 @@
|
|||
var $ = require('jquery');
|
||||
var d3 = require('d3');
|
||||
|
||||
const $ = require('jquery');
|
||||
const d3 = require('d3');
|
||||
/*
|
||||
Utility function that takes a d3 svg:text selection and a max width, and splits the
|
||||
text's text across multiple tspan lines such that any given line does not exceed max width
|
||||
|
||||
If text does not span multiple lines AND adjustedY is passed, will set the text to the passed val
|
||||
If text does not span multiple lines AND adjustedY is passed,
|
||||
will set the text to the passed val
|
||||
*/
|
||||
function wrapSvgText(text, width, adjustedY) {
|
||||
var lineHeight = 1; // ems
|
||||
|
||||
const lineHeight = 1;
|
||||
// ems
|
||||
text.each(function () {
|
||||
var text = d3.select(this),
|
||||
words = text.text().split(/\s+/),
|
||||
word,
|
||||
line = [],
|
||||
lineNumber = 0,
|
||||
x = text.attr("x"),
|
||||
y = text.attr("y"),
|
||||
dy = parseFloat(text.attr("dy")),
|
||||
tspan = text.text(null)
|
||||
.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", dy + "em");
|
||||
const d3Text = d3.select(this);
|
||||
const words = d3Text.text().split(/\s+/);
|
||||
let word;
|
||||
let line = [];
|
||||
let lineNumber = 0;
|
||||
const x = d3Text.attr('x');
|
||||
const y = d3Text.attr('y');
|
||||
const dy = parseFloat(d3Text.attr('dy'));
|
||||
let tspan =
|
||||
d3Text.text(null).append('tspan').attr('x', x)
|
||||
.attr('y', y)
|
||||
.attr('dy', dy + 'em');
|
||||
|
||||
var didWrap = false;
|
||||
|
||||
for (var i = 0; i < words.length; i++) {
|
||||
let didWrap = false;
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
word = words[i];
|
||||
line.push(word);
|
||||
tspan.text(line.join(" "));
|
||||
|
||||
tspan.text(line.join(' '));
|
||||
if (tspan.node().getComputedTextLength() > width) {
|
||||
line.pop(); // remove word that pushes over the limit
|
||||
tspan.text(line.join(" "));
|
||||
line.pop();
|
||||
// remove word that pushes over the limit
|
||||
tspan.text(line.join(' '));
|
||||
line = [word];
|
||||
tspan = text.append("tspan")
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("dy", ++lineNumber * lineHeight + dy + "em")
|
||||
.text(word);
|
||||
|
||||
tspan =
|
||||
d3Text.append('tspan').attr('x', x).attr('y', y)
|
||||
.attr('dy', ++lineNumber * lineHeight + dy + 'em')
|
||||
.text(word);
|
||||
didWrap = true;
|
||||
}
|
||||
}
|
||||
if (!didWrap && typeof adjustedY !== "undefined") {
|
||||
tspan.attr("y", adjustedY);
|
||||
if (!didWrap && typeof adjustedY !== 'undefined') {
|
||||
tspan.attr('y', adjustedY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body and title content of a modal, and shows it. Assumes HTML for modal exists and that
|
||||
* it handles closing (i.e., works with bootstrap)
|
||||
|
@ -65,47 +60,41 @@ function wrapSvgText(text, width, adjustedY) {
|
|||
* }
|
||||
*/
|
||||
function showModal(options) {
|
||||
options.modalSelector = options.modalSelector || ".misc-modal";
|
||||
options.titleSelector = options.titleSelector || ".misc-modal .modal-title";
|
||||
options.bodySelector = options.bodySelector || ".misc-modal .modal-body";
|
||||
|
||||
$(options.titleSelector).html(options.title || "");
|
||||
$(options.bodySelector).html(options.body || "");
|
||||
$(options.modalSelector).modal("show");
|
||||
/* eslint no-param-reassign: 0 */
|
||||
options.modalSelector = options.modalSelector || '.misc-modal';
|
||||
options.titleSelector = options.titleSelector || '.misc-modal .modal-title';
|
||||
options.bodySelector = options.bodySelector || '.misc-modal .modal-body';
|
||||
$(options.titleSelector).html(options.title || '');
|
||||
$(options.bodySelector).html(options.body || '');
|
||||
$(options.modalSelector).modal('show');
|
||||
}
|
||||
|
||||
var showApiMessage = function (resp) {
|
||||
var template = '<div class="alert"> ' +
|
||||
'<button type="button" class="close" ' +
|
||||
'data-dismiss="alert">×</button> </div>';
|
||||
|
||||
var severity = resp.severity || 'info';
|
||||
$(template)
|
||||
.addClass('alert-' + severity)
|
||||
.append(resp.message)
|
||||
.appendTo($('#alert-container'));
|
||||
const showApiMessage = function (resp) {
|
||||
const template =
|
||||
'<div class="alert"> ' +
|
||||
'<button type="button" class="close" ' +
|
||||
'data-dismiss="alert">\xD7</button> </div>';
|
||||
const severity = resp.severity || 'info';
|
||||
$(template).addClass('alert-' + severity)
|
||||
.append(resp.message)
|
||||
.appendTo($('#alert-container'));
|
||||
};
|
||||
|
||||
var toggleCheckbox = function (apiUrlPrefix, selector) {
|
||||
var apiUrl = apiUrlPrefix + $(selector)[0].checked;
|
||||
$.get(apiUrl).fail(function (xhr, textStatus, errorThrown) {
|
||||
var resp = xhr.responseJSON;
|
||||
const toggleCheckbox = function (apiUrlPrefix, selector) {
|
||||
const apiUrl = apiUrlPrefix + $(selector)[0].checked;
|
||||
$.get(apiUrl).fail(function (xhr) {
|
||||
const resp = xhr.responseJSON;
|
||||
if (resp && resp.message) {
|
||||
showApiMessage(resp);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Fix the height of the table body of a DataTable with scrollY set
|
||||
*/
|
||||
var fixDataTableBodyHeight = function ($tableDom, height) {
|
||||
var headHeight = $tableDom.find('.dataTables_scrollHead').height();
|
||||
$tableDom.find('.dataTables_scrollBody')
|
||||
.css('max-height', height - headHeight);
|
||||
const fixDataTableBodyHeight = function ($tableDom, height) {
|
||||
const headHeight = $tableDom.find('.dataTables_scrollHead').height();
|
||||
$tableDom.find('.dataTables_scrollBody').css('max-height', height - headHeight);
|
||||
};
|
||||
|
||||
var formatters = {};
|
||||
const formatters = {};
|
||||
function d3format(format, number) {
|
||||
// Formats a number and memoizes formatters to be reused
|
||||
format = format || '.3s';
|
||||
|
@ -114,11 +103,10 @@ function d3format(format, number) {
|
|||
}
|
||||
return formatters[format](number);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrapSvgText: wrapSvgText,
|
||||
showModal: showModal,
|
||||
toggleCheckbox: toggleCheckbox,
|
||||
fixDataTableBodyHeight: fixDataTableBodyHeight,
|
||||
d3format: d3format,
|
||||
d3format,
|
||||
fixDataTableBodyHeight,
|
||||
showModal,
|
||||
toggleCheckbox,
|
||||
wrapSvgText,
|
||||
};
|
||||
|
|
|
@ -1,93 +1,80 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var showModal = require('./modules/utils.js').showModal;
|
||||
|
||||
const $ = window.$ = require('jquery');
|
||||
const showModal = require('./modules/utils.js').showModal;
|
||||
require('./caravel-select2.js');
|
||||
|
||||
require('datatables.net-bs');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
require('bootstrap');
|
||||
|
||||
var ace = require('brace');
|
||||
const ace = require('brace');
|
||||
require('brace/mode/sql');
|
||||
require('brace/theme/crimson_editor');
|
||||
|
||||
require('../stylesheets/sql.css');
|
||||
|
||||
$(document).ready(function () {
|
||||
function getParam(name) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
|
||||
results = regex.exec(location.search);
|
||||
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
/* eslint no-param-reassign: 0 */
|
||||
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
|
||||
const results = regex.exec(location.search);
|
||||
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function initSqlEditorView() {
|
||||
var database_id = $('#database_id').val();
|
||||
var editor = ace.edit("sql");
|
||||
const databaseId = $('#databaseId').val();
|
||||
const editor = ace.edit('sql');
|
||||
editor.$blockScrolling = Infinity;
|
||||
editor.getSession().setUseWrapMode(true);
|
||||
|
||||
$('#sql').hide();
|
||||
editor.setTheme("ace/theme/crimson_editor");
|
||||
editor.setTheme('ace/theme/crimson_editor');
|
||||
editor.setOptions({
|
||||
minLines: 16,
|
||||
maxLines: Infinity,
|
||||
});
|
||||
editor.getSession().setMode("ace/mode/sql");
|
||||
editor.getSession().setMode('ace/mode/sql');
|
||||
editor.focus();
|
||||
$("select").select2({
|
||||
dropdownAutoWidth: true,
|
||||
});
|
||||
|
||||
$('select').select2({ dropdownAutoWidth: true });
|
||||
|
||||
function showTableMetadata() {
|
||||
$(".metadata").load(
|
||||
'/caravel/table/' + database_id + '/' + $("#dbtable").val() + '/');
|
||||
$('.metadata').load('/caravel/table/' + databaseId + '/' + $('#dbtable').val() + '/');
|
||||
}
|
||||
$("#dbtable").on("change", showTableMetadata);
|
||||
$('#dbtable').on('change', showTableMetadata);
|
||||
showTableMetadata();
|
||||
$("#create_view").click(function () {
|
||||
$('#create_view').click(function () {
|
||||
showModal({
|
||||
title: "Error",
|
||||
body: "Sorry, this feature is not yet implemented",
|
||||
title: 'Error',
|
||||
body: 'Sorry, this feature is not yet implemented',
|
||||
});
|
||||
});
|
||||
$(".sqlcontent").show();
|
||||
|
||||
$('.sqlcontent').show();
|
||||
function selectStarOnClick() {
|
||||
$.ajax('/caravel/select_star/' + database_id + '/' + $("#dbtable").val() + '/')
|
||||
.done(function (msg) {
|
||||
const url = '/caravel/select_star/' + databaseId + '/' + $('#dbtable').val() + '/';
|
||||
$.ajax(url).done(function (msg) {
|
||||
editor.setValue(msg);
|
||||
});
|
||||
}
|
||||
|
||||
$("#select_star").click(selectStarOnClick);
|
||||
|
||||
$('#select_star').click(selectStarOnClick);
|
||||
editor.setValue(getParam('sql'));
|
||||
$(window).bind("popstate", function (event) {
|
||||
$(window).bind('popstate', function () {
|
||||
// Could do something more lightweight here, but we're not optimizing
|
||||
// for the use of the back button anyways
|
||||
editor.setValue(getParam('sql'));
|
||||
$("#run").click();
|
||||
$('#run').click();
|
||||
});
|
||||
$("#run").click(function () {
|
||||
$('#run').click(function () {
|
||||
$('#results').hide(0);
|
||||
$('#loading').show(0);
|
||||
history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue()));
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
type: 'POST',
|
||||
url: '/caravel/runsql/',
|
||||
data: {
|
||||
data: JSON.stringify({
|
||||
database_id: $('#database_id').val(),
|
||||
databaseId: $('#databaseId').val(),
|
||||
sql: editor.getSession().getValue(),
|
||||
}),
|
||||
},
|
||||
success: function (data) {
|
||||
success(data) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(data);
|
||||
|
||||
$('table.sql_results').DataTable({
|
||||
retrieve: true,
|
||||
paging: false,
|
||||
|
@ -95,7 +82,7 @@ $(document).ready(function () {
|
|||
aaSorting: [],
|
||||
});
|
||||
},
|
||||
error: function (err, err2) {
|
||||
error(err) {
|
||||
$('#loading').hide(0);
|
||||
$('#results').show(0);
|
||||
$('#results').html(err.responseText);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
var px = require('./modules/caravel.js');
|
||||
const $ = window.$ = require('jquery');
|
||||
/* eslint no-unused-vars: 0 */
|
||||
const jQuery = window.jQuery = $;
|
||||
const px = require('./modules/caravel.js');
|
||||
|
||||
require('bootstrap');
|
||||
|
||||
$(document).ready(function () {
|
||||
var slice;
|
||||
var data = $('.slice').data('slice');
|
||||
slice = px.Slice(data);
|
||||
const data = $('.slice').data('slice');
|
||||
const slice = px.Slice(data);
|
||||
slice.render();
|
||||
slice.bindResizeToWindowResize();
|
||||
});
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
var $ = window.$ = require('jquery');
|
||||
var jQuery = window.jQuery = $;
|
||||
|
||||
const $ = window.$ = require('jquery');
|
||||
/* eslint no-unused-vars: 0 */
|
||||
const jQuery = window.jQuery = $;
|
||||
require('../stylesheets/welcome.css');
|
||||
require('bootstrap');
|
||||
require('datatables.net-bs');
|
||||
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
|
||||
require('../node_modules/cal-heatmap/cal-heatmap.css');
|
||||
var d3 = require('d3');
|
||||
|
||||
var CalHeatMap = require('cal-heatmap');
|
||||
|
||||
const d3 = require('d3');
|
||||
const CalHeatMap = require('cal-heatmap');
|
||||
function modelViewTable(selector, modelView, orderCol, order) {
|
||||
// Builds a dataTable from a flask appbuilder api endpoint
|
||||
var url = '/' + modelView.toLowerCase() + '/api/read';
|
||||
let url = '/' + modelView.toLowerCase() + '/api/read';
|
||||
url += '?_oc_' + modelView + '=' + orderCol;
|
||||
url += '&_od_' + modelView +'=' + order;
|
||||
url += '&_od_' + modelView + '=' + order;
|
||||
$.getJSON(url, function (data) {
|
||||
var tableData = jQuery.map(data.result, function (el, i) {
|
||||
var row = $.map(data.list_columns, function (col, i) {
|
||||
return el[col];
|
||||
});
|
||||
return [row];
|
||||
const tableData = $.map(data.result, function (el) {
|
||||
const row = $.map(data.list_columns, function (col) {
|
||||
return el[col];
|
||||
});
|
||||
return [row];
|
||||
});
|
||||
var cols = jQuery.map(data.list_columns, function (col, i) {
|
||||
const cols = $.map(data.list_columns, function (col) {
|
||||
return { sTitle: data.label_columns[col] };
|
||||
});
|
||||
var panel = $(selector).parents('.panel');
|
||||
panel.find("img.loading").remove();
|
||||
const panel = $(selector).parents('.panel');
|
||||
panel.find('img.loading').remove();
|
||||
$(selector).DataTable({
|
||||
aaData: tableData,
|
||||
aoColumns: cols,
|
||||
|
@ -37,32 +35,28 @@ function modelViewTable(selector, modelView, orderCol, order) {
|
|||
searching: true,
|
||||
bInfo: false,
|
||||
});
|
||||
|
||||
// Hack to move the searchbox in the right spot
|
||||
var search = panel.find(".dataTables_filter input");
|
||||
const search = panel.find('.dataTables_filter input');
|
||||
search.addClass('form-control').detach();
|
||||
search.appendTo(panel.find(".search"));
|
||||
search.appendTo(panel.find('.search'));
|
||||
panel.find('.dataTables_filter').remove();
|
||||
|
||||
// Hack to display the page navigator properly
|
||||
panel.find('.col-sm-5').remove();
|
||||
var nav = panel.find('.col-sm-7');
|
||||
const nav = panel.find('.col-sm-7');
|
||||
nav.removeClass('col-sm-7');
|
||||
nav.addClass('col-sm-12');
|
||||
|
||||
$(selector).slideDown();
|
||||
$('[data-toggle="tooltip"]').tooltip({ container: 'body' });
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
d3.json('/caravel/activity_per_day', function (json) {
|
||||
var ext = d3.extent(d3.values(json));
|
||||
var cal = new CalHeatMap();
|
||||
var range = 10;
|
||||
var legendBounds = [];
|
||||
var step = (ext[1] - ext[0]) / (range - 1);
|
||||
for (var i = 0; i< range; i++) {
|
||||
const ext = d3.extent(d3.values(json));
|
||||
const cal = new CalHeatMap();
|
||||
const range = 10;
|
||||
const legendBounds = [];
|
||||
const step = (ext[1] - ext[0]) / (range - 1);
|
||||
for (let i = 0; i < range; i++) {
|
||||
legendBounds.push(i * step + ext[0]);
|
||||
}
|
||||
cal.init({
|
||||
|
@ -70,10 +64,14 @@ $(document).ready(function () {
|
|||
range: 13,
|
||||
data: json,
|
||||
legend: legendBounds,
|
||||
legendColors: ['#D6E685', '#1E6823'], // Based on github's colors
|
||||
domain: "month",
|
||||
subDomain: "day",
|
||||
itemName: "action",
|
||||
legendColors: [
|
||||
'#D6E685',
|
||||
'#1E6823',
|
||||
],
|
||||
// Based on github's colors
|
||||
domain: 'month',
|
||||
subDomain: 'day',
|
||||
itemName: 'action',
|
||||
tooltip: true,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"test": "spec"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --compilers js:babel-core/register --required spec/helpers/browser.js spec/**/*_spec.*",
|
||||
"test": "npm run lint && mocha --compilers js:babel-core/register --required spec/helpers/browser.js spec/**/*_spec.*",
|
||||
"dev": "webpack -d --watch --colors",
|
||||
"prod": "webpack -p --colors",
|
||||
"lint": "npm run --silent lint:js",
|
||||
|
@ -81,7 +81,11 @@
|
|||
"chai": "^3.5.0",
|
||||
"css-loader": "^0.23.1",
|
||||
"enzyme": "^2.0.0",
|
||||
"eslint": "^2.2.0",
|
||||
"eslint": "^2.13.1",
|
||||
"eslint-config-airbnb": "^9.0.1",
|
||||
"eslint-plugin-import": "^1.11.1",
|
||||
"eslint-plugin-jsx-a11y": "^2.0.1",
|
||||
"eslint-plugin-react": "^5.2.2",
|
||||
"exports-loader": "^0.6.3",
|
||||
"file-loader": "^0.8.5",
|
||||
"imports-loader": "^0.6.5",
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
require('babel-register')();
|
||||
|
||||
var jsdom = require('jsdom').jsdom;
|
||||
const jsdom = require('jsdom').jsdom;
|
||||
|
||||
var exposedProperties = ['window', 'navigator', 'document'];
|
||||
const exposedProperties = ['window', 'navigator', 'document'];
|
||||
|
||||
global.document = jsdom('');
|
||||
global.window = document.defaultView;
|
||||
|
@ -16,7 +16,7 @@ Object.keys(document.defaultView).forEach((property) => {
|
|||
});
|
||||
|
||||
global.navigator = {
|
||||
serAgent: 'node.js',
|
||||
userAgent: 'node.js',
|
||||
};
|
||||
|
||||
documentRef = document;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint global-require: 0 */
|
||||
const d3 = window.d3 || require('d3');
|
||||
|
||||
export const EARTH_CIRCUMFERENCE_KM = 40075.16;
|
||||
|
@ -23,5 +24,5 @@ export function isNumeric(num) {
|
|||
|
||||
export function rgbLuminance(r, g, b) {
|
||||
// Formula: https://en.wikipedia.org/wiki/Relative_luminance
|
||||
return LUMINANCE_RED_WEIGHT*r + LUMINANCE_GREEN_WEIGHT*g + LUMINANCE_BLUE_WEIGHT*b;
|
||||
return (LUMINANCE_RED_WEIGHT * r) + (LUMINANCE_GREEN_WEIGHT * g) + (LUMINANCE_BLUE_WEIGHT * b);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
const path = require('path');
|
||||
|
||||
const APP_DIR = path.resolve(__dirname, './'); // input
|
||||
const BUILD_DIR = path.resolve(__dirname, './javascripts/dist'); // output
|
||||
// input dir
|
||||
const APP_DIR = path.resolve(__dirname, './');
|
||||
|
||||
// output dir
|
||||
const BUILD_DIR = path.resolve(__dirname, './javascripts/dist');
|
||||
|
||||
const config = {
|
||||
entry: {
|
||||
|
@ -18,10 +21,12 @@ const config = {
|
|||
filename: '[name].entry.js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['', '.js', '.jsx'],
|
||||
alias: {
|
||||
webworkify: 'webworkify-webpack',
|
||||
},
|
||||
extensions: [
|
||||
'',
|
||||
'.js',
|
||||
'.jsx',
|
||||
],
|
||||
alias: { webworkify: 'webworkify-webpack' },
|
||||
},
|
||||
module: {
|
||||
loaders: [
|
||||
|
@ -30,40 +35,53 @@ const config = {
|
|||
exclude: APP_DIR + '/node_modules',
|
||||
loader: 'babel',
|
||||
query: {
|
||||
presets: ['airbnb', 'es2015', 'react'],
|
||||
presets: [
|
||||
'airbnb',
|
||||
'es2015',
|
||||
'react',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
/* for react-map-gl overlays */
|
||||
{
|
||||
test: /\.react\.js$/,
|
||||
include: APP_DIR + '/node_modules/react-map-gl/src/overlays',
|
||||
loader: 'babel',
|
||||
},
|
||||
|
||||
/* for require('*.css') */
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: APP_DIR,
|
||||
loader: "style-loader!css-loader",
|
||||
loader: 'style-loader!css-loader',
|
||||
},
|
||||
|
||||
/* for css linking images */
|
||||
{ test: /\.png$/, loader: "url-loader?limit=100000" },
|
||||
{ test: /\.jpg$/, loader: "file-loader" },
|
||||
{ test: /\.gif$/, loader: "file-loader" },
|
||||
|
||||
{
|
||||
test: /\.png$/,
|
||||
loader: 'url-loader?limit=100000',
|
||||
},
|
||||
{
|
||||
test: /\.jpg$/,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
{
|
||||
test: /\.gif$/,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
/* for font-awesome */
|
||||
{ test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" },
|
||||
{ test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "file-loader" },
|
||||
|
||||
{
|
||||
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'url-loader?limit=10000&minetype=application/font-woff',
|
||||
},
|
||||
{
|
||||
test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
|
||||
loader: 'file-loader',
|
||||
},
|
||||
/* for require('*.less') */
|
||||
{
|
||||
test: /\.less$/,
|
||||
include: APP_DIR,
|
||||
loader: "style!css!less",
|
||||
loader: 'style!css!less',
|
||||
},
|
||||
|
||||
/* for mapbox */
|
||||
{
|
||||
test: /\.json$/,
|
||||
|
@ -88,5 +106,4 @@ const config = {
|
|||
},
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
|
Loading…
Reference in New Issue