[js linting] use airbnb eslint settings (#796)

* add airbnb eslint settings and lint all the code

* fix linting erros
This commit is contained in:
Alanna Scott 2016-07-27 16:57:05 -07:00 committed by GitHub
parent f43e5f18d5
commit 1101de5ae4
20 changed files with 1006 additions and 1132 deletions

View File

@ -1,3 +1,6 @@
node_modules/* node_modules/*
vendor/* vendor/*
javascripts/dist/* javascripts/dist/*
visualizations/*
stylesheets/*
spec/*

View File

@ -1,234 +1,12 @@
{ {
"root": true, "extends": "airbnb",
"globals": {
"Symbol": false,
"Map": false,
"Set": false,
"Reflect": false,
},
"env": {
"es6": false,
"browser": true,
"node": true,
},
"parserOptions": {
"ecmaVersion": 5,
"sourceType": "module"
},
"rules": { "rules": {
"array-bracket-spacing": [2, "never", { "prefer-template": 0,
"singleValue": false, "new-cap": 0,
"objectsInArrays": false, "no-restricted-syntax": 0,
"arraysInArrays": false "guard-for-in": 0,
}], "prefer-arrow-callback": 0,
"array-callback-return": [2], "func-names": 0,
"block-spacing": [2, "always"], "react/jsx-no-bind": 0,
"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 }]
} }

View File

@ -1,11 +1,10 @@
var $ = require('jquery'); const $ = require('jquery');
var utils = require('./modules/utils'); const utils = require('./modules/utils');
$(document).ready(function () { $(document).ready(function () {
$(':checkbox[data-checkbox-api-prefix]').change(function () { $(':checkbox[data-checkbox-api-prefix]').change(function () {
var $this = $(this); const $this = $(this);
var prefix = $this.data('checkbox-api-prefix'); const prefix = $this.data('checkbox-api-prefix');
var id = $this.attr('id'); const id = $this.attr('id');
utils.toggleCheckbox(prefix, "#" + id); utils.toggleCheckbox(prefix, '#' + id);
}); });
}); });

View File

@ -1,39 +1,61 @@
var $ = window.$ = require('jquery'); const $ = window.$ = require('jquery');
var jQuery = window.jQuery = $; const jQuery = window.jQuery = $;
var px = require('../modules/caravel.js'); const px = require('../modules/caravel.js');
var d3 = require('d3'); const d3 = require('d3');
var urlLib = require('url'); const urlLib = require('url');
var showModal = require('../modules/utils.js').showModal; const showModal = require('../modules/utils.js').showModal;
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import SliceAdder from './components/SliceAdder.jsx'; import SliceAdder from './components/SliceAdder.jsx';
import GridLayout from './components/GridLayout.jsx'; import GridLayout from './components/GridLayout.jsx';
var ace = require('brace'); const ace = require('brace');
require('bootstrap'); require('bootstrap');
require('brace/mode/css'); require('brace/mode/css');
require('brace/theme/crimson_editor'); require('brace/theme/crimson_editor');
require('./main.css'); require('./main.css');
require('../caravel-select2.js'); 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) { if (!style) {
var dashboard = $.extend(dashboardData, { 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: {}, filters: {},
init: function () { init() {
this.initDashboardView(); this.initDashboardView();
this.firstLoad = true; this.firstLoad = true;
px.initFavStars(); px.initFavStars();
var sliceObjects = [], const sliceObjects = [];
dash = this; const dash = this;
dashboard.slices.forEach(function (data) { dashboard.slices.forEach((data) => {
if (data.error) { if (data.error) {
var html = '<div class="alert alert-danger">' + data.error + '</div>'; const html = '<div class="alert alert-danger">' + data.error + '</div>';
$('#slice_' + data.slice_id).find('.token').html(html); $('#slice_' + data.sliceId).find('.token').html(html);
} else { } else {
var slice = px.Slice(data, dash); const slice = px.Slice(data, dash);
$('#slice_' + data.slice_id).find('a.refresh').click(function () { $('#slice_' + data.sliceId).find('a.refresh').click(() => {
slice.render(true); slice.render(true);
}); });
sliceObjects.push(slice); sliceObjects.push(slice);
@ -45,81 +67,81 @@ var Dashboard = function (dashboardData) {
this.startPeriodicRender(0); this.startPeriodicRender(0);
this.bindResizeToWindowResize(); this.bindResizeToWindowResize();
}, },
loadPreSelectFilters: function () { loadPreSelectFilters() {
try { try {
var filters = JSON.parse(px.getParam("preselect_filters") || "{}"); const filters = JSON.parse(px.getParam('preselect_filters') || '{}');
for (var slice_id in filters) { for (const sliceId in filters) {
for (var col in filters[slice_id]) { for (const col in filters[sliceId]) {
this.setFilter(slice_id, col, filters[slice_id][col], false, false); this.setFilter(sliceId, col, filters[sliceId][col], false, false);
} }
} }
} catch (e) { } catch (e) {
console.error(e); // console.error(e);
} }
}, },
setFilter: function (slice_id, col, vals, refresh) { setFilter(sliceId, col, vals, refresh) {
this.addFilter(slice_id, col, vals, false, refresh); this.addFilter(sliceId, col, vals, false, refresh);
}, },
addFilter: function (slice_id, col, vals, merge = true, refresh = true) { addFilter(sliceId, col, vals, merge = true, refresh = true) {
if (!(slice_id in this.filters)) { if (!(sliceId in this.filters)) {
this.filters[slice_id] = {}; this.filters[sliceId] = {};
} }
if (!(col in this.filters[slice_id]) || !merge) { if (!(col in this.filters[sliceId]) || !merge) {
this.filters[slice_id][col] = vals; this.filters[sliceId][col] = vals;
} else { } 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) { if (refresh) {
this.refreshExcept(slice_id); this.refreshExcept(sliceId);
} }
this.updateFilterParamsInUrl(); this.updateFilterParamsInUrl();
}, },
readFilters: function () { readFilters() {
// Returns a list of human readable active filters // Returns a list of human readable active filters
return JSON.stringify(this.filters, null, 4); return JSON.stringify(this.filters, null, 4);
}, },
updateFilterParamsInUrl: function () { updateFilterParamsInUrl() {
var urlObj = urlLib.parse(location.href, true); const urlObj = urlLib.parse(location.href, true);
urlObj.query = urlObj.query || {}; urlObj.query = urlObj.query || {};
urlObj.query.preselect_filters = this.readFilters(); urlObj.query.preselect_filters = this.readFilters();
urlObj.search = null; urlObj.search = null;
history.pushState(urlObj.query, window.title, urlLib.format(urlObj)); history.pushState(urlObj.query, window.title, urlLib.format(urlObj));
}, },
bindResizeToWindowResize: function () { bindResizeToWindowResize() {
var resizeTimer; let resizeTimer;
var dash = this; const dash = this;
$(window).on('resize', function (e) { $(window).on('resize', () => {
clearTimeout(resizeTimer); clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () { resizeTimer = setTimeout(() => {
dash.slices.forEach(function (slice) { dash.slices.forEach((slice) => {
slice.resize(); slice.resize();
}); });
}, 500); }, 500);
}); });
}, },
stopPeriodicRender: function () { stopPeriodicRender() {
if (this.refreshTimer) { if (this.refreshTimer) {
clearTimeout(this.refreshTimer); clearTimeout(this.refreshTimer);
this.refreshTimer = null; this.refreshTimer = null;
} }
}, },
startPeriodicRender: function (interval) { startPeriodicRender(interval) {
this.stopPeriodicRender(); this.stopPeriodicRender();
var dash = this; const dash = this;
var maxRandomDelay = Math.min(interval * 0.2, 5000); const maxRandomDelay = Math.min(interval * 0.2, 5000);
var refreshAll = function () { const refreshAll = function () {
dash.slices.forEach(function (slice) { dash.slices.forEach(function (slice) {
var force = !dash.firstLoad; const force = !dash.firstLoad;
setTimeout(function () { setTimeout(function () {
slice.render(force); 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()); maxRandomDelay * Math.random());
}); });
dash.firstLoad = false; dash.firstLoad = false;
}; };
var fetchAndRender = function () { const fetchAndRender = function () {
refreshAll(); refreshAll();
if (interval > 0) { if (interval > 0) {
dash.refreshTimer = setTimeout(function () { dash.refreshTimer = setTimeout(function () {
@ -129,122 +151,130 @@ var Dashboard = function (dashboardData) {
}; };
fetchAndRender(); fetchAndRender();
}, },
refreshExcept: function (slice_id) { refreshExcept(sliceId) {
var immune = this.metadata.filter_immune_slices || []; const immune = this.metadata.filter_immune_slices || [];
this.slices.forEach(function (slice) { 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(); slice.render();
} }
}); });
}, },
clearFilters: function (slice_id) { clearFilters(sliceId) {
delete this.filters[slice_id]; delete this.filters[sliceId];
this.refreshExcept(slice_id); this.refreshExcept(sliceId);
this.updateFilterParamsInUrl(); this.updateFilterParamsInUrl();
}, },
removeFilter: function (slice_id, col, vals) { removeFilter(sliceId, col, vals) {
if (slice_id in this.filters) { if (sliceId in this.filters) {
if (col in this.filters[slice_id]) { if (col in this.filters[sliceId]) {
var a = []; const a = [];
this.filters[slice_id][col].forEach(function (v) { this.filters[sliceId][col].forEach(function (v) {
if (vals.indexOf(v) < 0) { if (vals.indexOf(v) < 0) {
a.push(v); a.push(v);
} }
}); });
this.filters[slice_id][col] = a; this.filters[sliceId][col] = a;
} }
} }
this.refreshExcept(slice_id); this.refreshExcept(sliceId);
this.updateFilterParamsInUrl(); this.updateFilterParamsInUrl();
}, },
getSlice: function (slice_id) { getSlice(sliceId) {
slice_id = parseInt(slice_id, 10); const id = parseInt(sliceId, 10);
for (var i=0; i < this.slices.length; i++) { let i = 0;
if (this.slices[i].data.slice_id === slice_id) { let slice = null;
return this.slices[i]; 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 () { showAddSlice() {
var slicesOnDashMap = {}; const slicesOnDashMap = {};
this.reactGridLayout.serialize().forEach(function (position) { const layoutPositions = this.reactGridLayout.serialize();
layoutPositions.forEach((position) => {
slicesOnDashMap[position.slice_id] = true; slicesOnDashMap[position.slice_id] = true;
}, this); });
render( render(
<SliceAdder dashboard={dashboard} slicesOnDashMap={slicesOnDashMap} caravel={px} />, <SliceAdder dashboard={dashboard} slicesOnDashMap={slicesOnDashMap} caravel={px} />,
document.getElementById("add-slice-container") document.getElementById('add-slice-container')
); );
}, },
getAjaxErrorMsg: function (error) { getAjaxErrorMsg(error) {
var respJSON = error.responseJSON; const respJSON = error.responseJSON;
return (respJSON && respJSON.message) ? respJSON.message : return (respJSON && respJSON.message) ? respJSON.message :
error.responseText; error.responseText;
}, },
addSlicesToDashboard: function (sliceIds) { addSlicesToDashboard(sliceIds) {
const getAjaxErrorMsg = this.getAjaxErrorMsg;
$.ajax({ $.ajax({
type: "POST", type: 'POST',
url: '/caravel/add_slices/' + dashboard.id + '/', url: '/caravel/add_slices/' + dashboard.id + '/',
data: { 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 // Refresh page to allow for slices to re-render
window.location.reload(); window.location.reload();
}, },
error: function (error) { error(error) {
var errorMsg = this.getAjaxErrorMsg(error); const errorMsg = getAjaxErrorMsg(error);
showModal({ showModal({
title: "Error", title: 'Error',
body: "Sorry, there was an error adding slices to this dashboard: </ br>" + errorMsg body: 'Sorry, there was an error adding slices to this dashboard: </ br>' + errorMsg,
}); });
}.bind(this) },
}); });
}, },
saveDashboard: function () { saveDashboard() {
var expandedSlices = {}; const expandedSlices = {};
$.each($(".slice_info"), function (i, d) { $.each($('.slice_info'), function () {
var widget = $(this).parents('.widget'); const widget = $(this).parents('.widget');
var sliceDescription = widget.find('.slice_description'); const sliceDescription = widget.find('.slice_description');
if (sliceDescription.is(":visible")) { if (sliceDescription.is(':visible')) {
expandedSlices[$(widget).attr('data-slice-id')] = true; expandedSlices[$(widget).attr('data-slice-id')] = true;
} }
}); });
var data = { const positions = this.reactGridLayout.serialize();
positions: this.reactGridLayout.serialize(), const data = {
positions,
css: this.editor.getValue(), css: this.editor.getValue(),
expanded_slices: expandedSlices expanded_slices: expandedSlices,
}; };
$.ajax({ $.ajax({
type: "POST", type: 'POST',
url: '/caravel/save_dash/' + dashboard.id + '/', url: '/caravel/save_dash/' + dashboard.id + '/',
data: { data: {
data: JSON.stringify(data) data: JSON.stringify(data),
}, },
success: function () { success() {
showModal({ showModal({
title: "Success", title: 'Success',
body: "This dashboard was saved successfully." body: 'This dashboard was saved successfully.',
}); });
}, },
error: function (error) { error(error) {
var errorMsg = this.getAjaxErrorMsg(error); const errorMsg = this.getAjaxErrorMsg(error);
showModal({ showModal({
title: "Error", title: 'Error',
body: "Sorry, there was an error saving this dashboard: </ br>" + errorMsg body: 'Sorry, there was an error saving this dashboard: </ br>' + errorMsg,
}); });
} },
}); });
}, },
initDashboardView: function () { initDashboardView() {
this.posDict = {}; this.posDict = {};
this.position_json.forEach(function (position) { this.position_json.forEach(function (position) {
this.posDict[position.slice_id] = position; this.posDict[position.slice_id] = position;
}, this); }, this);
this.reactGridLayout = render( this.reactGridLayout = render(
<GridLayout slices={this.slices} posDict={this.posDict} dashboard={dashboard}/>, <GridLayout slices={this.slices} posDict={this.posDict} dashboard={dashboard} />,
document.getElementById("grid-container") document.getElementById('grid-container')
); );
this.curUserId = $('.dashboard').data('user'); this.curUserId = $('.dashboard').data('user');
@ -260,101 +290,77 @@ var Dashboard = function (dashboardData) {
$(this).find('.chart-controls').fadeOut(300); $(this).find('.chart-controls').fadeOut(300);
} }
); );
$("div.grid-container").css('visibility', 'visible'); $('div.grid-container').css('visibility', 'visible');
$("#savedash").click(this.saveDashboard.bind(this)); $('#savedash').click(this.saveDashboard.bind(this));
$("#add-slice").click(this.showAddSlice.bind(this)); $('#add-slice').click(this.showAddSlice.bind(this));
var editor = ace.edit("dash_css"); const editor = ace.edit('dash_css');
this.editor = editor; this.editor = editor;
editor.$blockScrolling = Infinity; editor.$blockScrolling = Infinity;
editor.setTheme("ace/theme/crimson_editor"); editor.setTheme('ace/theme/crimson_editor');
editor.setOptions({ editor.setOptions({
minLines: 16, minLines: 16,
maxLines: Infinity, maxLines: Infinity,
useWorker: false useWorker: false,
}); });
editor.getSession().setMode("ace/mode/css"); editor.getSession().setMode('ace/mode/css');
$(".select2").select2({ $('.select2').select2({
dropdownAutoWidth: true dropdownAutoWidth: true,
}); });
$("#css_template").on("change", function () { $('#css_template').on('change', function () {
var css = $(this).find('option:selected').data('css'); const css = $(this).find('option:selected').data('css');
editor.setValue(css); editor.setValue(css);
$('#dash_css').val(css); $('#dash_css').val(css);
injectCss("dashboard-template", css); injectCss('dashboard-template', css);
}); });
$('#filters').click(function () { $('#filters').click(() => {
showModal({ showModal({
title: "<span class='fa fa-info-circle'></span> Current Global Filters", title: '<span class="fa fa-info-circle"></span> Current Global Filters',
body: "The following global filters are currently applied:<br/>" + dashboard.readFilters() body: 'The following global filters are currently applied:<br/>' +
dashboard.readFilters(),
}); });
}); });
$("#refresh_dash_interval").on("change", function () { $('#refresh_dash_interval').on('change', function () {
var interval = $(this).find('option:selected').val() * 1000; const interval = $(this).find('option:selected').val() * 1000;
dashboard.startPeriodicRender(interval); dashboard.startPeriodicRender(interval);
}); });
$('#refresh_dash').click(function () { $('#refresh_dash').click(() => {
dashboard.slices.forEach(function (slice) { dashboard.slices.forEach((slice) => {
slice.render(true); slice.render(true);
}); });
}); });
$("div.widget").click(function (e) { $('div.widget').click(function (e) {
var $this = $(this); const $this = $(this);
var $target = $(e.target); const $target = $(e.target);
if ($target.hasClass("slice_info")) { if ($target.hasClass('slice_info')) {
$this.find(".slice_description").slideToggle(0, function () { $this.find('.slice_description').slideToggle(0, function () {
$this.find('.refresh').click(); $this.find('.refresh').click();
}); });
} else if ($target.hasClass("controls-toggle")) { } else if ($target.hasClass('controls-toggle')) {
$this.find(".chart-controls").toggle(); $this.find('.chart-controls').toggle();
} }
}); });
editor.on("change", function () { editor.on('change', function () {
var css = editor.getValue(); const css = editor.getValue();
$('#dash_css').val(css); $('#dash_css').val(css);
injectCss("dashboard-template", css); injectCss('dashboard-template', css);
}); });
var css = $('.dashboard').data('css'); const css = $('.dashboard').data('css');
injectCss("dashboard-template", 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;
}
}
}
}); });
dashboard.init(); dashboard.init();
return dashboard; return dashboard;
}; }
$(document).ready(function () { $(document).ready(() => {
Dashboard($('.dashboard').data('dashboard')); dashboardContainer($('.dashboard').data('dashboard'));
$('[data-toggle="tooltip"]').tooltip({ container: 'body' }); $('[data-toggle="tooltip"]').tooltip({ container: 'body' });
}); });

View File

@ -1,108 +1,69 @@
import $ from 'jquery';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Responsive, WidthProvider } from 'react-grid-layout'; import { Responsive, WidthProvider } from 'react-grid-layout';
const ResponsiveReactGridLayout = WidthProvider(Responsive); const ResponsiveReactGridLayout = WidthProvider(Responsive);
import SliceCell from './SliceCell';
require('../../../node_modules/react-grid-layout/css/styles.css'); require('../../../node_modules/react-grid-layout/css/styles.css');
require('../../../node_modules/react-resizable/css/styles.css'); require('../../../node_modules/react-resizable/css/styles.css');
const sliceCellPropTypes = { const propTypes = {
slice: PropTypes.object.isRequired,
removeSlice: PropTypes.func.isRequired,
expandedSlices: PropTypes.object
};
const gridLayoutPropTypes = {
dashboard: PropTypes.object.isRequired, dashboard: PropTypes.object.isRequired,
slices: PropTypes.arrayOf(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 { 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) { removeSlice(sliceId) {
$('[data-toggle="tooltip"]').tooltip("hide"); $('[data-toggle=tooltip]').tooltip('hide');
this.setState({ this.setState({
layout: this.state.layout.filter(function (reactPos) { layout: this.state.layout.filter(function (reactPos) {
return reactPos.i !== String(sliceId); return reactPos.i !== String(sliceId);
}), }),
slices: this.state.slices.filter(function (slice) { slices: this.state.slices.filter(function (slice) {
return slice.slice_id !== sliceId; 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, col: reactPos.x + 1,
row: reactPos.y, row: reactPos.y,
size_x: reactPos.w, 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() { render() {
return ( return (
<ResponsiveReactGridLayout <ResponsiveReactGridLayout
@ -157,30 +88,35 @@ class GridLayout extends React.Component {
onDragStop={this.onDragStop.bind(this)} onDragStop={this.onDragStop.bind(this)}
cols={{ lg: 12, md: 12, sm: 10, xs: 8, xxs: 6 }} cols={{ lg: 12, md: 12, sm: 10, xs: 8, xxs: 6 }}
rowHeight={100} rowHeight={100}
autoSize={true} autoSize
margin={[20, 20]} margin={[20, 20]}
useCSSTransforms={false} useCSSTransforms={false}
draggableHandle=".drag"> draggableHandle=".drag"
{this.state.slices.map((slice) => { >
return ( {
<div /* eslint arrow-body-style: 0 */
id={'slice_' + slice.slice_id} this.state.slices.map((slice) => {
key={slice.slice_id} return (
data-slice-id={slice.slice_id} <div
className={"widget " + slice.viz_name}> id={'slice_' + slice.slice_id}
<SliceCell key={slice.slice_id}
slice={slice} data-slice-id={slice.slice_id}
removeSlice={this.removeSlice.bind(this)} className={`widget ${slice.viz_name}`}
expandedSlices={this.props.dashboard.metadata.expanded_slices}/> >
</div> <SliceCell
); slice={slice}
})} removeSlice={(sliceId) => this.removeSlice(sliceId)}
expandedSlices={this.props.dashboard.metadata.expanded_slices}
/>
</div>
);
})
}
</ResponsiveReactGridLayout> </ResponsiveReactGridLayout>
); );
} }
} }
SliceCell.propTypes = sliceCellPropTypes; GridLayout.propTypes = propTypes;
GridLayout.propTypes = gridLayoutPropTypes;
export default GridLayout; export default GridLayout;

