diff --git a/server/package-lock.json b/server/package-lock.json index 2ddbdb5c01..99ae0d7e3e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -16,10 +16,68 @@ "cors": "^2.8.5", "crossfilter2": "^1.5.4", "d3": "^7.1.1", + "dc": "file:..", "express": "^4.18.2", "typescript": "^4.8.2" } }, + "..": { + "version": "5.0.0-alpha0", + "license": "Apache-2.0", + "dependencies": { + "d3": "^7.1.1" + }, + "devDependencies": { + "@chiragrupani/karma-chromium-edge-launcher": "^2.2.2", + "@knodes/typedoc-plugin-pages": "^0.23.1", + "@rollup/plugin-terser": "^0.1.0", + "@rollup/plugin-typescript": "^9.0.2", + "@types/d3": "^7.1.0", + "compare-versions": "^3.6.0", + "crossfilter2": "^1.5.2", + "docco": "^0.9.1", + "eslint": "^8.23.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-html": "^7.1.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.1", + "file-saver": "^1.3.8", + "grunt": "^1.5.3", + "grunt-cli": "^1.4.3", + "grunt-contrib-connect": "^3.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^4.0.0", + "grunt-contrib-jasmine": "^3.0.0", + "grunt-contrib-watch": "^1.1.0", + "grunt-fileindex": "kum-deepak/grunt-fileindex", + "grunt-gh-pages": "^4.0.0", + "grunt-karma": "^4.0.2", + "grunt-sass": "^3.1.0", + "grunt-shell": "^4.0.0", + "jasmine-core": "^4.3.0", + "json5": "^2.2.1", + "karma": "^6.4.0", + "karma-chrome-launcher": "^3.1.1", + "karma-coverage": "^2.2.0", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "^5.1.0", + "karma-safari-launcher": "https://github.com/RLovelett/karma-safari-launcher.git#safari-webdriver", + "karma-summary-reporter": "^3.1.1", + "load-grunt-tasks": "^5.1.0", + "prettier": "^2.7.1", + "reductio": "^1.0.0", + "regression": "^2.0.1", + "rollup": "^2.78.1", + "rollup-plugin-license": "^2.8.1", + "sass": "^1.54.5", + "time-grunt": "^2.0.0", + "tslint": "^6.1.3", + "tslint-config-prettier": "^1.18.0", + "typedoc": "^0.23.11", + "typescript": "^4.8.2" + } + }, "node_modules/@ranfdev/deepobj": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@ranfdev/deepobj/-/deepobj-1.0.2.tgz", @@ -830,6 +888,10 @@ "node": ">=12" } }, + "node_modules/dc": { + "resolved": "..", + "link": true + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2038,6 +2100,60 @@ "d3-transition": "2 - 3" } }, + "dc": { + "version": "file:..", + "requires": { + "@chiragrupani/karma-chromium-edge-launcher": "^2.2.2", + "@knodes/typedoc-plugin-pages": "^0.23.1", + "@rollup/plugin-terser": "^0.1.0", + "@rollup/plugin-typescript": "^9.0.2", + "@types/d3": "^7.1.0", + "compare-versions": "^3.6.0", + "crossfilter2": "^1.5.2", + "d3": "^7.1.1", + "docco": "^0.9.1", + "eslint": "^8.23.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-html": "^7.1.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.0.1", + "file-saver": "^1.3.8", + "grunt": "^1.5.3", + "grunt-cli": "^1.4.3", + "grunt-contrib-connect": "^3.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-cssmin": "^4.0.0", + "grunt-contrib-jasmine": "^3.0.0", + "grunt-contrib-watch": "^1.1.0", + "grunt-fileindex": "kum-deepak/grunt-fileindex", + "grunt-gh-pages": "^4.0.0", + "grunt-karma": "^4.0.2", + "grunt-sass": "^3.1.0", + "grunt-shell": "^4.0.0", + "jasmine-core": "^4.3.0", + "json5": "^2.2.1", + "karma": "^6.4.0", + "karma-chrome-launcher": "^3.1.1", + "karma-coverage": "^2.2.0", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "^5.1.0", + "karma-safari-launcher": "https://github.com/RLovelett/karma-safari-launcher.git#safari-webdriver", + "karma-summary-reporter": "^3.1.1", + "load-grunt-tasks": "^5.1.0", + "prettier": "^2.7.1", + "reductio": "^1.0.0", + "regression": "^2.0.1", + "rollup": "^2.78.1", + "rollup-plugin-license": "^2.8.1", + "sass": "^1.54.5", + "time-grunt": "^2.0.0", + "tslint": "^6.1.3", + "tslint-config-prettier": "^1.18.0", + "typedoc": "^0.23.11", + "typescript": "^4.8.2" + } + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/server/package.json b/server/package.json index 07ccdb3143..93093a6b1a 100644 --- a/server/package.json +++ b/server/package.json @@ -16,6 +16,7 @@ "cors": "^2.8.5", "crossfilter2": "^1.5.4", "d3": "^7.1.1", + "dc": "file:..", "express": "^4.18.2", "typescript": "^4.8.2" } diff --git a/server/src/data-server.mts b/server/src/data-server.ts similarity index 99% rename from server/src/data-server.mts rename to server/src/data-server.ts index 14176b57b4..7f5ece96a8 100644 --- a/server/src/data-server.mts +++ b/server/src/data-server.ts @@ -1,8 +1,8 @@ import crossfilter from 'crossfilter2'; import fs from 'fs'; import * as d3 from 'd3'; -import { CFDataCapHelper, CFMultiAdapter, CFSimpleAdapter } from '../../index.js'; -import { FilterStorage } from '../../index.js'; +import { CFDataCapHelper, CFMultiAdapter, CFSimpleAdapter } from 'dc'; +import { FilterStorage } from 'dc'; interface DataElement { volume: number; diff --git a/server/src/server.mts b/server/src/server.ts similarity index 90% rename from server/src/server.mts rename to server/src/server.ts index d0ffee62f4..ae3605a2ea 100644 --- a/server/src/server.mts +++ b/server/src/server.ts @@ -1,7 +1,7 @@ import express from 'express'; import cors from 'cors'; -import { creatAdapter, loadAndProcessData } from './data-server.mjs'; -import { dateTimeReviver } from './utils/date-time-reviver.mjs'; +import { creatAdapter, loadAndProcessData } from './data-server.js'; +import { dateTimeReviver } from './utils/date-time-reviver.js'; const app = express(); const port = 3030; diff --git a/server/src/utils/date-time-reviver.mts b/server/src/utils/date-time-reviver.ts similarity index 100% rename from server/src/utils/date-time-reviver.mts rename to server/src/utils/date-time-reviver.ts diff --git a/server/tsconfig.json b/server/tsconfig.json index 0fdcd4563f..33b59baa01 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -56,6 +56,6 @@ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ }, - "include": ["./src/**/*.mts"], /* Only compile files in src, avoids confusion with code in node_modules or compiled code */ + "include": ["./src/**/*.ts"], /* Only compile files in src, avoids confusion with code in node_modules or compiled code */ "exclude": [] /* Version info is needed only in the UMD bundle */ } diff --git a/web-src/remote/remote-stock.js b/web-src/remote/remote-stock.js new file mode 100644 index 0000000000..337ac8cdde --- /dev/null +++ b/web-src/remote/remote-stock.js @@ -0,0 +1,425 @@ +//# dc.js Getting Started and How-To Guide +'use strict'; + +// ### Create Chart group + +// This allows associating all charts that are linked together and should respond to changes in other charts +const chartGroup = new dc.ChartGroup(); +const filterStorage = chartGroup.filterStorage; + +// Remoting +const remoteDataSource = new dc.RemoteDataSource({ + chartGroup, + remoteURL: 'http://localhost:3030/api/stock', +}); + +const remoteSimpleAdapter = dimension => + new dc.RemoteSimpleAdapter({ dimension, remoteDataSource }); +const remoteMultiAdapter = dimension => new dc.RemoteMultiAdapter({ dimension, remoteDataSource }); + +// ### Create Chart Objects + +// Create chart objects associated with the container elements identified by the css selector. +// Note: It is often a good idea to have these objects accessible at the global scope so that they can be modified or +// filtered by other page controls. +const gainOrLossChart = new dc.PieChart('#gain-loss-chart', chartGroup); +const fluctuationChart = new dc.BarChart('#fluctuation-chart', chartGroup); +const quarterChart = new dc.PieChart('#quarter-chart', chartGroup); +const dayOfWeekChart = new dc.RowChart('#day-of-week-chart', chartGroup); +const moveChart = new dc.LineChart('#monthly-move-chart', chartGroup); +const volumeChart = new dc.BarChart('#monthly-volume-chart', chartGroup); +const yearlyBubbleChart = new dc.BubbleChart('#yearly-bubble-chart', chartGroup); +const nasdaqCount = new dc.DataCount('.dc-data-count', chartGroup); +// const nasdaqTable = new dc.DataTable('.dc-data-table', chartGroup); + +// ### Anchor Div for Charts +/* +// A div anchor that can be identified by id +
+// Title or anything you want to add above the chart +
Days by Gain or Loss
+// ##### .turnOnControls() + +// If a link with css class `reset` is present then the chart +// will automatically hide/show it based on whether there is a filter +// set on the chart (e.g. slice selection for pie chart and brush +// selection for bar chart). Enable this with `chart.turnOnControls(true)` + +// By default, dc.js >=2.1 uses `display: none` to control whether or not chart +// controls are shown. To use `visibility: hidden` to hide/show controls +// without disrupting the layout, set `chart.controlsUseVisibility(true)`. + +
+ +
+// dc.js will also automatically inject the current filter value into +// any html element with its css class set to `filter` +
+ +
+*/ + +const dateFormatSpecifier = '%m/%d/%Y'; +const dateFormat = d3.timeFormat(dateFormatSpecifier); +const numberFormat = d3.format('.2f'); + +const ndx = { size: () => remoteDataSource.data.totalRecords }; +const all = { value: () => remoteDataSource.data.selectedRecords }; + +//### Data Providers + +// dimension is used as a marker against which filters are stored - can be strings +// If two charts share the same dimension filters would be shared. + +const yearlyBubbleDataProvider = remoteSimpleAdapter('yearlyDimension'); +const gainOrLossDataProvider = remoteSimpleAdapter('gainOrLoss'); +const quarterDataProvider = remoteSimpleAdapter('quarter'); +const dayOfWeekDataProvider = remoteSimpleAdapter('dayOfWeek'); +const fluctuationDataProvider = remoteSimpleAdapter('fluctuation'); +const moveDataProvider = remoteMultiAdapter('moveMonths'); +const volumeDataProvider = remoteMultiAdapter('moveMonths'); + +//### Define Chart Attributes +// Define chart attributes using fluent methods. See the +// [dc.js API Reference](https://dc-js.github.io/dc.js/docs/html/) for more information +// + +//#### Bubble Chart + +//Create a bubble chart and use the given css selector as anchor. You can also specify +//an optional chart group for this chart to be scoped within. When a chart belongs +//to a specific group then any interaction with the chart will only trigger redraws +//on charts within the same chart group. +//
API: [Bubble Chart](https://dc-js.github.io/dc.js/docs/html/BubbleChart.html) +yearlyBubbleChart /* dc.bubbleChart('#yearly-bubble-chart', 'chartGroup') */ + // (_optional_) define chart width, `default = 200` + .width(990) + // (_optional_) define chart height, `default = 200` + .height(250) + // (_optional_) define chart transition duration, `default = 750` + .transitionDuration(1500) + .margins({ top: 10, right: 50, bottom: 30, left: 40 }) + .dataProvider(yearlyBubbleDataProvider) + // (_optional_) define color function or array for bubbles: [ColorBrewer](http://colorbrewer2.org/) + .colors(d3.schemeRdYlGn[9]) + //(optional) define color domain to match your data domain if you want to bind data or color + .colorDomain([-500, 500]) + //##### Accessors + + //Accessor functions are applied to each value returned by the grouping + + // `.colorAccessor` - the returned value will be passed to the `.colors()` scale to determine a fill color + .colorAccessor(d => d.value.absGain) + // `.keyAccessor` - the `X` value will be passed to the `.x()` scale to determine pixel location + .keyAccessor(p => p.value.absGain) + // `.radiusValueAccessor` - the value will be passed to the `.r()` scale to determine radius size; + // by default this maps linearly to [0,100] + .radiusValueAccessor(p => p.value.fluctuationPercentage) + .maxBubbleRelativeSize(0.3) + .x(d3.scaleLinear().domain([-2500, 2500])) + .y(d3.scaleLinear().domain([-100, 100])) + .r(d3.scaleLinear().domain([0, 4000])) + //##### Elastic Scaling + + //`.elasticY` and `.elasticX` determine whether the chart should rescale each axis to fit the data. + .elasticY(true) + .elasticX(true) + //`.yAxisPadding` and `.xAxisPadding` add padding to data above and below their max values in the same unit + //domains as the Accessors. + .yAxisPadding(100) + .xAxisPadding(500) + // (_optional_) render horizontal grid lines, `default=false` + .renderHorizontalGridLines(true) + // (_optional_) render vertical grid lines, `default=false` + .renderVerticalGridLines(true) + // (_optional_) render an axis label below the x axis + .xAxisLabel('Index Gain') + // (_optional_) render a vertical axis lable left of the y axis + .yAxisLabel('Index Gain %') + //##### Labels and Titles + + //Labels are displayed on the chart for each bubble. Titles displayed on mouseover. + // (_optional_) whether chart should render labels, `default = true` + .renderLabel(true) + .label(p => p.key) + // (_optional_) whether chart should render titles, `default = false` + .renderTitle(true) + .title(p => + [ + p.key, + `Index Gain: ${numberFormat(p.value.absGain)}`, + `Index Gain in Percentage: ${numberFormat(p.value.percentageGain)}%`, + `Fluctuation / Index Ratio: ${numberFormat(p.value.fluctuationPercentage)}%`, + ].join('\n') + ) + //#### Customize Axes + + // Set a custom tick format. Both `.yAxis()` and `.xAxis()` return an axis object, + // so any additional method chaining applies to the axis, not the chart. + .yAxis() + .tickFormat(v => `${v}%`); + +// #### Pie/Donut Charts + +// Create a pie chart and use the given css selector as anchor. You can also specify +// an optional chart group for this chart to be scoped within. When a chart belongs +// to a specific group then any interaction with such chart will only trigger redraw +// on other charts within the same chart group. +//
API: [Pie Chart](https://dc-js.github.io/dc.js/docs/html/PieChart.html) +gainOrLossChart /* dc.pieChart('#gain-loss-chart', 'chartGroup') */ + // (_optional_) define chart width, `default = 200` + .width(180) + // (optional) define chart height, `default = 200` + .height(180) + // Define pie radius + .radius(80) + .dataProvider(gainOrLossDataProvider) + // (_optional_) by default pie chart will use `group.key` as its label but you can overwrite it with a closure. + .label(d => { + if (gainOrLossChart.hasFilter() && !gainOrLossChart.hasFilter(d.key)) { + return `${d.key}(0%)`; + } + let label = d.key; + if (all.value()) { + label += `(${Math.floor((d.value / all.value()) * 100)}%)`; + } + return label; + }); +/* + // (_optional_) whether chart should render labels, `default = true` + .renderLabel(true) + // (_optional_) if inner radius is used then a donut chart will be generated instead of pie chart + .innerRadius(40) + // (_optional_) define chart transition duration, `default = 350` + .transitionDuration(500) + // (_optional_) define color array for slices + .colors(['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#dadaeb']) + // (_optional_) define color domain to match your data domain if you want to bind data or color + .colorDomain([-1750, 1644]) + // (_optional_) define color value accessor + .colorAccessor(function(d, i){return d.value;}) + */ +quarterChart /* dc.pieChart('#quarter-chart', 'chartGroup') */ + .width(180) + .height(180) + .radius(80) + .innerRadius(30) + .dataProvider(quarterDataProvider); + +//#### Row Chart + +// Create a row chart and use the given css selector as anchor. You can also specify +// an optional chart group for this chart to be scoped within. When a chart belongs +// to a specific group then any interaction with such chart will only trigger redraw +// on other charts within the same chart group. +//
API: [Row Chart](https://dc-js.github.io/dc.js/docs/html/RowChart.html) +dayOfWeekChart /* dc.rowChart('#day-of-week-chart', 'chartGroup') */ + .width(180) + .height(180) + .margins({ top: 20, left: 10, right: 10, bottom: 20 }) + .dataProvider(dayOfWeekDataProvider) + // Assign colors to each value in the x scale domain + .ordinalColors(['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#dadaeb']) + .label(d => d.key.split('.')[1]) + // Title sets the row text + .title(d => d.value) + .elasticX(true) + .xAxis() + .ticks(4); + +//#### Bar Chart + +// Create a bar chart and use the given css selector as anchor. You can also specify +// an optional chart group for this chart to be scoped within. When a chart belongs +// to a specific group then any interaction with such chart will only trigger redraw +// on other charts within the same chart group. +//
API: [Bar Chart](https://dc-js.github.io/dc.js/docs/html/BarChart.html) +fluctuationChart /* dc.barChart('#volume-month-chart', 'chartGroup') */ + .width(420) + .height(180) + .margins({ top: 10, right: 50, bottom: 30, left: 40 }) + .dataProvider(fluctuationDataProvider) + .elasticY(true) + // (_optional_) whether bar should be center to its x value. Not needed for ordinal chart, `default=false` + .centerBar(true) + // (_optional_) set gap between bars manually in px, `default=2` + .gap(1) + // (_optional_) set filter brush rounding + .round(Math.floor) + .alwaysUseRounding(true) + .x(d3.scaleLinear().domain([-25, 25])) + .renderHorizontalGridLines(true) + // Customize the filter displayed in the control span + .filterPrinter(filters => { + const filter = filters[0]; + let s = ''; + s += `${numberFormat(filter[0])}% -> ${numberFormat(filter[1])}%`; + return s; + }); + +// Customize axes +fluctuationChart.xAxis().tickFormat(v => `${v}%`); +fluctuationChart.yAxis().ticks(5); + +//#### Stacked Area Chart + +//Specify an area chart by using a line chart with `.renderArea(true)`. +//
API: [Stack Mixin](https://dc-js.github.io/dc.js/docs/html/StackMixin.html), +// [Line Chart](https://dc-js.github.io/dc.js/docs/html/LineChart.html) +moveChart /* dc.lineChart('#monthly-move-chart', 'chartGroup') */ + .renderArea(true) + .width(990) + .height(200) + .transitionDuration(1000) + .margins({ top: 30, right: 50, bottom: 25, left: 40 }) + .mouseZoomable(true) + // Specify a "range chart" to link its brush extent with the zoom of the current "focus chart". + .rangeChart(volumeChart) + .x(d3.scaleTime().domain([new Date(1985, 0, 1), new Date(2012, 11, 31)])) + .round(d3.timeMonth.round) + .xUnits(d3.timeMonths) + .elasticY(true) + .renderHorizontalGridLines(true) + //##### Legend + + // Position the legend relative to the chart origin and specify items' height and separation. + .legend(new dc.Legend().x(800).y(10).itemHeight(13).gap(5)) + .brushOn(false) + .dataProvider(moveDataProvider) + // Add the base layer of the stack with group. The second parameter specifies a series name for use in the + // legend. + // The `.valueAccessor` will be used for the base layer + // Title can be called by any stack layer. + .title(d => { + let value = d.value.avg ? d.value.avg : d.value; + if (isNaN(value)) { + value = 0; + } + return `${dateFormat(d.key)}\n${numberFormat(value)}`; + }); + +//#### Range Chart + +// Since this bar chart is specified as "range chart" for the area chart, its brush extent +// will always match the zoom of the area chart. +volumeChart + .width(990) /* dc.barChart('#monthly-volume-chart', 'chartGroup'); */ + .height(40) + .margins({ top: 0, right: 50, bottom: 20, left: 40 }) + .dataProvider(volumeDataProvider) + .centerBar(true) + .gap(1) + .x(d3.scaleTime().domain([new Date(1985, 0, 1), new Date(2012, 11, 31)])) + .round(d3.timeMonth.round) + .alwaysUseRounding(true) + .xUnits(d3.timeMonths); + +//#### Data Count + +// Create a data count widget and use the given css selector as anchor. You can also specify +// an optional chart group for this chart to be scoped within. When a chart belongs +// to a specific group then any interaction with such chart will only trigger redraw +// on other charts within the same chart group. +//
API: [Data Count Widget](https://dc-js.github.io/dc.js/docs/html/DataCount.html) +// +//```html +//
+// +// selected out of records. +//
+//``` + +nasdaqCount /* dc.dataCount('.dc-data-count', 'chartGroup'); */ + .crossfilter(ndx) + .groupAll(all) + // (_optional_) `.html` sets different html when some records or all records are selected. + // `.html` replaces everything in the anchor with the html given using the following function. + // `%filter-count` and `%total-count` are replaced with the values obtained. + .html({ + some: + '%filter-count selected out of %total-count records' + + " | Reset All", + all: 'All records selected. Please click on the graph to apply filters.', + }); + +//#### Data Table + +// Create a data table widget and use the given css selector as anchor. You can also specify +// an optional chart group for this chart to be scoped within. When a chart belongs +// to a specific group then any interaction with such chart will only trigger redraw +// on other charts within the same chart group. +//
API: [Data Table Widget](https://dc-js.github.io/dc.js/docs/html/DataTable.html) +// +// You can statically define the headers like in +// +// ```html +// +//
+// +//
+// Date +// Open +// Close +// Change +// Volume +//
+// +//
+// ``` +// or do it programmatically using `.columns()`. + +// nasdaqTable /* dc.dataTable('.dc-data-table', 'chartGroup') */ +// .dimension(dateDimension) +// // Specify a section function to nest rows of the table +// .section(d => { +// const format = d3.format('02d'); +// return `${d.dd.getFullYear()}/${format(d.dd.getMonth() + 1)}`; +// }) +// // (_optional_) max number of records to be shown, `default = 25` +// .size(10) +// // There are several ways to specify the columns; see the data-table documentation. +// // This code demonstrates generating the column header automatically based on the columns. +// .columns([ +// // Use the `d.date` field; capitalized automatically +// 'date', +// // Use `d.open`, `d.close` +// 'open', +// 'close', +// { +// // Specify a custom format for column 'Change' by using a label with a function. +// label: 'Change', +// format: function (d) { +// return numberFormat(d.close - d.open); +// }, +// }, +// // Use `d.volume` +// 'volume', +// ]) +// +// // (_optional_) sort using the given field, `default = function(d){return d;}` +// .sortBy(d => d.dd) +// // (_optional_) sort order, `default = d3.ascending` +// .order(d3.ascending) +// // (_optional_) custom renderlet to post-process chart using [D3](http://d3js.org) +// .on('renderlet', table => { +// table.selectAll('.dc-table-group').classed('info', true); +// }); + +//#### Rendering + +//simply call `.renderAll()` to render all charts on the page +chartGroup.renderAll(); + +/* + // Or you can render charts belonging to a specific chart group + dc.renderAll('group'); + // Once rendered you can call `.redrawAll()` to update charts incrementally when the data + // changes, without re-rendering everything + dc.redrawAll(); + // Or you can choose to redraw only those charts associated with a specific chart group + dc.redrawAll('group'); + */ diff --git a/web-src/remote/remote.html b/web-src/remote/remote.html new file mode 100644 index 0000000000..e7d58260ee --- /dev/null +++ b/web-src/remote/remote.html @@ -0,0 +1,304 @@ + + + + dc.js - Current Filters + + + + + + + + + + + + + + +
+
+
+ Saved Filter States + save current +
+
+
+ +
+
+ Current Filters + clear all +
+
+
+ +
+
+ Yearly Performance (radius: fluctuation/index ratio, + color: gain/loss) + + +
+
+
+ +
+
+ Days by Gain/Loss + + +
+
+ +
+ Quarters + + +
+
+ +
+ Day of Week + + +
+
+ +
+ Days by Fluctuation(%) + + + +
+
+
+ +
+
+ Monthly Index Abs Move & Volume/500,000 Chart + + + +
+
+
+ +
+
+

+ select a time range to zoom in +

+
+ +
+
+
+ selected out of + records | + Reset All +
+
+
+
+ +
+
+ + + + + + + + + +