diff --git a/.eslintrc.json b/.eslintrc.json index 871a0b38..b6e04f6e 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -13,6 +13,8 @@ "rules": { "prettier/prettier": "off", "vue/require-default-prop": "off", + "vue/prop-name-casing": "off", + "vue/name-property-casing": "off", "@typescript-eslint/camelcase": ["off"], "@typescript-eslint/explicit-function-return-type": ["off"], "@typescript-eslint/no-var-requires": ["off"], diff --git a/package.json b/package.json index 1779090a..3bc58938 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test": "jest --rootDir=src", "test:unit": "vue-cli-service test:unit", "test:e2e": "vue-cli-service test:e2e", - "lint": "vue-cli-service lint --no-fix-warnings", + "lint": "vue-cli-service lint", "lint_old": "eslint --ignore-path=.gitignore --ext .vue,.js,.ts ." }, "devDependencies": { diff --git a/src/components/ErrorBoundary.vue b/src/components/ErrorBoundary.vue index 0f9d51a0..3a0ea20c 100644 --- a/src/components/ErrorBoundary.vue +++ b/src/components/ErrorBoundary.vue @@ -16,7 +16,7 @@ export default { errorCaptured (err, vm, info) { //console.error("Error captured!"); //console.error(err, vm, info); - let msg = (err.name && err.message) ? (err.name + ": " + err.message) : err; + const msg = (err.name && err.message) ? (err.name + ": " + err.message) : err; this.errors.push({ msg: msg, time: new Date().toISOString(), diff --git a/src/components/Header.vue b/src/components/Header.vue index 3458649b..8f2a3e31 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -100,7 +100,7 @@ import 'vue-awesome/icons/desktop'; import _ from 'lodash'; // Set this to true to test Android behavior when on a desktop -let testingAndroid = false; +const testingAndroid = false; export default { name: 'Header', @@ -112,8 +112,8 @@ export default { }; }, mounted: async function() { - let buckets = await this.$aw.getBuckets(); - let types_by_host = {}; + const buckets = await this.$aw.getBuckets(); + const types_by_host = {}; _.each(buckets, (v) => { types_by_host[v.hostname] = types_by_host[v.hostname] || {}; // The '&& true;' is just to typecoerce into booleans diff --git a/src/components/SummaryView.vue b/src/components/SummaryView.vue index bccd1e1c..f6292c55 100644 --- a/src/components/SummaryView.vue +++ b/src/components/SummaryView.vue @@ -43,8 +43,8 @@ export default { methods: { query: async function() { - var periods = [this.period]; - var q = query.summaryQuery(this.windowBucketId, this.afkBucketId, this.limit); + const periods = [this.period]; + const q = query.summaryQuery(this.windowBucketId, this.afkBucketId, this.limit); let data = await this.$aw.query(periods, q); console.log(data); data = data[0]; diff --git a/src/mixins/asyncErrorCaptured.js b/src/mixins/asyncErrorCaptured.js index 3de97e25..e95db223 100644 --- a/src/mixins/asyncErrorCaptured.js +++ b/src/mixins/asyncErrorCaptured.js @@ -8,20 +8,20 @@ function handleError(error, vm, info) { let cur = vm; while ((cur = cur.$parent)) { - var hooks = cur.$options.errorCaptured || []; - for (let hook of hooks) if (hook.call(cur, error, vm, info) === false) break; + const hooks = cur.$options.errorCaptured || []; + for (const hook of hooks) if (hook.call(cur, error, vm, info) === false) break; } } export default { beforeCreate: function() { - var _self = this; - var methods = this.$options.methods || {}; + const _self = this; + const methods = this.$options.methods || {}; for (var key in methods) { var original = methods[key]; methods[key] = function() { try { - var result = original.apply(this, arguments); + const result = original.apply(this, arguments); // let's analyse what is returned from the method if (result && typeof result.then === 'function' && typeof result.catch === 'function') { // this looks like a Promise. let's handle it's errors: diff --git a/src/queries.js b/src/queries.js index fbbc3bfc..0aca70c3 100644 --- a/src/queries.js +++ b/src/queries.js @@ -12,7 +12,7 @@ function querystr_to_array(querystr) { export function summaryQuery(windowbucket, afkbucket, count) { windowbucket = windowbucket.replace('"', '\\"'); afkbucket = afkbucket.replace('"', '\\"'); - let code = `events = flood(query_bucket("${windowbucket}")); + const code = `events = flood(query_bucket("${windowbucket}")); not_afk = flood(query_bucket("${afkbucket}")); not_afk = filter_keyvals(not_afk, "status", ["not-afk"]); events = filter_period_intersect(events, not_afk); @@ -29,7 +29,7 @@ export function summaryQuery(windowbucket, afkbucket, count) { export function windowQuery(windowbucket, afkbucket, appcount, titlecount, filterAFK, classes) { windowbucket = windowbucket.replace('"', '\\"'); afkbucket = afkbucket.replace('"', '\\"'); - let code = + const code = `events = flood(query_bucket("${windowbucket}")); not_afk = flood(query_bucket("${afkbucket}")); not_afk = filter_keyvals(not_afk, "status", ["not-afk"]);` + @@ -51,7 +51,7 @@ export function windowQuery(windowbucket, afkbucket, appcount, titlecount, filte export function appQuery(appbucket, limit) { appbucket = appbucket.replace('"', '\\"'); limit = limit || 5; - let code = + const code = `events = query_bucket("${appbucket}");` + `events = merge_events_by_keys(events, ["app"]); events = sort_by_duration(events); @@ -104,12 +104,12 @@ export function browserSummaryQuery(browserbuckets, windowbucket, afkbucket, lim : ''); _.each(Object.keys(appnames), browserName => { - let bucketId = _.filter(browserbuckets, buckets => buckets.indexOf(browserName) !== -1)[0]; + const bucketId = _.filter(browserbuckets, buckets => buckets.indexOf(browserName) !== -1)[0]; if (bucketId === undefined) { // Skip browser if specific bucket not available return; } - let appnames_str = JSON.stringify(appnames[browserName]); + const appnames_str = JSON.stringify(appnames[browserName]); code += `events_${browserName} = flood(query_bucket("${bucketId}")); window_${browserName} = filter_keyvals(window, "app", ${appnames_str});` + diff --git a/src/route.js b/src/route.js index f418d6cf..79be36bc 100644 --- a/src/route.js +++ b/src/route.js @@ -23,7 +23,7 @@ const Dev = () => import('./views/Dev.vue'); Vue.use(VueRouter); -var router = new VueRouter({ +const router = new VueRouter({ routes: [ { path: '/', component: Home }, { diff --git a/src/util/awclient.js b/src/util/awclient.js index 53edd121..c33637a7 100644 --- a/src/util/awclient.js +++ b/src/util/awclient.js @@ -5,12 +5,12 @@ let baseURL = ""; // If running with `npm node dev`, use testing server as origin. // Works since CORS is enabled by default when running `aw-server --testing`. if(!PRODUCTION) { - let protocol = "http"; - let hostname = "127.0.0.1"; - let port = "5666"; + const protocol = "http"; + const hostname = "127.0.0.1"; + const port = "5666"; baseURL = protocol + "://" + hostname + ":" + port; } -let awc = new AWClient("aw-webui", {testing: !PRODUCTION, baseURL}); +const awc = new AWClient("aw-webui", {testing: !PRODUCTION, baseURL}); export default awc; diff --git a/src/util/classes.ts b/src/util/classes.ts index 0953cf08..8aa5fe0a 100644 --- a/src/util/classes.ts +++ b/src/util/classes.ts @@ -1,9 +1,9 @@ -let _ = require('lodash'); +const _ = require('lodash'); const level_sep = ">"; interface Category { - id?: number, + id?: number; name: string[]; name_pretty?: string; subname?: string; @@ -16,7 +16,7 @@ interface Category { children?: Category[]; } -export let defaultCategories: Category[] = [ +export const defaultCategories: Category[] = [ { name: ['Work'], rule: { type: 'regex', pattern: 'Google Docs' } }, { name: ['Work', 'Programming'], @@ -35,7 +35,7 @@ export let defaultCategories: Category[] = [ export function build_category_hierarchy(classes: Category[]): Category[] { function annotate(c: Category) { - let ch = c.name; + const ch = c.name; c.name_pretty = ch.join(level_sep); c.subname = ch.slice(-1)[0]; c.parent = ch.length > 1 ? ch.slice(0, -1) : null; @@ -43,19 +43,19 @@ export function build_category_hierarchy(classes: Category[]): Category[] { return c; } - let new_classes = classes.slice().map(c => annotate(c)); + const new_classes = classes.slice().map(c => annotate(c)); // Insert dangling/undefined parents - let all_full_names = new Set(new_classes.map(c => c.name.join(level_sep))); + const all_full_names = new Set(new_classes.map(c => c.name.join(level_sep))); function createMissingParents(children) { children .map(c => c.parent) .filter(p => !!p) .map(p => { - let name = p.join(level_sep); + const name = p.join(level_sep); if (p && !all_full_names.has(name)) { - let new_parent = annotate({ name: p, rule: { type: null, pattern: '' } }); + const new_parent = annotate({ name: p, rule: { type: null, pattern: '' } }); classes.push(new_parent); all_full_names.add(name); // New parent might not be top-level, so we need to recurse @@ -84,7 +84,7 @@ export function build_category_hierarchy(classes: Category[]): Category[] { export function flatten_category_hierarchy(hier: Category[]): Category[] { return _.flattenDeep( hier.map(h => { - let level = [h, flatten_category_hierarchy(h.children)]; + const level = [h, flatten_category_hierarchy(h.children)]; h.children = []; return level; }) @@ -102,7 +102,7 @@ export function saveClasses(classes: Category[]) { } export function loadClasses(): Category[] { - let classes_json = localStorage.classes; + const classes_json = localStorage.classes; if(classes_json && classes_json.length >= 1) { return JSON.parse(classes_json); } else { diff --git a/src/util/color.js b/src/util/color.js index 2cfddc22..37f26451 100644 --- a/src/util/color.js +++ b/src/util/color.js @@ -8,12 +8,12 @@ const d3 = require("d3"); // See here for examples: // https://bl.ocks.org/pstuffa/3393ff2711a53975040077b7453781a9 -let scale = d3.scaleOrdinal(['#90CAF9', '#FFE082', '#EF9A9A', '#A5D6A7']); +const scale = d3.scaleOrdinal(['#90CAF9', '#FFE082', '#EF9A9A', '#A5D6A7']); // Needed to prewarm the color table scale.domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]); -let customColors = { +const customColors = { "afk": "#EEE", "not-afk": "#7F6", "hibernating": "#DD6", @@ -46,8 +46,8 @@ function hashcode(str){ if (str.length === 0) { return hash; } - for (var i = 0; i < str.length; i++) { - var character = str.charCodeAt(i); + for (let i = 0; i < str.length; i++) { + const character = str.charCodeAt(i); hash = ((hash<<5)-hash)+character; hash = hash & hash; // Convert to 32bit integer } diff --git a/src/util/time.js b/src/util/time.js index 0573805f..e1b825d2 100644 --- a/src/util/time.js +++ b/src/util/time.js @@ -2,10 +2,10 @@ import moment from 'moment'; export function seconds_to_duration(seconds) { // Returns a human-readable duration string - var hrs = Math.floor(seconds / 60 / 60); - var min = Math.floor((seconds / 60) % 60); - var sec = Math.floor(seconds % 60); - let l = []; + const hrs = Math.floor(seconds / 60 / 60); + const min = Math.floor((seconds / 60) % 60); + const sec = Math.floor(seconds % 60); + const l = []; if (hrs != 0) l.push(hrs + 'h'); if (min != 0) l.push(min + 'm'); if (sec != 0 || l.length == 0) l.push(sec + 's'); @@ -13,9 +13,9 @@ export function seconds_to_duration(seconds) { } export function friendlydate(timestamp) { - let now = moment(); - let m = moment.parseZone(timestamp); - let sinceNow = moment.duration(m.diff(now)); + const now = moment(); + const m = moment.parseZone(timestamp); + const sinceNow = moment.duration(m.diff(now)); if (-sinceNow.asSeconds() <= 60) { return `${Math.round(-sinceNow.asSeconds())}s ago`; } else if (-sinceNow.asSeconds() <= 60 * 60 * 24) { @@ -25,10 +25,10 @@ export function friendlydate(timestamp) { } export function get_day_start_with_offset(dateParam) { - var dateMoment = dateParam ? moment(dateParam) : moment().startOf('day'); - var start_of_day = localStorage.startOfDay; - var start_of_day_hours = parseInt(start_of_day.split(':')[0]); - var start_of_day_minutes = parseInt(start_of_day.split(':')[1]); + const dateMoment = dateParam ? moment(dateParam) : moment().startOf('day'); + const start_of_day = localStorage.startOfDay; + const start_of_day_hours = parseInt(start_of_day.split(':')[0]); + const start_of_day_minutes = parseInt(start_of_day.split(':')[1]); return dateMoment .hour(start_of_day_hours) .minute(start_of_day_minutes) @@ -47,7 +47,7 @@ export function get_day_period(date) { export function get_day_start(datestr) { // Get start time of date - var datestart = moment(datestr); + const datestart = moment(datestr); datestart.set('hour', 0); datestart.set('minute', 0); datestart.set('second', 0); diff --git a/src/util/tooltip.js b/src/util/tooltip.js index bcc98f76..9d2d65fa 100644 --- a/src/util/tooltip.js +++ b/src/util/tooltip.js @@ -3,7 +3,7 @@ import { seconds_to_duration } from './time.js'; import DOMPurify from 'dompurify'; import _ from 'lodash'; -let sanitize = DOMPurify.sanitize; +const sanitize = DOMPurify.sanitize; export function buildTooltip(bucket, e) { // WARNING: XSS risk, make sure to sanitize properly diff --git a/src/views/Bucket.vue b/src/views/Bucket.vue index 19b25251..95b27693 100644 --- a/src/views/Bucket.vue +++ b/src/views/Bucket.vue @@ -47,7 +47,7 @@ export default { }, computed: { buckets() { - let bucket = this.bucket; + const bucket = this.bucket; bucket.events = this.events; return [bucket]; } @@ -57,6 +57,12 @@ export default { this.getEvents(this.id); } }, + mounted: function() { + this.id = this.$route.params.id; + this.getBucketInfo(this.id); + this.getEvents(this.id); + this.getEventCount(this.id); + }, methods: { getBucketInfo: async function(bucket_id) { this.bucket = await this.$aw.getBucketInfo(bucket_id); @@ -75,11 +81,5 @@ export default { this.eventcount = (await this.$aw.countEvents(bucket_id)).data; }, }, - mounted: function() { - this.id = this.$route.params.id; - this.getBucketInfo(this.id); - this.getEvents(this.id); - this.getEventCount(this.id); - }, } diff --git a/src/views/Buckets.vue b/src/views/Buckets.vue index 5e88ad83..6ffcb001 100644 --- a/src/views/Buckets.vue +++ b/src/views/Buckets.vue @@ -97,14 +97,14 @@ import _ from 'lodash'; export default { name: "Buckets", - mounted: function() { - this.getBuckets(); - }, data: () => { return { buckets: [], } }, + mounted: function() { + this.getBuckets(); + }, methods: { getBuckets: async function() { this.buckets = _.orderBy(await this.$aw.getBuckets(), [(b) => b.id], ["asc"]); diff --git a/src/views/Log.vue b/src/views/Log.vue index 13da630b..0a36f807 100644 --- a/src/views/Log.vue +++ b/src/views/Log.vue @@ -43,14 +43,14 @@ div diff --git a/src/visualizations/Summary.vue b/src/visualizations/Summary.vue index 06fe8a4b..292a329d 100644 --- a/src/visualizations/Summary.vue +++ b/src/visualizations/Summary.vue @@ -37,11 +37,6 @@ export default { data: function() { return { limit_: this.limit } }, - mounted: function() { - let el = this.$el.children[0]; - summary.create(el); - this.update(); - }, watch: { fields: function() { this.update(); @@ -50,9 +45,14 @@ export default { this.update(); }, }, + mounted: function() { + const el = this.$el.children[0]; + summary.create(el); + this.update(); + }, methods: { update: function() { - let el = this.$el.children[0]; + const el = this.$el.children[0]; if(this.fields !== null) { summary.updateSummedEvents(el, this.fields.slice(0, this.limit_), this.namefunc, this.colorfunc) } else { diff --git a/src/visualizations/SunburstClock.vue b/src/visualizations/SunburstClock.vue index 6f163881..67d6345e 100644 --- a/src/visualizations/SunburstClock.vue +++ b/src/visualizations/SunburstClock.vue @@ -112,12 +112,6 @@ import _ from 'lodash'; export default { name: "aw-sunburst-clock", props: ['date', 'afkBucketId', 'windowBucketId'], - mounted: function() { - sunburst.create(this.$el); - this.starttime = moment(this.date); - this.endtime = moment(this.date).add(1, 'days'); - this.visualize(); - }, data: () => { return { @@ -134,6 +128,12 @@ export default { this.visualize(); } }, + mounted: function() { + sunburst.create(this.$el); + this.starttime = moment(this.date); + this.endtime = moment(this.date).add(1, 'days'); + this.visualize(); + }, methods: { todaysEvents: function(bucket_id) { @@ -148,21 +148,21 @@ export default { parents = _.sortBy(parents, "timestamp", "desc"); children = _.sortBy(children, "timestamp", "desc"); - var i_child = 0; - for(var i_parent = 0; i_parent < parents.length; i_parent++) { - let p = parents[i_parent]; - let p_start = moment(p.timestamp); - let p_end = p_start.clone().add(p.duration, "seconds"); + let i_child = 0; + for(let i_parent = 0; i_parent < parents.length; i_parent++) { + const p = parents[i_parent]; + const p_start = moment(p.timestamp); + const p_end = p_start.clone().add(p.duration, "seconds"); p.children = []; while(i_child < children.length) { - var e = children[i_child]; - var e_start = moment(e.timestamp); - var e_end = e_start.clone().add(e.duration, "seconds"); + const e = children[i_child]; + const e_start = moment(e.timestamp); + const e_end = e_start.clone().add(e.duration, "seconds"); - let before_parent = e_end.isBefore(p_start); - let within_parent = e_start.isAfter(p_start) && e_end.isBefore(p_end); - let after_parent = e_start.isAfter(p_end); + const before_parent = e_end.isBefore(p_start); + const within_parent = e_start.isAfter(p_start) && e_end.isBefore(p_end); + const after_parent = e_start.isAfter(p_end); // TODO: This isn't correct, yet if(before_parent) { @@ -188,9 +188,9 @@ export default { // Build the root node //console.log(parents); - let m_start = moment(_.first(parents).timestamp) - let m_end = moment(_.tail(parents).timestamp) - let duration = (m_end - m_start) / 1000; + const m_start = moment(_.first(parents).timestamp) + const m_end = moment(_.tail(parents).timestamp) + const duration = (m_end - m_start) / 1000; return { "timestamp": _.first(parents).timestamp, // TODO: If we want a 12/24h clock, this has to change diff --git a/src/visualizations/TimelineInspect.vue b/src/visualizations/TimelineInspect.vue index 4bd3ba36..0925fcd0 100644 --- a/src/visualizations/TimelineInspect.vue +++ b/src/visualizations/TimelineInspect.vue @@ -18,10 +18,6 @@ import timeline from './timeline.js'; export default { name: "aw-timeline", props: ['chunks', 'show_afk', 'chunkfunc', 'eventfunc'], - mounted: function() { - timeline.create(this.$el); - this.update(); - }, watch: { chunks: function() { this.update(); @@ -30,6 +26,10 @@ export default { this.update(); } }, + mounted: function() { + timeline.create(this.$el); + this.update(); + }, methods: { update: function() { if(this.chunks === null) { diff --git a/src/visualizations/TimelineSimple.vue b/src/visualizations/TimelineSimple.vue index 41e983d2..06899b10 100644 --- a/src/visualizations/TimelineSimple.vue +++ b/src/visualizations/TimelineSimple.vue @@ -18,13 +18,13 @@ import timeline_simple from './timeline-simple.js'; export default { name: "aw-timeline", props: ['type', 'event_type', 'events'], - mounted: function() { - timeline_simple.create(this.$el); - }, watch: { "events": function() { timeline_simple.update(this.$el, this.events, this.event_type) } + }, + mounted: function() { + timeline_simple.create(this.$el); } } diff --git a/src/visualizations/VisTimeline.vue b/src/visualizations/VisTimeline.vue index 3727033c..d4a477dc 100644 --- a/src/visualizations/VisTimeline.vue +++ b/src/visualizations/VisTimeline.vue @@ -51,11 +51,33 @@ export default { } }; }, - mounted() { - this.$nextTick(() => { - let el = this.$el.querySelector('#visualization'); - this.timeline = new Timeline(el, [], [], this.options); - }); + computed: { + chartData() { + const data = []; + _.each(this.buckets, (bucket, bidx) => { + if(bucket.events === undefined) { + return; + } + let events = bucket.events; + // Filter out events shorter than 1 second (notably including 0-duration events) + // TODO: Use flooding instead, preferably with some additional method of removing/simplifying short events for even greater performance + if(this.filterShortEvents) { + events = _.filter(events, (e) => e.duration > 1); + } + events = _.sortBy(events, (e) => e.timestamp); + _.each(events, (e) => { + data.push([ + bidx, + getTitleAttr(bucket, e), + buildTooltip(bucket, e), + new Date(e.timestamp), + new Date(moment(e.timestamp).add(e.duration, 'seconds')), + getColorFromString(getTitleAttr(bucket, e)), + ]); + }) + }) + return data; + }, }, watch: { buckets() { @@ -66,12 +88,12 @@ export default { } // Build groups - let groups = _.map(this.buckets, (bucket, bidx) => { + const groups = _.map(this.buckets, (bucket, bidx) => { return {id: bidx, content: this.showRowLabels ? '': bucket.id}; }); // Build items - let items = _.map(this.chartData, (row, i) => { + const items = _.map(this.chartData, (row, i) => { return { id: i, group: row[0], @@ -85,7 +107,7 @@ export default { if(groups.length > 0 && items.length > 0) { if(this.queriedInterval && this.showQueriedInterval) { - let duration = this.queriedInterval[1].diff(this.queriedInterval[0], "seconds"); + const duration = this.queriedInterval[1].diff(this.queriedInterval[0], "seconds"); groups.push({id: groups.length, content: "queried interval"}); items.push({ id: items.length + 1, @@ -102,8 +124,8 @@ export default { }); } - let start = (this.queriedInterval && this.queriedInterval[0]) || _.min(_.map(items, (item) => item.start)); - let end = (this.queriedInterval && this.queriedInterval[1]) || _.max(_.map(items, (item) => item.end)); + const start = (this.queriedInterval && this.queriedInterval[0]) || _.min(_.map(items, (item) => item.start)); + const end = (this.queriedInterval && this.queriedInterval[1]) || _.max(_.map(items, (item) => item.end)); this.options.min = start; this.options.max = end; this.timeline.setOptions(this.options); @@ -112,33 +134,11 @@ export default { } } }, - computed: { - chartData() { - let data = []; - _.each(this.buckets, (bucket, bidx) => { - if(bucket.events === undefined) { - return; - } - let events = bucket.events; - // Filter out events shorter than 1 second (notably including 0-duration events) - // TODO: Use flooding instead, preferably with some additional method of removing/simplifying short events for even greater performance - if(this.filterShortEvents) { - events = _.filter(events, (e) => e.duration > 1); - } - events = _.sortBy(events, (e) => e.timestamp); - _.each(events, (e) => { - data.push([ - bidx, - getTitleAttr(bucket, e), - buildTooltip(bucket, e), - new Date(e.timestamp), - new Date(moment(e.timestamp).add(e.duration, 'seconds')), - getColorFromString(getTitleAttr(bucket, e)), - ]); - }) - }) - return data; - }, + mounted() { + this.$nextTick(() => { + const el = this.$el.querySelector('#visualization'); + this.timeline = new Timeline(el, [], [], this.options); + }); }, } diff --git a/src/visualizations/periodusage.js b/src/visualizations/periodusage.js index 3a278e2d..c08cdd67 100644 --- a/src/visualizations/periodusage.js +++ b/src/visualizations/periodusage.js @@ -14,7 +14,7 @@ function create(svg_elem) { function set_status(svg_elem, msg) { // Select svg canvas svg_elem.innerHTML = ''; - let svg = d3.select(svg_elem); + const svg = d3.select(svg_elem); svg .append('text') @@ -26,12 +26,12 @@ function set_status(svg_elem, msg) { .attr('fill', 'black'); } -var diagramcolor = '#aaa'; -var diagramcolor_selected = '#fc5'; -var diagramcolor_focused = '#adf'; +const diagramcolor = '#aaa'; +const diagramcolor_selected = '#fc5'; +const diagramcolor_focused = '#adf'; function update(svg_elem, usage_arr, link_prefix) { - let dateformat = 'YYYY-MM-DD'; + const dateformat = 'YYYY-MM-DD'; // No apps, sets status to "No data" if (usage_arr.length <= 0) { @@ -42,33 +42,33 @@ function update(svg_elem, usage_arr, link_prefix) { var svg = d3.select(svg_elem); function get_usage_time(day_events) { - var day_event = _.head(_.filter(day_events, e => e.data.status == 'not-afk')); + const day_event = _.head(_.filter(day_events, e => e.data.status == 'not-afk')); return day_event != undefined ? day_event.duration : 0; } - var usage_times = usage_arr.map(day_events => get_usage_time(day_events)); - var longest_usage = Math.max.apply(null, usage_times); + const usage_times = usage_arr.map(day_events => get_usage_time(day_events)); + let longest_usage = Math.max.apply(null, usage_times); // Avoid division by zero if (longest_usage <= 0) { longest_usage = 0.00000000001; } - var padding = 0.3 * (100 / (usage_arr.length - 1)); - var width = 100 / usage_arr.length - padding; - var center_elem = Math.floor(usage_arr.length / 2); + const padding = 0.3 * (100 / (usage_arr.length - 1)); + const width = 100 / usage_arr.length - padding; + const center_elem = Math.floor(usage_arr.length / 2); _.each(usage_arr, (events, i) => { - var usage_time = get_usage_time(events); - var height = 85 * (usage_time / longest_usage); - var date = ''; + const usage_time = get_usage_time(events); + const height = 85 * (usage_time / longest_usage); + let date = ''; if (events.length > 0) { // slice off so it's only the day date = moment(usage_arr[i][0].timestamp).format(dateformat); } - var color = i == center_elem ? diagramcolor_selected : diagramcolor; - var offset = 50; + const color = i == center_elem ? diagramcolor_selected : diagramcolor; + const offset = 50; - let x = i * padding + i * width + 0.25 * width; + const x = i * padding + i * width + 0.25 * width; if (moment(date).isSame(moment(), 'day')) { svg @@ -87,7 +87,7 @@ function update(svg_elem, usage_arr, link_prefix) { .text('Today'); } - let rect = svg + const rect = svg .append('rect') .attr('x', x + '%') .attr('y', 101 - height + '%') // Draw rect bottom-up @@ -107,14 +107,14 @@ function update(svg_elem, usage_arr, link_prefix) { rect.style('fill', diagramcolor_focused); }) .on('mouseout', (d, j, n) => { - var a = n[j]; + const a = n[j]; rect.style('fill', a.getAttribute('color')); }) .on('click', function(d, j, n) { - var me = n[j]; - var date = d3.select(me).attr('date'); - var link_prefix = d3.select(me).attr('data'); - var url = `/#${link_prefix}/${date}`; + const me = n[j]; + const date = d3.select(me).attr('date'); + const link_prefix = d3.select(me).attr('data'); + const url = `/#${link_prefix}/${date}`; /* Not the vue-way, but works */ window.location.assign(url); /* Hardcoding click behavior also isn't optimal I guess */ diff --git a/src/visualizations/summary.js b/src/visualizations/summary.js index 59af96d4..a71ff3ef 100644 --- a/src/visualizations/summary.js +++ b/src/visualizations/summary.js @@ -13,7 +13,7 @@ function create(container) { container.innerHTML = ''; // Create svg canvas - let svg = d3.select(container).append('svg'); + const svg = d3.select(container).append('svg'); svg .attr('width', '100%') .attr('height', '100px') @@ -22,8 +22,8 @@ function create(container) { function set_status(container, msg) { // Select svg canvas - let svg_elem = container.querySelector('.appsummary'); - let svg = d3.select(svg_elem); + const svg_elem = container.querySelector('.appsummary'); + const svg = d3.select(svg_elem); svg_elem.innerHTML = ''; svg @@ -37,7 +37,7 @@ function set_status(container, msg) { } function updateSummedEvents(container, summedEvents, titleKeyFunc, colorKeyFunc) { - let apps = _.map(summedEvents, e => { + const apps = _.map(summedEvents, e => { return { name: titleKeyFunc(e), duration: e.duration, colorKey: colorKeyFunc(e) }; }); update(container, apps); @@ -50,34 +50,34 @@ function update(container, apps) { return container; } - let svg_elem = container.querySelector('.appsummary'); + const svg_elem = container.querySelector('.appsummary'); svg_elem.innerHTML = ''; - let svg = d3.select(svg_elem); + const svg = d3.select(svg_elem); // Remove apps without a duration from list apps = apps.filter(function(app) { return app.duration !== undefined; }); - var curr_y = 0; - var longest_duration = apps[0].duration; + let curr_y = 0; + const longest_duration = apps[0].duration; _.each(apps, function(app, i) { // TODO: Expand on click and list titles // Variables - var width = (app.duration / longest_duration) * 100 + '%'; - let barHeight = 50; - let textSize = 15; - var baseappcolor = getColorFromString(app.colorKey || app.name); - var appcolor = Color(baseappcolor) + const width = (app.duration / longest_duration) * 100 + '%'; + const barHeight = 50; + const textSize = 15; + const baseappcolor = getColorFromString(app.colorKey || app.name); + const appcolor = Color(baseappcolor) .lighten(0.1) .hex(); - var hovercolor = Color(baseappcolor) + const hovercolor = Color(baseappcolor) .darken(0.1) .hex(); // The group representing an application in the barchart - let eg = svg.append('g'); + const eg = svg.append('g'); eg.attr('id', 'summary_app_' + i) .on('mouseover', function() { eg.select('rect').style('fill', hovercolor); diff --git a/src/visualizations/sunburst-clock.js b/src/visualizations/sunburst-clock.js index 0599e586..99ad9923 100644 --- a/src/visualizations/sunburst-clock.js +++ b/src/visualizations/sunburst-clock.js @@ -21,20 +21,20 @@ import { seconds_to_duration } from '../util/time.js'; const color = require('../util/color.js'); // Dimensions of sunburst. -var width = 750; -var height = 600; -var radius = Math.min(width, height) / 2; +const width = 750; +const height = 600; +const radius = Math.min(width, height) / 2; -var legendData = { +const legendData = { afk: color.getColorFromString('afk'), 'not-afk': color.getColorFromString('not-afk'), hibernating: color.getColorFromString('hibernating'), }; -var rootEl; // The root DOM node of the graph as a d3 object -var vis; // The root SVG node of the graph as a d3 object -var partition; -var arc; +let rootEl; // The root DOM node of the graph as a d3 object +let vis; // The root SVG node of the graph as a d3 object +let partition; +let arc; function create(el) { // Clear the svg in case we are redrawing @@ -70,8 +70,8 @@ function create(el) { } function drawClockTick(a) { - let xn = Math.cos(a); - let yn = Math.sin(a); + const xn = Math.cos(a); + const yn = Math.sin(a); vis .append('line') @@ -84,11 +84,11 @@ function drawClockTick(a) { } function drawClock(h, m, text) { - let a = 2 * Math.PI * (h / 24 + m / 24 / 60) - (1 / 2) * Math.PI; + const a = 2 * Math.PI * (h / 24 + m / 24 / 60) - (1 / 2) * Math.PI; drawClockTick(a); - let xn = Math.cos(a); - let yn = Math.sin(a); + const xn = Math.cos(a); + const yn = Math.sin(a); vis .append('text') @@ -116,13 +116,13 @@ function update(el, json) { .style('opacity', 0); // Turn the data into a d3 hierarchy and calculate the sums. - var root = d3.hierarchy(json); - var nodes = partition(root); + let root = d3.hierarchy(json); + let nodes = partition(root); - let mode_clock = true; + const mode_clock = true; if (mode_clock) { // TODO: Make this a checkbox in the UI - let show_whole_day = true; + const show_whole_day = true; let root_start = moment(json.timestamp); let root_end = moment(json.timestamp).add(json.duration, 'seconds'); @@ -136,19 +136,19 @@ function update(el, json) { drawClock(18, 0); // TODO: Draw only if showing today - let now = moment(); + const now = moment(); drawClock(now.hour(), now.minute(), 'Now'); } nodes = nodes .each(function(d) { - let loc_start_sec = moment(d.data.timestamp).diff(root_start, 'seconds', true); - let loc_end_sec = moment(d.data.timestamp) + const loc_start_sec = moment(d.data.timestamp).diff(root_start, 'seconds', true); + const loc_end_sec = moment(d.data.timestamp) .add(d.data.duration, 'seconds') .diff(root_start, 'seconds', true); - let loc_start = Math.max(0, loc_start_sec / ((root_end - root_start) / 1000)); - let loc_end = Math.min(1, loc_end_sec / ((root_end - root_start) / 1000)); + const loc_start = Math.max(0, loc_start_sec / ((root_end - root_start) / 1000)); + const loc_end = Math.min(1, loc_end_sec / ((root_end - root_start) / 1000)); d.x0 = 2 * Math.PI * loc_start; d.x1 = 2 * Math.PI * loc_end; @@ -167,7 +167,7 @@ function update(el, json) { // If show_whole_day: // 0.0044 rad = 1min // 0.0011 rad = 15s - let threshold = 0.001; + const threshold = 0.001; return d.x1 - d.x0 > threshold; }); @@ -199,13 +199,13 @@ function mouseclick(d) { } function showInfo(d) { - let hoverEl = d3.select('.explanation > .hover'); + const hoverEl = d3.select('.explanation > .hover'); - let m = moment(d.data.timestamp); + const m = moment(d.data.timestamp); hoverEl.select('.date').text(m.format('YYYY-MM-DD')); hoverEl.select('.time').text(m.format('HH:mm:ss')); - let durationString = seconds_to_duration(d.data.duration); + const durationString = seconds_to_duration(d.data.duration); hoverEl.select('.duration').text(durationString); hoverEl.select('.title').text(d.data.data.app || d.data.data.status); @@ -220,7 +220,7 @@ function showInfo(d) { function mouseover(d) { showInfo(d); - var sequenceArray = d.ancestors().reverse(); + const sequenceArray = d.ancestors().reverse(); sequenceArray.shift(); // remove root node from the array // Fade all the segments. @@ -257,7 +257,7 @@ function mouseleave() { function initializeBreadcrumbTrail() { // Add the svg area. - var trail = d3 + const trail = d3 .select('.sequence') .append('svg:svg') .attr('width', width) @@ -272,20 +272,20 @@ function initializeBreadcrumbTrail() { function drawLegend() { // Dimensions of legend item: width, height, spacing, radius of rounded rect. - var li = { + const li = { w: 75, h: 30, s: 3, r: 3, }; - var legend = d3 + const legend = d3 .select('.legend') .append('svg:svg') .attr('width', li.w) .attr('height', d3.keys(legendData).length * (li.h + li.s)); - var g = legend + const g = legend .selectAll('g') .data(d3.entries(legendData)) .enter() diff --git a/src/visualizations/timeline-simple.js b/src/visualizations/timeline-simple.js index 51b00adf..e569d204 100644 --- a/src/visualizations/timeline-simple.js +++ b/src/visualizations/timeline-simple.js @@ -7,7 +7,7 @@ const moment = require("moment"); import {getTitleAttr, getColorFromString} from "../util/color.js"; -var time = require("../util/time.js"); +const time = require("../util/time.js"); function create(svg_el) { // Clear element @@ -20,7 +20,7 @@ function create(svg_el) { } function set_status(svg_el, text){ - let timeline = d3.select(svg_el); + const timeline = d3.select(svg_el); timeline.selectAll("*").remove(); timeline.append("text") @@ -33,7 +33,7 @@ function set_status(svg_el, text){ } function update(svg_el, events, event_type) { - let timeline = d3.select(svg_el); + const timeline = d3.select(svg_el); timeline.selectAll("*").remove(); if (events.length <= 0){ @@ -43,40 +43,40 @@ function update(svg_el, events, event_type) { events = _.clone(events); _.reverse(events); - let e_first = _.first(events); - let e_last = _.last(events); - let m_first = moment(e_first.timestamp); - let m_last = moment(e_last.timestamp); - let total_duration = (m_last - m_first) / 1000 + e_last.duration; + const e_first = _.first(events); + const e_last = _.last(events); + const m_first = moment(e_first.timestamp); + const m_last = moment(e_last.timestamp); + const total_duration = (m_last - m_first) / 1000 + e_last.duration; console.log("First: " + m_first.format()); console.log("Last: " + m_last.format()); console.log("Duration: " + total_duration); // Iterate over each event and create interval boxes _.each(events, function(e, i) { - let id = "timeline_event_" + i; - let timestamp = moment(e.timestamp); + const id = "timeline_event_" + i; + const timestamp = moment(e.timestamp); - let color_base = getColorFromString(getTitleAttr({type: event_type}, e)); - let color_hover = Color(color_base).darken(0.4).hex(); + const color_base = getColorFromString(getTitleAttr({type: event_type}, e)); + const color_hover = Color(color_base).darken(0.4).hex(); - let x = (timestamp - m_first) / 1000 / total_duration; - let width = 100 * e.duration / total_duration; + const x = (timestamp - m_first) / 1000 / total_duration; + const width = 100 * e.duration / total_duration; - let eg = timeline.append("g") + const eg = timeline.append("g") .attr("id", id) .attr("transform", "translate(" + 100 * x + "," + 0 + ")"); - let rect = eg.append("rect") + const rect = eg.append("rect") .attr("width", width) .attr("height", 4) .style("fill", color_base) .on("mouseover", function(d, j, n){ - let elem = n[j]; + const elem = n[j]; elem.style.fill = color_hover; }) .on("mouseout", function(d, j, n){ - let elem = n[j]; + const elem = n[j]; elem.style.fill = color_base; }); diff --git a/src/visualizations/timeline.js b/src/visualizations/timeline.js index 699a6b6b..a1652272 100644 --- a/src/visualizations/timeline.js +++ b/src/visualizations/timeline.js @@ -14,8 +14,8 @@ import { getColorFromString } from '../util/color.js'; import { seconds_to_duration } from '../util/time.js'; function show_info(container, elem_id) { - var title_event_box = container.querySelector('#' + elem_id); - var titleinfo = container.querySelector('.titleinfo-container'); + const title_event_box = container.querySelector('#' + elem_id); + const titleinfo = container.querySelector('.titleinfo-container'); titleinfo.innerHTML = title_event_box.innerHTML; titleinfo.style.height = title_event_box.getAttribute('height'); } @@ -38,7 +38,7 @@ function create(container) { .attr('display', 'none'); // Container for titleinfo that has a fixed size and a overflow scroll - let titleinfo_container = document.createElement('div'); + const titleinfo_container = document.createElement('div'); titleinfo_container.style.width = '100%'; titleinfo_container.style.height = '200px'; titleinfo_container.style.overflowY = 'scroll'; @@ -52,11 +52,11 @@ function create(container) { } function set_status(container, text) { - let timeline_elem = container.querySelector('.apptimeline'); - let titleinfo_list_elem = container.querySelector('.titleinfo-list'); - let titleinfo_container_elem = container.querySelector('.titleinfo-container'); + const timeline_elem = container.querySelector('.apptimeline'); + const titleinfo_list_elem = container.querySelector('.titleinfo-list'); + const titleinfo_container_elem = container.querySelector('.titleinfo-container'); - let timeline = d3.select(timeline_elem); + const timeline = d3.select(timeline_elem); timeline_elem.innerHTML = ''; titleinfo_list_elem.innerHTML = ''; titleinfo_container_elem.innerHTML = ''; @@ -72,8 +72,8 @@ function set_status(container, text) { } function update(container, events, showAFK, chunkfunc, eventfunc) { - let timeline = d3.select(container.querySelector('.apptimeline')).html(null); - let titleinfo_list = d3.select(container.querySelector('.titleinfo-list')).html(null); + const timeline = d3.select(container.querySelector('.apptimeline')).html(null); + const titleinfo_list = d3.select(container.querySelector('.titleinfo-list')).html(null); d3.select(container.querySelector('.titleinfo-container')).html(null); if (events.length <= 0) { @@ -81,14 +81,14 @@ function update(container, events, showAFK, chunkfunc, eventfunc) { return; } - var firstEvent = events[0]; - var lastEvent = events[events.length - 1]; + const firstEvent = events[0]; + const lastEvent = events[events.length - 1]; - var timeStart = moment(firstEvent.timestamp); - var timeEnd = moment(lastEvent.timestamp).add(lastEvent.duration, 'seconds'); - let totalDuration = _.sumBy(events, 'duration'); + const timeStart = moment(firstEvent.timestamp); + const timeEnd = moment(lastEvent.timestamp).add(lastEvent.duration, 'seconds'); + const totalDuration = _.sumBy(events, 'duration'); - var secSinceStart = timeEnd.diff(timeStart, 'seconds', true); + const secSinceStart = timeEnd.diff(timeStart, 'seconds', true); // Iterate over each app timeperiod let curr_x = 0; @@ -97,7 +97,7 @@ function update(container, events, showAFK, chunkfunc, eventfunc) { let eventX, eventWidth; if (showAFK) { - let eventBegin = moment(e.timestamp); + const eventBegin = moment(e.timestamp); eventX = eventBegin.diff(timeStart, 'seconds', true) / secSinceStart; eventX = eventX * 100 + '%'; eventWidth = (e.duration / secSinceStart) * 100 + '%'; @@ -106,14 +106,14 @@ function update(container, events, showAFK, chunkfunc, eventfunc) { eventWidth = (e.duration / totalDuration) * 100; } - var appcolor = getColorFromString(chunkfunc(e)); - var hovercolor = Color(appcolor) + const appcolor = getColorFromString(chunkfunc(e)); + const hovercolor = Color(appcolor) .darken(0.4) .hex(); - let eg = timeline.append('g').attr('id', 'timeline_event_' + i); + const eg = timeline.append('g').attr('id', 'timeline_event_' + i); - let rect = eg + const rect = eg .append('rect') .attr('x', eventX) .attr('y', 0) @@ -129,7 +129,7 @@ function update(container, events, showAFK, chunkfunc, eventfunc) { }); // Titleinfo box - var infobox = titleinfo_list + const infobox = titleinfo_list .append('div') .attr('id', 'titleinfo_event_' + i) .style('display', 'none'); @@ -145,14 +145,14 @@ function update(container, events, showAFK, chunkfunc, eventfunc) { .attr('fill', 'black'); // Titleinfo - var infolist = infobox.append('table'); + const infolist = infobox.append('table'); _.each(e.data.subevents, function(t) { - var inforow = infolist.append('tr'); + const inforow = infolist.append('tr'); // Clocktime - var clocktime = moment(t.timestamp).format('HH:mm:ss'); + const clocktime = moment(t.timestamp).format('HH:mm:ss'); inforow.append('td').text(clocktime); // Duration - var duration = seconds_to_duration(t.duration); + const duration = seconds_to_duration(t.duration); inforow .append('td') .text(duration) diff --git a/test.js b/test.js deleted file mode 100644 index 40e4ed8b..00000000 --- a/test.js +++ /dev/null @@ -1,21 +0,0 @@ -const jsdom = require("jsdom"); -const { JSDOM } = jsdom; -const timeline = require("./src/visualizations/timeline.js"); - -var dom = new JSDOM("") -global.window = dom.window; -global.document = dom.window.document; - - -var root_el = dom.window.document.createElement("div"); - -var example_events = [ - {"timestamp": "", "duration": 10, "data": {"title": "lololtitle", "app": "lololapp"}}, - {"timestamp": "", "duration": 10, "data": {"title": "lololtitle", "app": "lololapp"}} -] - -var total_duration = 20; - -timeline.create(root_el) -timeline.update(root_el, example_events, total_duration) -