View File

@ -4,37 +4,37 @@ const propTypes = {
modalId: PropTypes.string.isRequired, modalId: PropTypes.string.isRequired,
title: PropTypes.string, title: PropTypes.string,
modalContent: PropTypes.node, modalContent: PropTypes.node,
customButtons: PropTypes.node customButton: PropTypes.node,
}; };
class Modal extends React.Component { function Modal({ modalId, title, modalContent, customButton }) {
render() { return (
return ( <div className="modal fade" id={modalId} role="dialog">
<div className="modal fade" id={this.props.modalId} role="dialog"> <div className="modal-dialog" role="document">
<div className="modal-dialog" role="document"> <div className="modal-content">
<div className="modal-content"> <div className="modal-header">
<div className="modal-header"> <button type="button" className="close" data-dismiss="modal" aria-label="Close">
<button type="button" className="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">&times;</span>
<span aria-hidden="true">&times;</span> </button>
</button> <h4 className="modal-title">{title}</h4>
<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>
</div> </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>
); </div>
} );
} }
Modal.propTypes = propTypes; Modal.propTypes = propTypes;

View File

@ -1,3 +1,4 @@
import $ from 'jquery';
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import update from 'immutability-helper'; import update from 'immutability-helper';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table'; import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
@ -6,15 +7,14 @@ require('../../../node_modules/react-bootstrap-table/css/react-bootstrap-table.c
const propTypes = { const propTypes = {
dashboard: PropTypes.object.isRequired, dashboard: PropTypes.object.isRequired,
caravel: PropTypes.object.isRequired caravel: PropTypes.object.isRequired,
}; };
class SliceAdder extends React.Component { class SliceAdder extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
slices: [] slices: [],
}; };
this.addSlices = this.addSlices.bind(this); this.addSlices = this.addSlices.bind(this);
@ -22,46 +22,47 @@ class SliceAdder extends React.Component {
this.toggleAllSlices = this.toggleAllSlices.bind(this); this.toggleAllSlices = this.toggleAllSlices.bind(this);
this.slicesLoaded = false; this.slicesLoaded = false;
this.selectRowProp = { this.selectRowProp = {
mode: "checkbox", mode: 'checkbox',
clickToSelect: true, clickToSelect: true,
onSelect: this.toggleSlice, onSelect: this.toggleSlice,
onSelectAll: this.toggleAllSlices onSelectAll: this.toggleAllSlices,
}; };
this.options = { this.options = {
defaultSortOrder: "desc", defaultSortOrder: 'desc',
defaultSortName: "modified", defaultSortName: 'modified',
sizePerPage: 10 sizePerPage: 10,
}; };
} }
componentDidMount() { 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({ this.slicesRequest = $.ajax({
url: uri, url: uri,
type: 'GET', type: 'GET',
success: function (response) { success: function (response) {
this.slicesLoaded = true; this.slicesLoaded = true;
// Prepare slice data for table // Prepare slice data for table
let slices = response.result; const slices = response.result.map(function (slice) {
slices.forEach(function (slice) { return {
slice.id = slice.data.slice_id; id: slice.data.slice_id,
slice.sliceName = slice.data.slice_name; sliceName: slice.data.slice_name,
slice.vizType = slice.viz_type; vizType: slice.viz_type,
slice.modified = slice.modified; modified: slice.modified,
data: slice.data,
};
}); });
this.setState({ this.setState({
slices: slices, slices,
selectionMap: {} selectionMap: {},
}); });
}.bind(this), }.bind(this),
error: function (error) { error: function (error) {
this.errored = true; this.errored = true;
this.setState({ 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() { addSlices() {
var slices = this.state.slices.filter(function (slice) { const slices = this.state.slices.filter(function (slice) {
return this.state.selectionMap[slice.id]; return this.state.selectionMap[slice.id];
}, this); }, this);
slices.forEach(function (slice) { slices.forEach(function (slice) {
var sliceObj = this.props.caravel.Slice(slice.data, this.props.dashboard); const sliceObj = this.props.caravel.Slice(slice.data, this.props.dashboard);
$("#slice_" + slice.data.slice_id).find('a.refresh').click(function () { $('#slice_' + slice.data.slice_id).find('a.refresh').click(function () {
sliceObj.render(true); sliceObj.render(true);
}); });
this.props.dashboard.slices.push(sliceObj); this.props.dashboard.slices.push(sliceObj);
@ -89,23 +89,23 @@ class SliceAdder extends React.Component {
this.setState({ this.setState({
selectionMap: update(this.state.selectionMap, { selectionMap: update(this.state.selectionMap, {
[slice.id]: { [slice.id]: {
$set: !this.state.selectionMap[slice.id] $set: !this.state.selectionMap[slice.id],
} },
}) }),
}); });
} }
toggleAllSlices(value) { toggleAllSlices(value) {
let updatePayload = {}; const updatePayload = {};
this.state.slices.forEach(function (slice) { this.state.slices.forEach(function (slice) {
updatePayload[slice.id] = { updatePayload[slice.id] = {
$set: value $set: value,
}; };
}, this); }, this);
this.setState({ this.setState({
selectionMap: update(this.state.selectionMap, updatePayload) selectionMap: update(this.state.selectionMap, updatePayload),
}); });
} }
@ -134,13 +134,15 @@ class SliceAdder extends React.Component {
}, this); }, this);
const modalContent = ( const modalContent = (
<div> <div>
<img src="/static/assets/images/loading.gif" <img
className={"loading " + (hideLoad ? "hidden" : "")} src="/static/assets/images/loading.gif"
alt={hideLoad ? "" : "loading"}/> className={'loading ' + (hideLoad ? 'hidden' : '')}
<div className={this.errored ? "" : "hidden"}> alt={hideLoad ? '' : 'loading'}
/>
<div className={this.errored ? '' : 'hidden'}>
{this.state.errorMsg} {this.state.errorMsg}
</div> </div>
<div className={this.slicesLoaded ? "" : "hidden"}> <div className={this.slicesLoaded ? '' : 'hidden'}>
<BootstrapTable <BootstrapTable
ref="table" ref="table"
data={this.state.slices} data={this.state.slices}
@ -149,37 +151,52 @@ class SliceAdder extends React.Component {
hover hover
search search
pagination pagination
height="auto"> height="auto"
<TableHeaderColumn dataField="sliceName" isKey={true} dataSort={true}>Name</TableHeaderColumn> >
<TableHeaderColumn dataField="vizType" dataSort={true}>Viz</TableHeaderColumn> <TableHeaderColumn
dataField="sliceName"
isKey
dataSort
>
Name
</TableHeaderColumn>
<TableHeaderColumn
dataField="vizType"
dataSort
>
Viz
</TableHeaderColumn>
<TableHeaderColumn <TableHeaderColumn
dataField="modified" dataField="modified"
dataSort={true} dataSort
sortFunc={this.modifiedDateComparator} sortFunc={this.modifiedDateComparator}
// Will cause react-bootstrap-table to interpret the HTML returned // Will cause react-bootstrap-table to interpret the HTML returned
dataFormat={modified => modified}> dataFormat={modified => modified}
>
Modified Modified
</TableHeaderColumn> </TableHeaderColumn>
</BootstrapTable> </BootstrapTable>
</div> </div>
</div> </div>
); );
const customButtons = [ const customButton = (
<button key={0} <button
type="button" type="button"
className="btn btn-default" className="btn btn-default"
data-dismiss="modal" data-dismiss="modal"
onClick={this.addSlices} onClick={this.addSlices}
disabled={!enableAddSlice}> disabled={!enableAddSlice}
Add Slices >
Add Slices
</button> </button>
]; );
return ( return (
<Modal modalId='add_slice_modal' <Modal
modalContent={modalContent} modalId="add_slice_modal"
title='Add New Slices' modalContent={modalContent}
customButtons={customButtons} title="Add New Slices"
customButton={customButton}
/> />
); );
} }

View File

@ -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;

View File

@ -16,10 +16,11 @@ export default function QueryAndSaveBtns({ canAdd, onQuery }) {
<button type="button" className="btn btn-primary" onClick={onQuery}> <button type="button" className="btn btn-primary" onClick={onQuery}>
<i className="fa fa-bolt"></i>Query <i className="fa fa-bolt"></i>Query
</button> </button>
<button type="button" <button
className={saveClasses} type="button"
data-target="#save_modal" className={saveClasses}
data-toggle="modal" data-target="#save_modal"
data-toggle="modal"
> >
<i className="fa fa-plus-circle"></i>Save as <i className="fa fa-plus-circle"></i>Save as
</button> </button>

View File

@ -28,31 +28,31 @@ require('../../node_modules/bootstrap-toggle/css/bootstrap-toggle.min.css');
var slice; var slice;
var getPanelClass = function (fieldPrefix) { var getPanelClass = function (fieldPrefix) {
return (fieldPrefix === "flt" ? "filter" : "having") + "_panel"; return (fieldPrefix === 'flt' ? 'filter' : 'having') + '_panel';
}; };
function prepForm() { function prepForm() {
// Assigning the right id to form elements in filters // Assigning the right id to form elements in filters
var fixId = function ($filter, fieldPrefix, i) { var fixId = function ($filter, fieldPrefix, i) {
$filter.attr("id", function () { $filter.attr('id', function () {
return fieldPrefix + "_" + i; return fieldPrefix + '_' + i;
}); });
["col", "op", "eq"].forEach(function (fieldMiddle) { ['col', 'op', 'eq'].forEach(function (fieldMiddle) {
var fieldName = fieldPrefix + "_" + fieldMiddle; var fieldName = fieldPrefix + '_' + fieldMiddle;
$filter.find("[id^='" + fieldName + "_']") $filter.find('[id^=' + fieldName + '_]')
.attr("id", function () { .attr('id', function () {
return fieldName + "_" + i; return fieldName + '_' + i;
}) })
.attr("name", function () { .attr('name', function () {
return fieldName + "_" + i; return fieldName + '_' + i;
}); });
}); });
}; };
["flt", "having"].forEach(function (fieldPrefix) { ['flt', 'having'].forEach(function (fieldPrefix) {
var i = 1; var i = 1;
$("#" + getPanelClass(fieldPrefix) + " #filters > div").each(function () { $('#' + getPanelClass(fieldPrefix) + ' #filters > div').each(function () {
fixId($(this), fieldPrefix, i); fixId($(this), fieldPrefix, i);
i++; i++;
}); });
@ -79,11 +79,10 @@ function query(force, pushState) {
} }
function initExploreView() { function initExploreView() {
function get_collapsed_fieldsets() { 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('||'); collapsed_fieldsets = collapsed_fieldsets.split('||');
} else { } else {
collapsed_fieldsets = []; collapsed_fieldsets = [];
@ -93,18 +92,18 @@ function initExploreView() {
function toggle_fieldset(legend, animation) { function toggle_fieldset(legend, animation) {
var parent = legend.parent(); 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 collapsed_fieldsets = get_collapsed_fieldsets();
var index; var index;
if (parent.hasClass("collapsed")) { if (parent.hasClass('collapsed')) {
if (animation) { if (animation) {
parent.find(".panel-body").slideDown(); parent.find('.panel-body').slideDown();
} else { } else {
parent.find(".panel-body").show(); parent.find('.panel-body').show();
} }
parent.removeClass("collapsed"); parent.removeClass('collapsed');
parent.find("span.collapser").text("[-]"); parent.find('span.collapser').text('[-]');
// removing from array, js is overcomplicated // removing from array, js is overcomplicated
index = collapsed_fieldsets.indexOf(fieldset); index = collapsed_fieldsets.indexOf(fieldset);
@ -113,20 +112,20 @@ function initExploreView() {
} }
} else { // not collapsed } else { // not collapsed
if (animation) { if (animation) {
parent.find(".panel-body").slideUp(); parent.find('.panel-body').slideUp();
} else { } else {
parent.find(".panel-body").hide(); parent.find('.panel-body').hide();
} }
parent.addClass("collapsed"); parent.addClass('collapsed');
parent.find("span.collapser").text("[+]"); parent.find('span.collapser').text('[+]');
index = collapsed_fieldsets.indexOf(fieldset); index = collapsed_fieldsets.indexOf(fieldset);
if (index === -1 && fieldset !== "" && fieldset !== undefined) { if (index === -1 && fieldset !== '' && fieldset !== undefined) {
collapsed_fieldsets.push(fieldset); collapsed_fieldsets.push(fieldset);
} }
} }
$("#collapsed_fieldsets").val(collapsed_fieldsets.join("||")); $('#collapsed_fieldsets').val(collapsed_fieldsets.join('||'));
} }
px.initFavStars(); px.initFavStars();
@ -137,7 +136,7 @@ function initExploreView() {
}); });
function copyURLToClipboard(url) { function copyURLToClipboard(url) {
var textArea = document.createElement("textarea"); var textArea = document.createElement('textarea');
textArea.style.position = 'fixed'; textArea.style.position = 'fixed';
textArea.style.left = '-1000px'; textArea.style.left = '-1000px';
textArea.value = url; textArea.value = url;
@ -148,10 +147,10 @@ function initExploreView() {
try { try {
var successful = document.execCommand('copy'); var successful = document.execCommand('copy');
if (!successful) { if (!successful) {
throw new Error("Not successful"); throw new Error('Not successful');
} }
} catch (err) { } 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); document.body.removeChild(textArea);
return successful; return successful;
@ -159,12 +158,12 @@ function initExploreView() {
$('#shortner').click(function () { $('#shortner').click(function () {
$.ajax({ $.ajax({
type: "POST", type: 'POST',
url: '/r/shortner/', url: '/r/shortner/',
data: { 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 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 copy = '<a style="cursor: pointer;"><i class="fa fa-clipboard" title="Copy to clipboard" id="copy_url"></i></a>';
var spaces = '&nbsp;&nbsp;&nbsp;'; var spaces = '&nbsp;&nbsp;&nbsp;';
@ -175,14 +174,14 @@ function initExploreView() {
content: popover, content: popover,
placement: 'left', placement: 'left',
html: true, html: true,
trigger: 'manual' trigger: 'manual',
}) })
.popover('show'); .popover('show');
$('#copy_url').tooltip().click(function () { $('#copy_url').tooltip().click(function () {
var success = copyURLToClipboard(data); var success = copyURLToClipboard(data);
if (success) { 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); window.setTimeout(destroyPopover, 1200);
} }
}); });
@ -192,13 +191,13 @@ function initExploreView() {
$shortner.popover('destroy'); $shortner.popover('destroy');
} }
}, },
error: function (error) { error(error) {
showModal({ showModal({
title: "Error", title: 'Error',
body: "Sorry, an error occurred during this operation:<br/>" + 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', title: 'embed html',
placement: 'left', placement: 'left',
html: true, html: true,
trigger: 'manual' trigger: 'manual',
}) })
.popover('show'); .popover('show');
$('#copy_embed').tooltip().click(function () { $('#copy_embed').tooltip().click(function () {
var success = copyURLToClipboard(dataToCopy); var success = copyURLToClipboard(dataToCopy);
if (success) { 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); window.setTimeout(destroyPopover, 1200);
} }
}); });
@ -250,17 +249,17 @@ function initExploreView() {
function generateEmbedHTML() { function generateEmbedHTML() {
var width = $standalone_width.val(); var width = $standalone_width.val();
var height = $standalone_height.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>'; dataToCopy = dataToCopy + ' seamless frameBorder="0" scrolling="no"></iframe>';
$standalone_text.val(dataToCopy); $standalone_text.val(dataToCopy);
} }
}); });
$("#viz_type").change(function () { $('#viz_type').change(function () {
$("#query").submit(); $('#query').submit();
}); });
$("#datasource_id").change(function () { $('#datasource_id').change(function () {
var url = $(this).find('option:selected').attr('url'); var url = $(this).find('option:selected').attr('url');
window.location = url; window.location = url;
}); });
@ -278,28 +277,28 @@ function initExploreView() {
); );
} }
$(".select2").select2({ $('.select2').select2({
dropdownAutoWidth: true
});
$(".select2Sortable").select2({
dropdownAutoWidth: true
});
$(".select2-with-images").select2({
dropdownAutoWidth: true, dropdownAutoWidth: true,
dropdownCssClass: "bigdrop",
formatResult: formatViz
}); });
$(".select2Sortable").select2Sortable({ $('.select2Sortable').select2({
bindOrder: 'sortableStop' 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' }); $('[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() { function set_filters() {
["flt", "having"].forEach(function (prefix) { ['flt', 'having'].forEach(function (prefix) {
for (var i = 1; i < 10; i++) { for (var i = 1; i < 10; i++) {
var col = px.getParam(prefix + "_col_" + i); var col = px.getParam(prefix + '_col_' + i);
if (col !== '') { if (col !== '') {
add_filter(i, prefix); add_filter(i, prefix);
} }
@ -309,13 +308,13 @@ function initExploreView() {
set_filters(); set_filters();
function add_filter(i, fieldPrefix) { function add_filter(i, fieldPrefix) {
var cp = $("#"+fieldPrefix+"0").clone(); var cp = $('#' + fieldPrefix + '0').clone();
$(cp).appendTo("#" + getPanelClass(fieldPrefix) + " #filters"); $(cp).appendTo('#' + getPanelClass(fieldPrefix) + ' #filters');
$(cp).show(); $(cp).show();
if (i !== undefined) { if (i !== undefined) {
$(cp).find("#"+fieldPrefix+"_eq_0").val(px.getParam(fieldPrefix+"_eq_" + 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 + '_op_0').val(px.getParam(fieldPrefix + '_op_' + i));
$(cp).find("#"+fieldPrefix+"_col_0").val(px.getParam(fieldPrefix+"_col_" + i)); $(cp).find('#' + fieldPrefix + '_col_0').val(px.getParam(fieldPrefix + '_col_' + i));
} }
$(cp).find('select').select2(); $(cp).find('select').select2();
$(cp).find('.remove').click(function () { $(cp).find('.remove').click(function () {
@ -323,7 +322,7 @@ function initExploreView() {
}); });
} }
$(window).bind("popstate", function (event) { $(window).bind('popstate', function (event) {
// Browser back button // Browser back button
var returnLocation = history.location || document.location; var returnLocation = history.location || document.location;
// Could do something more lightweight here, but we're not optimizing // Could do something more lightweight here, but we're not optimizing
@ -331,11 +330,11 @@ function initExploreView() {
returnLocation.reload(); returnLocation.reload();
}); });
$("#filter_panel #plus").click(function () { $('#filter_panel #plus').click(function () {
add_filter(undefined, "flt"); add_filter(undefined, 'flt');
}); });
$("#having_panel #plus").click(function () { $('#having_panel #plus').click(function () {
add_filter(undefined, "having"); add_filter(undefined, 'having');
}); });
const queryAndSaveBtnsEl = document.getElementById('js-query-and-save-btns'); const queryAndSaveBtnsEl = document.getElementById('js-query-and-save-btns');
@ -354,7 +353,7 @@ function initExploreView() {
if (filtered.length === 0) { if (filtered.length === 0) {
return { return {
id: term, id: term,
text: term text: term,
}; };
} }
} }
@ -362,11 +361,11 @@ function initExploreView() {
function initSelectionToValue(element, callback) { function initSelectionToValue(element, callback) {
callback({ callback({
id: element.val(), id: element.val(),
text: element.val() text: element.val(),
}); });
} }
$(".select2_freeform").each(function () { $('.select2_freeform').each(function () {
var parent = $(this).parent(); var parent = $(this).parent();
var name = $(this).attr('name'); var name = $(this).attr('name');
var l = []; var l = [];
@ -374,7 +373,7 @@ function initExploreView() {
for (var i = 0; i < this.options.length; i++) { for (var i = 0; i < this.options.length; i++) {
l.push({ l.push({
id: this.options[i].value, id: this.options[i].value,
text: this.options[i].text text: this.options[i].text,
}); });
if (this.options[i].selected) { if (this.options[i].selected) {
selected = this.options[i].value; selected = this.options[i].value;
@ -388,14 +387,14 @@ function initExploreView() {
initSelection: initSelectionToValue, initSelection: initSelectionToValue,
dropdownAutoWidth: true, dropdownAutoWidth: true,
multiple: false, multiple: false,
data: l data: l,
}); });
$(this).remove(); $(this).remove();
}); });
function prepSaveDialog() { function prepSaveDialog() {
var setButtonsState = function () { 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') { if (add_to_dash === 'existing' || add_to_dash === 'new') {
$('.gotodash').removeAttr('disabled'); $('.gotodash').removeAttr('disabled');
} else { } else {
@ -406,25 +405,25 @@ function initExploreView() {
url += '?_flt_0_owners=' + $('#userid').val(); url += '?_flt_0_owners=' + $('#userid').val();
$.get(url, function (data) { $.get(url, function (data) {
var choices = []; 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 }); choices.push({ id: data.pks[i], text: data.result[i].dashboard_title });
} }
$('#save_to_dashboard_id').select2({ $('#save_to_dashboard_id').select2({
data: choices, data: choices,
dropdownAutoWidth: true dropdownAutoWidth: true,
}).on("select2-selecting", function () { }).on('select2-selecting', function () {
$("#add_to_dash_existing").prop("checked", true); $('#add_to_dash_existing').prop('checked', true);
setButtonsState(); setButtonsState();
}); });
}); });
$("input[name=add_to_dash]").change(setButtonsState); $('input[name=add_to_dash]').change(setButtonsState);
$("input[name='new_dashboard_name']").on('focus', function () { $("input[name='new_dashboard_name']").on('focus', function () {
$("#add_to_new_dash").prop("checked", true); $('#add_to_new_dash').prop('checked', true);
setButtonsState(); setButtonsState();
}); });
$("input[name='new_slice_name']").on('focus', function () { $("input[name='new_slice_name']").on('focus', function () {
$("#save_as_new").prop("checked", true); $('#save_as_new').prop('checked', true);
setButtonsState(); setButtonsState();
}); });
@ -444,30 +443,30 @@ function saveSlice() {
var slice_name = $('input[name=new_slice_name]').val(); var slice_name = $('input[name=new_slice_name]').val();
if (slice_name === '') { if (slice_name === '') {
showModal({ showModal({
title: "Error", title: 'Error',
body: "You must pick a name for the new slice" body: 'You must pick a name for the new slice',
}); });
return; 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(); var add_to_dash = $('input[name=add_to_dash]:checked').val();
if (add_to_dash === 'existing' && $('#save_to_dashboard_id').val() === '') { if (add_to_dash === 'existing' && $('#save_to_dashboard_id').val() === '') {
showModal({ showModal({
title: "Error", title: 'Error',
body: "You must pick an existing dashboard" body: 'You must pick an existing dashboard',
}); });
return; return;
} else if (add_to_dash === 'new' && $('input[name=new_dashboard_name]').val() === '') { } else if (add_to_dash === 'new' && $('input[name=new_dashboard_name]').val() === '') {
showModal({ showModal({
title: "Error", title: 'Error',
body: "Please enter a name for the new dashboard" body: 'Please enter a name for the new dashboard',
}); });
return; return;
} }
$('#action').val(action); $('#action').val(action);
prepForm(); prepForm();
$("#query").submit(); $('#query').submit();
} }
$(document).ready(function () { $(document).ready(function () {
@ -489,9 +488,9 @@ $(document).ready(function () {
// make checkbox inputs display as toggles // make checkbox inputs display as toggles
$(':checkbox') $(':checkbox')
.addClass('pull-right') .addClass('pull-right')
.attr("data-onstyle", "default") .attr('data-onstyle', 'default')
.bootstrapToggle({ .bootstrapToggle({
size: 'mini' size: 'mini',
}); });
$('div.toggle').addClass('pull-right'); $('div.toggle').addClass('pull-right');

View File

@ -1,18 +1,14 @@
var $ = require('jquery');
var jQuery = $;
import React from 'react'; import React from 'react';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { Jumbotron } from 'react-bootstrap'; import { Jumbotron } from 'react-bootstrap';
class App extends React.Component { function App() {
render () { return (
return ( <Jumbotron>
<Jumbotron> <h1>Caravel</h1>
<h1>Caravel</h1> <p>Extensible visualization tool for exploring data from any database.</p>
<p>Extensible visualization tool for exploring data from any database.</p> </Jumbotron>
</Jumbotron> );
);
}
} }
render(<App />, document.getElementById('app')); render(<App />, document.getElementById('app'));

View File

@ -1,11 +1,10 @@
var $ = require('jquery'); import $ from 'jquery';
var jQuery = $; const d3 = require('d3');
var d3 = require('d3'); const Mustache = require('mustache');
var Mustache = require('mustache'); const utils = require('./utils');
var utils = require('./utils');
// vis sources // vis sources
var sourceMap = { /* eslint camel-case: 0 */
const sourceMap = {
area: 'nvd3_vis.js', area: 'nvd3_vis.js',
bar: 'nvd3_vis.js', bar: 'nvd3_vis.js',
bubble: 'nvd3_vis.js', bubble: 'nvd3_vis.js',
@ -34,185 +33,247 @@ var sourceMap = {
horizon: 'horizon.js', horizon: 'horizon.js',
mapbox: 'mapbox.jsx', mapbox: 'mapbox.jsx',
}; };
const color = function () {
var color = function () {
// Color related utility functions go in this object // Color related utility functions go in this object
var bnbColors = [ const bnbColors = [
//rausch hackb kazan babu lima beach barol '#ff5a5f', // rausch
'#ff5a5f', '#7b0051', '#007A87', '#00d1c1', '#8ce071', '#ffb400', '#b4a76c', '#7b0051', // hackb
'#ff8083', '#cc0086', '#00a1b3', '#00ffeb', '#bbedab', '#ffd266', '#cbc29a', '#007A87', // kazan
'#ff3339', '#ff1ab1', '#005c66', '#00b3a5', '#55d12e', '#b37e00', '#988b4e', '#00d1c1', // babu
'#8ce071', // lima
'#ffb400', // beach
'#b4a76c', // barol
'#ff8083',
'#cc0086',
'#00a1b3',
'#00ffeb',
'#bbedab',
'#ffd266',
'#cbc29a',
'#ff3339',
'#ff1ab1',
'#005c66',
'#00b3a5',
'#55d12e',
'#b37e00',
'#988b4e',
]; ];
var spectrums = { const spectrums = {
blue_white_yellow: ['#00d1c1', 'white', '#ffb400'], blue_white_yellow: [
fire: ['white', 'yellow', 'red', 'black'], '#00d1c1',
white_black: ['white', 'black'], 'white',
black_white: ['black', 'white'], '#ffb400',
],
fire: [
'white',
'yellow',
'red',
'black',
],
white_black: [
'white',
'black',
],
black_white: [
'black',
'white',
],
}; };
var colorBnb = function () { const colorBnb = function () {
// Color factory // Color factory
var seen = {}; const seen = {};
return function (s) { return function (s) {
if (!s) { return; } if (!s) {
s = String(s); return;
// 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;
} }
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 // Returns a linear scaler our of an array of color
if (!Array.isArray(colors)) { if (!Array.isArray(colors)) {
/* eslint no-param-reassign: 0 */
colors = spectrums[colors]; colors = spectrums[colors];
} }
let ext = [
var ext = [0, 1]; 0,
1,
];
if (data !== undefined) { if (data !== undefined) {
ext = d3.extent(data, accessor); ext = d3.extent(data, accessor);
} }
const points = [];
var points = []; const chunkSize = (ext[1] - ext[0]) / colors.length;
var chunkSize = (ext[1] - ext[0]) / colors.length; $.each(colors, function (i) {
$.each(colors, function (i, c) {
points.push(i * chunkSize); points.push(i * chunkSize);
}); });
return d3.scale.linear().domain(points).range(colors); return d3.scale.linear().domain(points).range(colors);
}; };
return { return {
bnbColors: bnbColors, bnbColors,
category21: colorBnb(), category21: colorBnb(),
colorScalerFactory: colorScalerFactory, colorScalerFactory,
}; };
}; };
/* eslint wrap-iife: 0*/
var px = (function () { const px = function () {
const visualizations = {};
var visualizations = {}; let slice;
var slice;
function getParam(name) { function getParam(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
results = regex.exec(location.search); const results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
} }
function UTC(dttm) { 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([ const tickMultiFormat = d3.time.format.multi([
[".%L", function (d) { [
return d.getMilliseconds(); '.%L',
}], // If there are millisections, show only them function (d) {
[":%S", function (d) { return d.getMilliseconds();
return d.getSeconds(); },
}], // If there are seconds, show only them ],
["%a %b %d, %I:%M %p", function (d) { // If there are millisections, show only them
return d.getMinutes() !== 0; [
}], // If there are non-zero minutes, show Date, Hour:Minute [AM/PM] ':%S',
["%a %b %d, %I %p", function (d) { function (d) {
return d.getHours() !== 0; return d.getSeconds();
}], // If there are hours that are multiples of 3, show date and AM/PM },
["%a %b %d", function (d) { ],
return d.getDate() !== 1; // If there are seconds, show only them
}], // If not the first of the month, do "month day, year." [
["%B %Y", function (d) { '%a %b %d, %I:%M %p',
return d.getMonth() !== 0 && d.getDate() === 1; function (d) {
}], // If the first of the month, do "month day, year." return d.getMinutes() !== 0;
["%Y", function (d) { },
return true; ],
}], // fall back on month, year // 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) { function formatDate(dttm) {
var d = UTC(new Date(dttm)); const d = UTC(new Date(dttm));
//d = new Date(d.getTime() - 1 * 60 * 60 * 1000); // d = new Date(d.getTime() - 1 * 60 * 60 * 1000);
return tickMultiFormat(d); return tickMultiFormat(d);
} }
function timeFormatFactory(d3timeFormat) { function timeFormatFactory(d3timeFormat) {
var f = d3.time.format(d3timeFormat); const f = d3.time.format(d3timeFormat);
return function (dttm) { return function (dttm) {
var d = UTC(new Date(dttm)); const d = UTC(new Date(dttm));
return f(d); return f(d);
}; };
} }
function initFavStars() { function initFavStars() {
var baseUrl = '/caravel/favstar/'; const baseUrl = '/caravel/favstar/';
// Init star behavihor for favorite // Init star behavihor for favorite
function show() { function show() {
if ($(this).hasClass('selected')) { if ($(this).hasClass('selected')) {
$(this).html('<i class="fa fa-star"></i>'); $(this).html('<i class="fa fa-star"></i>');
} else { } else {
$(this).html('<i class="fa fa-star-o"></i>'); $(this).html('<i class="fa fa-star-o"></i>');
}
} }
$('.favstar') }
.attr('title', 'Click to favorite/unfavorite') $('.favstar')
.each(show) .attr('title', 'Click to favorite/unfavorite')
.each(function () { .each(show)
var url = baseUrl + $(this).attr("class_name"); .each(function () {
var star = this; let url = baseUrl + $(this).attr('class_name');
url += '/' + $(this).attr("obj_id") + '/'; const star = this;
$.getJSON(url + 'count/', function (data) { url += '/' + $(this).attr('obj_id') + '/';
if (data.count > 0) { $.getJSON(url + 'count/', function (data) {
$(star) if (data.count > 0) {
.addClass('selected') $(star).addClass('selected').each(show);
.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/';
} }
$.get(url); });
$(this).each(show); })
}) .click(function () {
.tooltip(); $(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();
} }
const Slice = function (data, dashboard) {
var Slice = function (data, dashboard) { let timer;
var timer; const token = $('#' + data.token);
var token = $('#' + data.token); const containerId = data.token + '_con';
var container_id = data.token + '_con'; const selector = '#' + containerId;
var selector = '#' + container_id; const container = $(selector);
var container = $(selector); const sliceId = data.sliceId;
var slice_id = data.slice_id; let dttm = 0;
var dttm = 0; const stopwatch = function () {
var stopwatch = function () {
dttm += 10; dttm += 10;
var num = dttm / 1000; const num = dttm / 1000;
$('#timer').text(num.toFixed(2) + " sec"); $('#timer').text(num.toFixed(2) + ' sec');
}; };
var qrystr = ''; let qrystr = '';
var always = function (data) { const always = function () {
//Private f, runs after done and error // Private f, runs after done and error
clearInterval(timer); clearInterval(timer);
$('#timer').removeClass('btn-warning'); $('#timer').removeClass('btn-warning');
}; };
slice = { slice = {
data: data, data,
container: container, container,
container_id: container_id, containerId,
selector: selector, selector,
querystring: function (params) { querystring(params) {
params = params || {}; params = params || {};
var parser = document.createElement('a'); const parser = document.createElement('a');
parser.href = data.json_endpoint; parser.href = data.json_endpoint;
if (dashboard !== undefined) { if (dashboard !== undefined) {
var flts = params.extraFilters === false ? const flts =
'' : encodeURIComponent(JSON.stringify(dashboard.filters)); params.extraFilters === false ? '' :
qrystr = parser.search + "&extra_filters=" + flts; encodeURIComponent(JSON.stringify(dashboard.filters));
qrystr = parser.search + '&extra_filters=' + flts;
} else if ($('#query').length === 0) { } else if ($('#query').length === 0) {
qrystr = parser.search; qrystr = parser.search;
} else { } else {
@ -220,69 +281,66 @@ var px = (function () {
} }
return qrystr; return qrystr;
}, },
getWidgetHeader: function () { getWidgetHeader() {
return this.container.parents("div.widget").find(".chart-header"); return this.container.parents('div.widget').find('.chart-header');
}, },
render_template: function (s) { render_template(s) {
var context = { const context = {
width: this.width, width: this.width,
height: this.height, height: this.height,
}; };
return Mustache.render(s, context); return Mustache.render(s, context);
}, },
jsonEndpoint: function (params) { jsonEndpoint(params) {
params = params || {}; params = params || {};
var parser = document.createElement('a'); const parser = document.createElement('a');
parser.href = data.json_endpoint; parser.href = data.json_endpoint;
var endpoint = parser.pathname + this.querystring({ let endpoint = parser.pathname + this.querystring({ extraFilters: params.extraFilters });
extraFilters: params.extraFilters, endpoint += '&json=true';
}); endpoint += '&force=' + this.force;
endpoint += "&json=true";
endpoint += "&force=" + this.force;
return endpoint; return endpoint;
}, },
d3format: function (col, number) { d3format(col, number) {
// uses the utils memoized d3format function and formats based on // uses the utils memoized d3format function and formats based on
// column level defined preferences // column level defined preferences
var format = this.data.column_formats[col]; const format = this.data.column_formats[col];
return utils.d3format(format, number); return utils.d3format(format, number);
}, },
done: function (data) { /* eslint no-shadow: 0 */
done(data) {
clearInterval(timer); clearInterval(timer);
token.find("img.loading").hide(); token.find('img.loading').hide();
container.show(); container.show();
let cachedSelector = null;
var cachedSelector = null;
if (dashboard === undefined) { if (dashboard === undefined) {
cachedSelector = $('#is_cached'); cachedSelector = $('#is_cached');
if (data !== undefined && data.is_cached) { if (data !== undefined && data.is_cached) {
cachedSelector 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() .show()
.tooltip('fixTitle'); .tooltip('fixTitle');
} else { } else {
cachedSelector.hide(); cachedSelector.hide();
} }
} else { } else {
var refresh = this.getWidgetHeader().find('.refresh'); const refresh = this.getWidgetHeader().find('.refresh');
if (data !== undefined && data.is_cached) { if (data !== undefined && data.is_cached) {
refresh refresh
.addClass('danger') .addClass('danger')
.attr( .attr('title',
'title', 'Served from data cached at ' + data.cached_dttm +
'Served from data cached at ' + data.cached_dttm + '. Click to force-refresh') '. Click to force-refresh')
.tooltip('fixTitle'); .tooltip('fixTitle');
} else { } else {
refresh refresh
.removeClass('danger') .removeClass('danger')
.attr( .attr('title', 'Click to force-refresh')
'title', .tooltip('fixTitle');
'Click to force-refresh')
.tooltip('fixTitle');
} }
} }
if (data !== undefined) { if (data !== undefined) {
$("#query_container").html(data.query); $('#query_container').html(data.query);
} }
$('#timer').removeClass('btn-warning'); $('#timer').removeClass('btn-warning');
$('#timer').addClass('btn-success'); $('#timer').addClass('btn-success');
@ -297,26 +355,26 @@ var px = (function () {
$('.query-and-save button').removeAttr('disabled'); $('.query-and-save button').removeAttr('disabled');
always(data); always(data);
}, },
getErrorMsg: function (xhr) { getErrorMsg(xhr) {
if (xhr.statusText === "timeout") { if (xhr.statusText === 'timeout') {
return "The request timed out"; return 'The request timed out';
} }
var msg = ""; let msg = '';
if (!xhr.responseText) { if (!xhr.responseText) {
var status = xhr.status; const status = xhr.status;
msg += "An unknown error occurred. (Status: " + status + ")"; msg += 'An unknown error occurred. (Status: ' + status + ')';
if (status === 0) { if (status === 0) {
// This may happen when the worker in gunicorn times out // This may happen when the worker in gunicorn times out
msg += " Maybe the request timed out?"; msg += ' Maybe the request timed out?';
} }
} }
return msg; return msg;
}, },
error: function (msg, xhr) { error(msg, xhr) {
token.find("img.loading").hide(); token.find('img.loading').hide();
var err = msg ? ('<div class="alert alert-danger">' + msg + '</div>') : ""; let err = msg ? '<div class="alert alert-danger">' + msg + '</div>' : '';
if (xhr) { if (xhr) {
var extendedMsg = this.getErrorMsg(xhr); const extendedMsg = this.getErrorMsg(xhr);
if (extendedMsg) { if (extendedMsg) {
err += '<div class="alert alert-danger">' + extendedMsg + '</div>'; err += '<div class="alert alert-danger">' + extendedMsg + '</div>';
} }
@ -329,35 +387,35 @@ var px = (function () {
$('.query-and-save button').removeAttr('disabled'); $('.query-and-save button').removeAttr('disabled');
always(data); always(data);
}, },
width: function () { width() {
return token.width(); return token.width();
}, },
height: function () { height() {
var others = 0; let others = 0;
var widget = container.parents('.widget'); const widget = container.parents('.widget');
var slice_description = widget.find('.slice_description'); const sliceDescription = widget.find('.sliceDescription');
if (slice_description.is(":visible")) { if (sliceDescription.is(':visible')) {
others += widget.find('.slice_description').height() + 25; others += widget.find('.sliceDescription').height() + 25;
} }
others += widget.find('.chart-header').height(); others += widget.find('.chart-header').height();
return widget.height() - others - 10; return widget.height() - others - 10;
}, },
bindResizeToWindowResize: function () { bindResizeToWindowResize() {
var resizeTimer; let resizeTimer;
var slice = this; const slice = this;
$(window).on('resize', function (e) { $(window).on('resize', function () {
clearTimeout(resizeTimer); clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () { resizeTimer = setTimeout(function () {
slice.resize(); slice.resize();
}, 500); }, 500);
}); });
}, },
render: function (force) { render(force) {
if (force === undefined) { if (force === undefined) {
force = false; force = false;
} }
this.force = force; this.force = force;
token.find("img.loading").show(); token.find('img.loading').show();
container.css('height', this.height()); container.css('height', this.height());
dttm = 0; dttm = 0;
timer = setInterval(stopwatch, 10); timer = setInterval(stopwatch, 10);
@ -365,67 +423,65 @@ var px = (function () {
$('#timer').addClass('btn-warning'); $('#timer').addClass('btn-warning');
this.viz.render(); this.viz.render();
}, },
resize: function () { resize() {
token.find("img.loading").show(); token.find('img.loading').show();
container.css('height', this.height()); container.css('height', this.height());
this.viz.render(); this.viz.render();
this.viz.resize(); this.viz.resize();
}, },
addFilter: function (col, vals) { addFilter(col, vals) {
if (dashboard !== undefined) { if (dashboard !== undefined) {
dashboard.addFilter(slice_id, col, vals); dashboard.addFilter(sliceId, col, vals);
} }
}, },
setFilter: function (col, vals) { setFilter(col, vals) {
if (dashboard !== undefined) { if (dashboard !== undefined) {
dashboard.setFilter(slice_id, col, vals); dashboard.setFilter(sliceId, col, vals);
} }
}, },
getFilters: function (col, vals) { getFilters() {
if (dashboard !== undefined) { 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) { if (dashboard !== undefined) {
delete dashboard.clearFilter(slice_id); dashboard.removeFilter(sliceId, col, vals);
}
},
removeFilter: function (col, vals) {
if (dashboard !== undefined) {
delete dashboard.removeFilter(slice_id, col, vals);
} }
}, },
}; };
var visType = data.form_data.viz_type; const visType = data.form_data.viz_type;
px.registerViz(visType); px.registerViz(visType);
slice.viz = visualizations[data.form_data.viz_type](slice); slice.viz = visualizations[data.form_data.viz_type](slice);
return slice; return slice;
}; };
function registerViz(name) { function registerViz(name) {
var visSource = sourceMap[name]; const visSource = sourceMap[name];
if (visSource) { if (visSource) {
var visFactory = require('../../visualizations/' + visSource); /* eslint global-require: 0 */
const visFactory = require('../../visualizations/' + visSource);
if (typeof visFactory === 'function') { if (typeof visFactory === 'function') {
visualizations[name] = visFactory; visualizations[name] = visFactory;
} }
} else { } else {
throw new Error("require(" + name + ") failed."); throw new Error('require(' + name + ') failed.');
} }
} }
// Export public functions // Export public functions
return { return {
registerViz: registerViz,
Slice: Slice,
formatDate: formatDate,
timeFormatFactory: timeFormatFactory,
color: color(), color: color(),
getParam: getParam, formatDate,
initFavStars: initFavStars, getParam,
initFavStars,
registerViz,
Slice,
timeFormatFactory,
}; };
})(); }();
module.exports = px; module.exports = px;

View File

@ -1,56 +1,51 @@
var $ = require('jquery'); const $ = require('jquery');
var d3 = require('d3'); const d3 = require('d3');
/* /*
Utility function that takes a d3 svg:text selection and a max width, and splits the 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 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) { function wrapSvgText(text, width, adjustedY) {
var lineHeight = 1; // ems const lineHeight = 1;
// ems
text.each(function () { text.each(function () {
var text = d3.select(this), const d3Text = d3.select(this);
words = text.text().split(/\s+/), const words = d3Text.text().split(/\s+/);
word, let word;
line = [], let line = [];
lineNumber = 0, let lineNumber = 0;
x = text.attr("x"), const x = d3Text.attr('x');
y = text.attr("y"), const y = d3Text.attr('y');
dy = parseFloat(text.attr("dy")), const dy = parseFloat(d3Text.attr('dy'));
tspan = text.text(null) let tspan =
.append("tspan") d3Text.text(null).append('tspan').attr('x', x)
.attr("x", x) .attr('y', y)
.attr("y", y) .attr('dy', dy + 'em');
.attr("dy", dy + "em");
var didWrap = false; let didWrap = false;
for (let i = 0; i < words.length; i++) {
for (var i = 0; i < words.length; i++) {
word = words[i]; word = words[i];
line.push(word); line.push(word);
tspan.text(line.join(" ")); tspan.text(line.join(' '));
if (tspan.node().getComputedTextLength() > width) { if (tspan.node().getComputedTextLength() > width) {
line.pop(); // remove word that pushes over the limit line.pop();
tspan.text(line.join(" ")); // remove word that pushes over the limit
tspan.text(line.join(' '));
line = [word]; line = [word];
tspan = text.append("tspan") tspan =
.attr("x", x) d3Text.append('tspan').attr('x', x).attr('y', y)
.attr("y", y) .attr('dy', ++lineNumber * lineHeight + dy + 'em')
.attr("dy", ++lineNumber * lineHeight + dy + "em") .text(word);
.text(word);
didWrap = true; didWrap = true;
} }
} }
if (!didWrap && typeof adjustedY !== "undefined") { if (!didWrap && typeof adjustedY !== 'undefined') {
tspan.attr("y", adjustedY); tspan.attr('y', adjustedY);
} }
}); });
} }
/** /**
* Sets the body and title content of a modal, and shows it. Assumes HTML for modal exists and that * 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) * it handles closing (i.e., works with bootstrap)
@ -65,47 +60,41 @@ function wrapSvgText(text, width, adjustedY) {
* } * }
*/ */
function showModal(options) { function showModal(options) {
options.modalSelector = options.modalSelector || ".misc-modal"; /* eslint no-param-reassign: 0 */
options.titleSelector = options.titleSelector || ".misc-modal .modal-title"; options.modalSelector = options.modalSelector || '.misc-modal';
options.bodySelector = options.bodySelector || ".misc-modal .modal-body"; options.titleSelector = options.titleSelector || '.misc-modal .modal-title';
options.bodySelector = options.bodySelector || '.misc-modal .modal-body';
$(options.titleSelector).html(options.title || ""); $(options.titleSelector).html(options.title || '');
$(options.bodySelector).html(options.body || ""); $(options.bodySelector).html(options.body || '');
$(options.modalSelector).modal("show"); $(options.modalSelector).modal('show');
} }
const showApiMessage = function (resp) {
var showApiMessage = function (resp) { const template =
var template = '<div class="alert"> ' + '<div class="alert"> ' +
'<button type="button" class="close" ' + '<button type="button" class="close" ' +
'data-dismiss="alert">×</button> </div>'; 'data-dismiss="alert">\xD7</button> </div>';
const severity = resp.severity || 'info';
var severity = resp.severity || 'info'; $(template).addClass('alert-' + severity)
$(template) .append(resp.message)
.addClass('alert-' + severity) .appendTo($('#alert-container'));
.append(resp.message)
.appendTo($('#alert-container'));
}; };
const toggleCheckbox = function (apiUrlPrefix, selector) {
var toggleCheckbox = function (apiUrlPrefix, selector) { const apiUrl = apiUrlPrefix + $(selector)[0].checked;
var apiUrl = apiUrlPrefix + $(selector)[0].checked; $.get(apiUrl).fail(function (xhr) {
$.get(apiUrl).fail(function (xhr, textStatus, errorThrown) { const resp = xhr.responseJSON;
var resp = xhr.responseJSON;
if (resp && resp.message) { if (resp && resp.message) {
showApiMessage(resp); showApiMessage(resp);
} }
}); });
}; };
/** /**
* Fix the height of the table body of a DataTable with scrollY set * Fix the height of the table body of a DataTable with scrollY set
*/ */
var fixDataTableBodyHeight = function ($tableDom, height) { const fixDataTableBodyHeight = function ($tableDom, height) {
var headHeight = $tableDom.find('.dataTables_scrollHead').height(); const headHeight = $tableDom.find('.dataTables_scrollHead').height();
$tableDom.find('.dataTables_scrollBody') $tableDom.find('.dataTables_scrollBody').css('max-height', height - headHeight);
.css('max-height', height - headHeight);
}; };
const formatters = {};
var formatters = {};
function d3format(format, number) { function d3format(format, number) {
// Formats a number and memoizes formatters to be reused // Formats a number and memoizes formatters to be reused
format = format || '.3s'; format = format || '.3s';
@ -114,11 +103,10 @@ function d3format(format, number) {
} }
return formatters[format](number); return formatters[format](number);
} }
module.exports = { module.exports = {
wrapSvgText: wrapSvgText, d3format,
showModal: showModal, fixDataTableBodyHeight,
toggleCheckbox: toggleCheckbox, showModal,
fixDataTableBodyHeight: fixDataTableBodyHeight, toggleCheckbox,
d3format: d3format, wrapSvgText,
}; };

View File

@ -1,93 +1,80 @@
var $ = window.$ = require('jquery'); const $ = window.$ = require('jquery');
var jQuery = window.jQuery = $; const showModal = require('./modules/utils.js').showModal;
var showModal = require('./modules/utils.js').showModal;
require('./caravel-select2.js'); require('./caravel-select2.js');
require('datatables.net-bs'); require('datatables.net-bs');
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css'); require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
require('bootstrap'); require('bootstrap');
const ace = require('brace');
var ace = require('brace');
require('brace/mode/sql'); require('brace/mode/sql');
require('brace/theme/crimson_editor'); require('brace/theme/crimson_editor');
require('../stylesheets/sql.css'); require('../stylesheets/sql.css');
$(document).ready(function () { $(document).ready(function () {
function getParam(name) { function getParam(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); /* eslint no-param-reassign: 0 */
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
results = regex.exec(location.search); const regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); const results = regex.exec(location.search);
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
} }
function initSqlEditorView() { function initSqlEditorView() {
var database_id = $('#database_id').val(); const databaseId = $('#databaseId').val();
var editor = ace.edit("sql"); const editor = ace.edit('sql');
editor.$blockScrolling = Infinity; editor.$blockScrolling = Infinity;
editor.getSession().setUseWrapMode(true); editor.getSession().setUseWrapMode(true);
$('#sql').hide(); $('#sql').hide();
editor.setTheme("ace/theme/crimson_editor"); editor.setTheme('ace/theme/crimson_editor');
editor.setOptions({ editor.setOptions({
minLines: 16, minLines: 16,
maxLines: Infinity, maxLines: Infinity,
}); });
editor.getSession().setMode("ace/mode/sql"); editor.getSession().setMode('ace/mode/sql');
editor.focus(); editor.focus();
$("select").select2({
dropdownAutoWidth: true, $('select').select2({ dropdownAutoWidth: true });
});
function showTableMetadata() { function showTableMetadata() {
$(".metadata").load( $('.metadata').load('/caravel/table/' + databaseId + '/' + $('#dbtable').val() + '/');
'/caravel/table/' + database_id + '/' + $("#dbtable").val() + '/');
} }
$("#dbtable").on("change", showTableMetadata); $('#dbtable').on('change', showTableMetadata);
showTableMetadata(); showTableMetadata();
$("#create_view").click(function () { $('#create_view').click(function () {
showModal({ showModal({
title: "Error", title: 'Error',
body: "Sorry, this feature is not yet implemented", body: 'Sorry, this feature is not yet implemented',
}); });
}); });
$(".sqlcontent").show(); $('.sqlcontent').show();
function selectStarOnClick() { function selectStarOnClick() {
$.ajax('/caravel/select_star/' + database_id + '/' + $("#dbtable").val() + '/') const url = '/caravel/select_star/' + databaseId + '/' + $('#dbtable').val() + '/';
.done(function (msg) { $.ajax(url).done(function (msg) {
editor.setValue(msg); editor.setValue(msg);
}); });
} }
$('#select_star').click(selectStarOnClick);
$("#select_star").click(selectStarOnClick);
editor.setValue(getParam('sql')); editor.setValue(getParam('sql'));
$(window).bind("popstate", function (event) { $(window).bind('popstate', function () {
// Could do something more lightweight here, but we're not optimizing // Could do something more lightweight here, but we're not optimizing
// for the use of the back button anyways // for the use of the back button anyways
editor.setValue(getParam('sql')); editor.setValue(getParam('sql'));
$("#run").click(); $('#run').click();
}); });
$("#run").click(function () { $('#run').click(function () {
$('#results').hide(0); $('#results').hide(0);
$('#loading').show(0); $('#loading').show(0);
history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue())); history.pushState({}, document.title, '?sql=' + encodeURIComponent(editor.getValue()));
$.ajax({ $.ajax({
type: "POST", type: 'POST',
url: '/caravel/runsql/', url: '/caravel/runsql/',
data: { data: {
data: JSON.stringify({ data: JSON.stringify({
database_id: $('#database_id').val(), databaseId: $('#databaseId').val(),
sql: editor.getSession().getValue(), sql: editor.getSession().getValue(),
}), }),
}, },
success: function (data) { success(data) {
$('#loading').hide(0); $('#loading').hide(0);
$('#results').show(0); $('#results').show(0);
$('#results').html(data); $('#results').html(data);
$('table.sql_results').DataTable({ $('table.sql_results').DataTable({
retrieve: true, retrieve: true,
paging: false, paging: false,
@ -95,7 +82,7 @@ $(document).ready(function () {
aaSorting: [], aaSorting: [],
}); });
}, },
error: function (err, err2) { error(err) {
$('#loading').hide(0); $('#loading').hide(0);
$('#results').show(0); $('#results').show(0);
$('#results').html(err.responseText); $('#results').html(err.responseText);

View File

@ -1,13 +1,13 @@
var $ = window.$ = require('jquery'); const $ = window.$ = require('jquery');
var jQuery = window.jQuery = $; /* eslint no-unused-vars: 0 */
var px = require('./modules/caravel.js'); const jQuery = window.jQuery = $;
const px = require('./modules/caravel.js');
require('bootstrap'); require('bootstrap');
$(document).ready(function () { $(document).ready(function () {
var slice; const data = $('.slice').data('slice');
var data = $('.slice').data('slice'); const slice = px.Slice(data);
slice = px.Slice(data);
slice.render(); slice.render();
slice.bindResizeToWindowResize(); slice.bindResizeToWindowResize();
}); });

View File

@ -1,32 +1,30 @@
var $ = window.$ = require('jquery'); const $ = window.$ = require('jquery');
var jQuery = window.jQuery = $; /* eslint no-unused-vars: 0 */
const jQuery = window.jQuery = $;
require('../stylesheets/welcome.css'); require('../stylesheets/welcome.css');
require('bootstrap'); require('bootstrap');
require('datatables.net-bs'); require('datatables.net-bs');
require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css'); require('../node_modules/datatables-bootstrap3-plugin/media/css/datatables-bootstrap3.css');
require('../node_modules/cal-heatmap/cal-heatmap.css'); require('../node_modules/cal-heatmap/cal-heatmap.css');
var d3 = require('d3'); const d3 = require('d3');
const CalHeatMap = require('cal-heatmap');
var CalHeatMap = require('cal-heatmap');
function modelViewTable(selector, modelView, orderCol, order) { function modelViewTable(selector, modelView, orderCol, order) {
// Builds a dataTable from a flask appbuilder api endpoint // 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 += '?_oc_' + modelView + '=' + orderCol;
url += '&_od_' + modelView +'=' + order; url += '&_od_' + modelView + '=' + order;
$.getJSON(url, function (data) { $.getJSON(url, function (data) {
var tableData = jQuery.map(data.result, function (el, i) { const tableData = $.map(data.result, function (el) {
var row = $.map(data.list_columns, function (col, i) { const row = $.map(data.list_columns, function (col) {
return el[col]; return el[col];
}); });
return [row]; 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] }; return { sTitle: data.label_columns[col] };
}); });
var panel = $(selector).parents('.panel'); const panel = $(selector).parents('.panel');
panel.find("img.loading").remove(); panel.find('img.loading').remove();
$(selector).DataTable({ $(selector).DataTable({
aaData: tableData, aaData: tableData,
aoColumns: cols, aoColumns: cols,
@ -37,32 +35,28 @@ function modelViewTable(selector, modelView, orderCol, order) {
searching: true, searching: true,
bInfo: false, bInfo: false,
}); });
// Hack to move the searchbox in the right spot // 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.addClass('form-control').detach();
search.appendTo(panel.find(".search")); search.appendTo(panel.find('.search'));
panel.find('.dataTables_filter').remove(); panel.find('.dataTables_filter').remove();
// Hack to display the page navigator properly // Hack to display the page navigator properly
panel.find('.col-sm-5').remove(); 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.removeClass('col-sm-7');
nav.addClass('col-sm-12'); nav.addClass('col-sm-12');
$(selector).slideDown(); $(selector).slideDown();
$('[data-toggle="tooltip"]').tooltip({ container: 'body' }); $('[data-toggle="tooltip"]').tooltip({ container: 'body' });
}); });
} }
$(document).ready(function () { $(document).ready(function () {
d3.json('/caravel/activity_per_day', function (json) { d3.json('/caravel/activity_per_day', function (json) {
var ext = d3.extent(d3.values(json)); const ext = d3.extent(d3.values(json));
var cal = new CalHeatMap(); const cal = new CalHeatMap();
var range = 10; const range = 10;
var legendBounds = []; const legendBounds = [];
var step = (ext[1] - ext[0]) / (range - 1); const step = (ext[1] - ext[0]) / (range - 1);
for (var i = 0; i< range; i++) { for (let i = 0; i < range; i++) {
legendBounds.push(i * step + ext[0]); legendBounds.push(i * step + ext[0]);
} }
cal.init({ cal.init({
@ -70,10 +64,14 @@ $(document).ready(function () {
range: 13, range: 13,
data: json, data: json,
legend: legendBounds, legend: legendBounds,
legendColors: ['#D6E685', '#1E6823'], // Based on github's colors legendColors: [
domain: "month", '#D6E685',
subDomain: "day", '#1E6823',
itemName: "action", ],
// Based on github's colors
domain: 'month',
subDomain: 'day',
itemName: 'action',
tooltip: true, tooltip: true,
}); });
}); });

View File

@ -7,7 +7,7 @@
"test": "spec" "test": "spec"
}, },
"scripts": { "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", "dev": "webpack -d --watch --colors",
"prod": "webpack -p --colors", "prod": "webpack -p --colors",
"lint": "npm run --silent lint:js", "lint": "npm run --silent lint:js",
@ -81,7 +81,11 @@
"chai": "^3.5.0", "chai": "^3.5.0",
"css-loader": "^0.23.1", "css-loader": "^0.23.1",
"enzyme": "^2.0.0", "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", "exports-loader": "^0.6.3",
"file-loader": "^0.8.5", "file-loader": "^0.8.5",
"imports-loader": "^0.6.5", "imports-loader": "^0.6.5",

View File

@ -2,9 +2,9 @@
require('babel-register')(); 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.document = jsdom('');
global.window = document.defaultView; global.window = document.defaultView;
@ -16,7 +16,7 @@ Object.keys(document.defaultView).forEach((property) => {
}); });
global.navigator = { global.navigator = {
serAgent: 'node.js', userAgent: 'node.js',
}; };
documentRef = document; documentRef = document;

View File

@ -1,3 +1,4 @@
/* eslint global-require: 0 */
const d3 = window.d3 || require('d3'); const d3 = window.d3 || require('d3');
export const EARTH_CIRCUMFERENCE_KM = 40075.16; export const EARTH_CIRCUMFERENCE_KM = 40075.16;
@ -23,5 +24,5 @@ export function isNumeric(num) {
export function rgbLuminance(r, g, b) { export function rgbLuminance(r, g, b) {
// Formula: https://en.wikipedia.org/wiki/Relative_luminance // 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);
} }

View File

@ -1,7 +1,10 @@
const path = require('path'); const path = require('path');
const APP_DIR = path.resolve(__dirname, './'); // input // input dir
const BUILD_DIR = path.resolve(__dirname, './javascripts/dist'); // output const APP_DIR = path.resolve(__dirname, './');
// output dir
const BUILD_DIR = path.resolve(__dirname, './javascripts/dist');
const config = { const config = {
entry: { entry: {
@ -18,10 +21,12 @@ const config = {
filename: '[name].entry.js', filename: '[name].entry.js',
}, },
resolve: { resolve: {
extensions: ['', '.js', '.jsx'], extensions: [
alias: { '',
webworkify: 'webworkify-webpack', '.js',
}, '.jsx',
],
alias: { webworkify: 'webworkify-webpack' },
}, },
module: { module: {
loaders: [ loaders: [
@ -30,40 +35,53 @@ const config = {
exclude: APP_DIR + '/node_modules', exclude: APP_DIR + '/node_modules',
loader: 'babel', loader: 'babel',
query: { query: {
presets: ['airbnb', 'es2015', 'react'], presets: [
'airbnb',
'es2015',
'react',
],
}, },
}, },
/* for react-map-gl overlays */ /* for react-map-gl overlays */
{ {
test: /\.react\.js$/, test: /\.react\.js$/,
include: APP_DIR + '/node_modules/react-map-gl/src/overlays', include: APP_DIR + '/node_modules/react-map-gl/src/overlays',
loader: 'babel', loader: 'babel',
}, },
/* for require('*.css') */ /* for require('*.css') */
{ {
test: /\.css$/, test: /\.css$/,
include: APP_DIR, include: APP_DIR,
loader: "style-loader!css-loader", loader: 'style-loader!css-loader',
}, },
/* for css linking images */ /* for css linking images */
{ test: /\.png$/, loader: "url-loader?limit=100000" }, {
{ test: /\.jpg$/, loader: "file-loader" }, test: /\.png$/,
{ test: /\.gif$/, loader: "file-loader" }, loader: 'url-loader?limit=100000',
},
{
test: /\.jpg$/,
loader: 'file-loader',
},
{
test: /\.gif$/,
loader: 'file-loader',
},
/* for font-awesome */ /* 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') */ /* for require('*.less') */
{ {
test: /\.less$/, test: /\.less$/,
include: APP_DIR, include: APP_DIR,
loader: "style!css!less", loader: 'style!css!less',
}, },
/* for mapbox */ /* for mapbox */
{ {
test: /\.json$/, test: /\.json$/,
@ -88,5 +106,4 @@ const config = {
}, },
plugins: [], plugins: [],
}; };
module.exports = config; module.exports = config;