diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..503dbe8 --- /dev/null +++ b/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "browsers": ["> 1%"] + } + } + ] + ] +} diff --git a/.gitignore b/.gitignore index e2c0817..0e95075 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .idea .DS_Store -node_modules \ No newline at end of file +node_modules +*.lock +public/assets/scripts/ +public/assets/styles/ \ No newline at end of file diff --git a/README.md b/README.md index 06087e4..43cb943 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,82 @@ [AnyChart - Robust JavaScript/HTML5 Chart library for any project](https://www.anychart.com) ## AnyStock Big Data Speed Test + Our JavaScript charts support rendering thousands of data points in milliseconds. Run a speedtest or try real-time live data streaming. [Big Data Speed Test | AnyChart](https://www.anychart.com/solutions/big-data-speed-test/) ## Modifying source code + There are two possible options of modifying demo source code, [using Node.js and npm](#using-nodejs-and-npm) and [with no additional requirements](#with-no-additional-requirements). Please, ensure you have all [requirements](#installing-requirements) installed before running. -Then, to run demo with Node.js and npm, use following commands: -``` + +### Download the project + +```bash git clone git@github.com:anychart-solutions/big-data-speed-test.git cd big-data-speed-test -gulp +``` + +### Install the project dependencies + +```bash +yarn +``` + +### Run the project for development (with hot-reload) + +```bash +yarn dev ``` Now, when all environment is up and running, you may use following instructions to modify source code: -* To modify demo stylesheets, edit `src/sass/*.scss` files. -* To modify demo JavaScript, edit `src/js/*.js` file. -* To modify demo markup, edit `src/index.html` file. + +- To modify demo stylesheets, edit `src/styles/*.scss` files. +- To modify demo JavaScript, edit `src/scripts/*.js` file. +- To modify demo markup, edit `public/index.html` file. ### With no additional requirements + This option doesn't require Node.js and npm installation. Also it imposes some limitations on demo source code modification process. -* To run the demo, please, open index.html page. -* To modify demo stylesheets, please add your own `` section to `src/index.html` file. -* Unfortunately, here is no way to modify demo JavaScript code except adding your own ` \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 29cbe2f..0000000 --- a/gulpfile.js +++ /dev/null @@ -1,66 +0,0 @@ -var gulp = require('gulp'); -var sass = require('gulp-sass'); -var uglify = require('gulp-uglify'); -var rename = require('gulp-rename'); -var concat = require('gulp-concat'); -var htmlmin = require('gulp-htmlmin'); -var plumber = require('gulp-plumber'); -var autoprefixer = require('gulp-autoprefixer'); -var cleanCSS = require('gulp-clean-css'); -var sourcemaps = require('gulp-sourcemaps'); -var open = require('gulp-open'); - -var config = { - sourceDir: './src/', - publicDir: './dist/' -}; - -/* Scripts task */ -gulp.task('scripts', function () { - return gulp.src([config.sourceDir + 'js/perfomance.js', config.sourceDir + 'js/data-generator.js', config.sourceDir + 'js/index.js']) - .pipe(concat('app.js')) - .pipe(gulp.dest(config.sourceDir)) - .pipe(uglify()) - .pipe(gulp.dest(config.publicDir)); -}); - -/* Styles task */ -gulp.task('styles', function () { - return gulp.src(config.sourceDir + '/sass/app.scss') - .pipe(sass().on('error', sass.logError)) - .pipe(sourcemaps.write('.')) - .pipe(autoprefixer()) - .pipe(gulp.dest(config.sourceDir)) - .pipe(cleanCSS()) - .pipe(gulp.dest(config.publicDir)) -}); - -/* HTML min */ -gulp.task('minifyHtml', function () { - return gulp.src(config.sourceDir + 'index.html') - .pipe(htmlmin({collapseWhitespace: true})) - .pipe(gulp.dest(config.publicDir)); -}); - -// Open file with default application -gulp.task('openSrc', function(){ - gulp.src('./src/index.html') - .pipe(open()); -}); - -// Open file with default application -gulp.task('openDist', function(){ - gulp.src('./dist/index.min.html') - .pipe(open()); -}); - -/* Watch scss, js and html files, doing different things with each. */ -gulp.task('default', ['scripts', 'styles', 'openSrc', 'minifyHtml'], function () { - /* Watch scss, run the sass task on change. */ - gulp.watch(config.sourceDir + 'sass/**/*.*', ['styles']); - /* Watch app.js file, run the scripts task on change. */ - gulp.watch(config.sourceDir + 'js/**/*.*', ['scripts']); -}); - -/* Production */ -gulp.task('prod', ['scripts', 'styles', 'minifyHtml', 'openDist']); \ No newline at end of file diff --git a/package.json b/package.json index 3815c0e..b06550a 100644 --- a/package.json +++ b/package.json @@ -30,21 +30,32 @@ "framework", "VML" ], + "main": "index.js", + "scripts": { + "dev:assets": "webpack --watch", + "dev:start": "live-server --open=./public/ --host=localhost --watch=./public/", + "dev": "npm-run-all -p dev:*", + "build": "cross-env NODE_ENV=production webpack" + }, "dependencies": { - "anychart": "^8.0.0" + "anychart": "^8.9.0" }, "devDependencies": { - "gulp": "^3.9.1", - "gulp-autoprefixer": "^3.1.1", - "gulp-clean-css": "^2.0.12", - "gulp-concat": "^2.6.0", - "gulp-htmlmin": "^3.0.0", - "gulp-open": "^2.0.0", - "gulp-plumber": "^1.1.0", - "gulp-rename": "^1.2.2", - "gulp-sass": "^3.1.0", - "gulp-sourcemaps": "^1.6.0", - "gulp-uglify": "^2.0.0", - "gulp-util": "^3.0.7" + "@babel/core": "^7.13.14", + "@babel/preset-env": "^7.13.12", + "babel-loader": "^8.2.2", + "cross-env": "^7.0.3", + "css-loader": "^5.2.0", + "cssnano": "^4.1.10", + "file-loader": "^6.2.0", + "live-server": "^1.2.1", + "mini-css-extract-plugin": "^1.4.0", + "node-sass": "^5.0.0", + "npm-run-all": "^4.1.5", + "postcss-loader": "^5.2.0", + "postcss-preset-env": "^6.7.0", + "sass-loader": "^11.0.1", + "webpack": "^5.30.0", + "webpack-cli": "^4.6.0" } -} +} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..ac48f50 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,13 @@ +const postcssPresetEnv = require('postcss-preset-env'); +if (process.env.NODE_ENV === 'production') { + module.exports = { + plugins: [ + postcssPresetEnv({ + browsers: ['> 1%'], + }), + require('cssnano'), + ], + }; + return; +} +module.exports = {}; diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..47c7863 --- /dev/null +++ b/public/index.html @@ -0,0 +1,193 @@ + + + + + + + + + + + + + AnyChart Big Data Speed Test + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + +
+
+
+
+ +
+
+
+
Chart Settings
+ +
+ +
+
+ +
+
Streaming
+ +
+ +
+
+ +
+
+ +
+
+
Rendering 50,000 data points
+ +
+ title: + value +
+
+
+ + + + + + + + + + + + + + + + + + + + diff --git a/src/app.css b/src/app.css deleted file mode 100644 index 3000181..0000000 --- a/src/app.css +++ /dev/null @@ -1,183 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600,700,900,300italic,400italic,600italic,700italic,900italic); -@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700); -@import url(https://fonts.googleapis.com/css?family=PT+Serif:400,700,400italic,700italic); -.btn-primary { - color: #ffffff; - background-color: #1c75ba; - border-color: #1c75ba; - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.5); - text-transform: uppercase; } - .btn-primary:hover, .btn-primary:focus, .btn-primary.focus, .btn-primary:active, .btn-primary.active, - .btn-primary .open > .dropdown-toggle.btn-primary { - color: #ffffff; - background-color: #56a7e6; - border-color: #56a7e6; } - -.select > .btn { - font-weight: 400; - border-color: #c5c8d1; - background: #fff; } - -.btn-lg, -.btn-group-lg > .btn { - padding: 8.5px 16px; - font-size: 16px; - line-height: 1.3333333; - border-radius: 3px; } - -.anychart-loader { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1000; - background: #fff; } - .anychart-loader .rotating-cover { - width: 70px; - height: 70px; - position: absolute; - top: 50%; - margin-top: -35px; - left: 50%; - margin-left: -35px; } - .anychart-loader .rotating-plane { - display: block; - width: 100%; - height: 100%; - border-radius: 20%; - border: 5px solid #1c75ba; - margin: 0 auto; - position: relative; - animation: rotateplane 3s infinite; } - .anychart-loader .chart-row { - position: absolute; - top: 10px; - bottom: 0; - left: 10px; - right: 10px; - letter-spacing: -3px; - line-height: 0; - font-size: 0; - white-space: nowrap; } - .anychart-loader .chart-row .chart-col { - display: inline-block; - width: 25%; - height: 90%; - background: #000; - margin: 0 12.5% 0 0; - vertical-align: bottom; } - .anychart-loader .chart-row .chart-col.green { - background: #26a957; - height: 50%; - animation: blinkplane 1.5s infinite; } - .anychart-loader .chart-row .chart-col.orange { - background: #ff8207; - height: 70%; - animation: blinkplane 1.5s infinite 0.25s; } - .anychart-loader .chart-row .chart-col.red { - background: #f0402e; - height: 90%; - animation: blinkplane 1.5s infinite 0.5s; } - -@keyframes rotateplane { - 0% { - transform: perspective(120px) rotateX(0deg) rotateY(0deg); - opacity: 1; } - 25% { - transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg); - opacity: 0.3; } - 50% { - transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); - opacity: 1; } - 75% { - transform: perspective(120px) rotateX(0deg) rotateY(-180.1deg); - opacity: 0.3; } - 100% { - transform: perspective(120px) rotateX(0deg) rotateY(0deg); - opacity: 1; } } - -@keyframes blinkplane { - 0% { - opacity: 1; } - 50% { - opacity: 0.01; } - 100% { - opacity: 1; } } - -/* fonts */ -html { - font-size: 10px; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } - -body { - font-family: 'Source Sans Pro', sans-serif; - font-size: 15px; - line-height: 1.42857143; - color: #323c44; - background-color: #ffffff; } - -h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; } - -h4, .h4, h5, .h5, h6, .h6 { - margin-top: 0; - margin-bottom: 20px; } - -h5, .h5 { - font-size: 20px; } - -p, ul, ol { - margin: 0 0 20px; } - -.anystock-speed-test-container { - margin-top: 20px; - position: relative; - width: 100%; - height: 595px; } - -.anystock-speed-test-chart { - position: absolute; - left: 0; - right: 0; - height: 595px; } - -.anystock-speed-test-controls { - position: absolute; - top: 0; - right: 15px; - min-width: 270px; } - -.anystock-speed-test-result { - position: absolute; - width: 270px; - min-width: 270px; - right: 15px; - top: 310px; } - -.anystock-speed-test-result-row-title { - float: left; } - -.anystock-speed-test-result-row-value { - float: right; } - -.anystock-speed-test-streamPointsCount { - float: left; - width: 160px; } - -.anystock-speed-test-streamInterval { - margin-left: 20px; - float: left; - width: 50px; } - -#anystock-speed-test-stream-stat { - margin-top: 20px; } - -.pull-left { - float: left !important; } - -.pull-right { - float: right !important; } diff --git a/src/app.js b/src/app.js deleted file mode 100644 index 056db9f..0000000 --- a/src/app.js +++ /dev/null @@ -1,388 +0,0 @@ -(function() { - var PerfMeter = function () { - this.started = {}; - this.finished = {}; - }; - PerfMeter.prototype.start = function(label) { - this.started[label] = window.performance ? window.performance.now() : +new Date(); - }; - PerfMeter.prototype.end = function(label) { - var now = window.performance ? window.performance.now() : +new Date(); - this.finished[label] = now - (this.started[label] || now); - delete this.started[label]; - }; - PerfMeter.prototype.get = function(label) { - return this.finished[label]; - }; - PerfMeter.prototype.print = function(groupName, var_args) { - console.group(groupName || 'Performance log'); - for (var i = 1; i < arguments.length; i++) { - var val = this.get(arguments[i]); - if (typeof val == 'number') val = val.toFixed(3); - else val = '' + val; - console.log(arguments[i] + ': ' + val + 'ms'); - } - console.groupEnd(groupName || 'Performance log'); - }; - window.basePerfMeter = new PerfMeter(); - window.perfMeter = new PerfMeter(); -})(); - -function generate5MinsOHLCData(startDate, rowsCount, openValue, spread, opt_startVolume) { - function rand(opt_spreadMult, opt_spreadAdd) { - var spreadMult = (isNaN(opt_spreadMult) ? 1 : opt_spreadMult) * spread; - var spreadAdd = isNaN(opt_spreadAdd) ? -0.5 : +opt_spreadAdd; - return Math.round(((Math.random() * spreadMult) + spreadMult * spreadAdd) * 100) * 0.01; - } - - function randVolume(opt_prev) { - if (!opt_prev) - return Math.round(Math.random() * 1e3) + 1e3; - var diff = Math.round(Math.random() * 2e2) - 4e2; - return Math.abs(opt_prev + diff); - } - - function nextDate(date) { - date.setTime(date.getTime() + 1000 * 60 * 5); - if (date.getUTCHours() > 18) { - date.setUTCDate(date.getUTCDate() + 1); - date.setUTCHours(9); - } - if (date.getUTCDay() == 6) { - date.setUTCDate(date.getUTCDate() + 2); - } else if (date.getUTCDay() == 0) { - date.setUTCDate(date.getUTCDate() + 1); - } - } - - startDate = startDate instanceof Date ? startDate : new Date(startDate); - var current = new Date(startDate.getTime()); - var index = -1; - - var data = []; - var open = openValue, close, high, low; - var volume = randVolume(opt_startVolume); - while (++index < rowsCount) { - var diff = rand(); - close = open + diff; - if (close < 0) close = open - diff; - high = Math.max(open, close) + rand(0.2, 0); - low = Math.max(Math.min(open, close) - rand(0.2, 0), 0); - volume = randVolume(volume); - data.push([ - current.getTime(), - open, - high, - low, - close, - volume - ]); - open = close + rand(0.0001); - nextDate(current); - } - return { - data: data, - lastValue: open, - lastDate: current, - lastVolume: volume - }; -} - - -var rawData; -var perfMeter; -var columnMapping, scrollerMapping; -var streamingTimerId; -var streamingAverage = NaN; - -var isFirstInit = true; - -var chart; -var dataTable; - -var chartConfiguration = 'ohlc-basic'; -var initialPointsCount = 50000; -var streamPointsCount = 500; -var streamingInterval = 20; - -var index = 0; - -var chartType = $('#anystock-speed-test-chartType-select').find('option:selected').attr('data-chart'); - -var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; - -anychart.onDocumentReady(function () { - var resultWidth = $('#anystock-speed-test-base-result').width(); - $('#anystock-speed-test-base-chart').css({'right': resultWidth + 50}); - - $('.select').selectpicker(); - - $('#start-base-chart-stream-btn').click(toggleStreaming); - - // first init - createChart(initialPointsCount, chartConfiguration, execCreateStock); - - $('.select[data-action-type]').on('change', changeChart); -}); - -function changeChart() { - var $optionSelected = $(this).find("option:selected"); - var value = $optionSelected.val(); - var type = $(this).attr('data-action-type'); - - if (type == 'configurationType') { - chartConfiguration = value; - stopStreaming(); - createChart(initialPointsCount, chartConfiguration, getFunctionForCreateChart(chartType)); - } else if (type == 'initialPointsCount') { - initialPointsCount = value; - stopStreaming(); - createChart(initialPointsCount, chartConfiguration, getFunctionForCreateChart(chartType)) - } else if (type == 'streamPointsCount') { - streamPointsCount = value; - removeStreamingStat(); - if (isStreaming()) { - stopStreaming(); - startStreaming(); - } - } else if (type == 'streamingInterval') { - streamingInterval = value; - removeStreamingStat(); - if (isStreaming()) { - stopStreaming(); - startStreaming(); - } - } -} - -function getFunctionForCreateChart(chartType) { - switch (chartType) { - case 'stock-ohlc': - return execCreateStock; - } -} - -function createChart(pointsCount, chartConfiguration, createChartFunc) { - if (chart) { - chart.dispose(); - chart = null; - index = 0; - $('#anystock-speed-test-base-chart').empty(); - } - - if (!isFirstInit) { - showPreloader(); - } - - setTimeout(function () { - isFirstInit = false; - createChartFunc(pointsCount, chartConfiguration); - }, 1); - -} - -function execCreateStock(pointsCount, chartConfiguration) { - rawData = generate5MinsOHLCData(new Date(new Date().getUTCFullYear() - 4, 0), pointsCount, 100, 10, 100); - - perfMeter.start('Total'); - - perfMeter.start('Creating data storage'); - dataTable = anychart.data.table(0); - dataTable.addData(rawData.data); - mainMapping = dataTable.mapAs(); - // first 3 fields for OHLC and Candlestick - mainMapping.addField('open', 1, 'first'); - mainMapping.addField('high', 2, 'max'); - mainMapping.addField('low', 3, 'min'); - mainMapping.addField('close', 4, 'last'); - // this one for line and spline - mainMapping.addField('value', 1, 'first'); - - columnMapping = dataTable.mapAs(); - columnMapping.addField('value', 5, 'sum'); - - scrollerMapping = dataTable.mapAs(); - scrollerMapping.addField('value', 4, 'last'); - - perfMeter.end('Creating data storage'); - - perfMeter.start('Creating chart instance'); - chart = anychart.stock(); - chart.listen('chartDraw', function () { - $('#loader-wrapper').remove(); - hidePreloader(); - }); - switch(chartConfiguration) { - case 'ohlc-basic': - setBasicChartSettings('ohlc'); - break; - case 'ohlc-advanced': - setAdvancedChartSettings('ohlc'); - break; - case 'candlestick-basic': - setBasicChartSettings('candlestick'); - break; - case 'candlestick-advanced': - setAdvancedChartSettings('candlestick'); - break; - case 'line-basic': - setBasicChartSettings('line'); - break; - case 'spline-basic': - setBasicChartSettings('spline'); - break; - default: - setBasicChartSettings(); - } - - chart.container('anystock-speed-test-base-chart'); - perfMeter.end('Creating chart instance'); - - perfMeter.start('Chart rendering'); - chart.draw(); - rawData.data = null; - perfMeter.end('Chart rendering'); - - perfMeter.end('Total'); - - var resultContainer = $('#anystock-speed-test-base-result'); - resultContainer.empty(); - resultContainer.append('
Rendering ' + addCommas(pointsCount) + ' Data Points
'); - resultContainer.append(generateHTMLStatRecord('Creating data storage', perfMeter.get('Creating data storage'))); - resultContainer.append(generateHTMLStatRecord('Creating chart instance', perfMeter.get('Creating chart instance'))); - resultContainer.append(generateHTMLStatRecord('Chart rendering', perfMeter.get('Chart rendering'))); - resultContainer.append(generateHTMLStatRecord('Total', perfMeter.get('Total'))); -} - -function setBasicChartSettings(type) { - chart.plot(0).defaultSeriesType(type); - chart.plot(0).addSeries(mainMapping); - chart.plot(0).getSeries(0).name('Main Series'); - chart.padding(10, 10, 10, 65); - chart.scroller().line(scrollerMapping); -} - -function setAdvancedChartSettings(type) { - chart.plot(0).defaultSeriesType(type); - var mainSeries = chart.plot(0).addSeries(mainMapping); - mainSeries[0].name('Main Series'); - - var columnSeies = chart.plot(1).column(columnMapping); - columnSeies.name('Column'); - - chart.padding(10, 10, 10, 50); - chart.plot(1).height('30%'); - - chart.plot(1).yAxis().labels().format(function () { - var val = this['tickValue']; - var neg = val < 0; - val = Math.abs(val); - if (val / 1e15 >= 1) { - return (val / 1e9).toFixed(0) + 'Q'; - } else if (val / 1e12 >= 1) { - return (val / 1e9).toFixed(0) + 'T'; - } else if (val / 1e9 >= 1) { - return (val / 1e9).toFixed(0) + 'B'; - } else if (val / 1e6 >= 1) { - return (val / 1e6).toFixed(0) + 'M'; - } else if (val / 1e3 >= 1) { - return (val / 1e3).toFixed(0) + 'K'; - } - return neg ? '-' + val : val; - }); - - - chart.scroller().line(scrollerMapping); -} - -function isStreaming() { - return !isNaN(streamingTimerId); -} - -function toggleStreaming() { - if (!isStreaming()) { - startStreaming(); - } else { - stopStreaming(); - } -} - -function startStreaming() { - $('#start-base-chart-stream-btn').html('Stop Data Streaming'); - - streamingTimerId = setInterval(function () { - - if (requestAnimationFrame) { - requestAnimationFrame(streamData); - } else { - streamData(); - } - - function streamData() { - rawData = generate5MinsOHLCData(rawData.lastDate, streamPointsCount, rawData.lastValue, 10, rawData.lastVolume); - - perfMeter.start(streamPointsCount); - dataTable.addData(rawData.data, true); - rawData.data = null; - perfMeter.end(streamPointsCount); - - if (isNaN(streamingAverage)) streamingAverage = perfMeter.get(streamPointsCount); - - var streamingStat = $('#anystock-speed-test-stream-stat').length; - if (!streamingStat) { - var resultContainer = $('#anystock-speed-test-base-result'); - resultContainer.append('
Streaming ' + addCommas(streamPointsCount) + ' Data Points
'); - resultContainer.append(generateHTMLStatRecord('Streaming interval', streamingInterval, undefined, 'anystock-speed-test-stream-interval')); - resultContainer.append(generateHTMLStatRecord('Average rendering time', Math.round(streamingAverage), undefined, 'anystock-speed-test-stream-average')); - } else { - streamingAverage = (streamingAverage + perfMeter.get(streamPointsCount)) / 2; - $('#anystock-speed-test-stream-average .anystock-speed-test-result-row-value').html(Math.round(streamingAverage) + 'ms'); - } - } - - }, streamingInterval); -} - -function stopStreaming() { - if (isStreaming()) { - clearInterval(streamingTimerId); - streamingTimerId = NaN; - } - streamingAverage = NaN; - $('#start-base-chart-stream-btn').html('Start Data Streaming'); -} - -function removeStreamingStat() { - $('#anystock-speed-test-stream-stat').remove(); - $('#anystock-speed-test-stream-interval').remove(); - $('#anystock-speed-test-stream-average').remove(); -} - -function showPreloader() { - $('#anystock-speed-test-base-chart').append('
'); -} - -function hidePreloader() { - $('#loader-wrapper-chart').hide(); -} - -function generateHTMLStatRecord(title, value, postfix, opt_id) { - if (postfix === undefined) postfix = 'ms'; - opt_id = opt_id ? 'id="' + opt_id + '" ' : ''; - return '
' + - '' + title + ': ' + - '' + Math.round(value) + postfix + '' + - '
'; -} - -function addCommas(nStr) { - nStr += ''; - x = nStr.split('.'); - x1 = x[0]; - x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; -} diff --git a/src/index.html b/src/index.html deleted file mode 100644 index f3c3f66..0000000 --- a/src/index.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - AnyChart Big Data Speed Test - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - -
-
-
-
- -
-
-
-
Chart Settings
- -
- -
-
- -
-
Streaming
- -
- -
-
- -
-
- -
-
-
Rendering 50,000 data points
- -
- title: - value -
-
-
- - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/js/data-generator.js b/src/js/data-generator.js deleted file mode 100644 index 29ea202..0000000 --- a/src/js/data-generator.js +++ /dev/null @@ -1,60 +0,0 @@ -function generate5MinsOHLCData(startDate, rowsCount, openValue, spread, opt_startVolume) { - function rand(opt_spreadMult, opt_spreadAdd) { - var spreadMult = (isNaN(opt_spreadMult) ? 1 : opt_spreadMult) * spread; - var spreadAdd = isNaN(opt_spreadAdd) ? -0.5 : +opt_spreadAdd; - return Math.round(((Math.random() * spreadMult) + spreadMult * spreadAdd) * 100) * 0.01; - } - - function randVolume(opt_prev) { - if (!opt_prev) - return Math.round(Math.random() * 1e3) + 1e3; - var diff = Math.round(Math.random() * 2e2) - 4e2; - return Math.abs(opt_prev + diff); - } - - function nextDate(date) { - date.setTime(date.getTime() + 1000 * 60 * 5); - if (date.getUTCHours() > 18) { - date.setUTCDate(date.getUTCDate() + 1); - date.setUTCHours(9); - } - if (date.getUTCDay() == 6) { - date.setUTCDate(date.getUTCDate() + 2); - } else if (date.getUTCDay() == 0) { - date.setUTCDate(date.getUTCDate() + 1); - } - } - - startDate = startDate instanceof Date ? startDate : new Date(startDate); - var current = new Date(startDate.getTime()); - var index = -1; - - var data = []; - var open = openValue, close, high, low; - var volume = randVolume(opt_startVolume); - while (++index < rowsCount) { - var diff = rand(); - close = open + diff; - if (close < 0) close = open - diff; - high = Math.max(open, close) + rand(0.2, 0); - low = Math.max(Math.min(open, close) - rand(0.2, 0), 0); - volume = randVolume(volume); - data.push([ - current.getTime(), - open, - high, - low, - close, - volume - ]); - open = close + rand(0.0001); - nextDate(current); - } - return { - data: data, - lastValue: open, - lastDate: current, - lastVolume: volume - }; -} - diff --git a/src/js/index.js b/src/js/index.js deleted file mode 100644 index 6b569b7..0000000 --- a/src/js/index.js +++ /dev/null @@ -1,297 +0,0 @@ -var rawData; -var perfMeter; -var columnMapping, scrollerMapping; -var streamingTimerId; -var streamingAverage = NaN; - -var isFirstInit = true; - -var chart; -var dataTable; - -var chartConfiguration = 'ohlc-basic'; -var initialPointsCount = 50000; -var streamPointsCount = 500; -var streamingInterval = 20; - -var index = 0; - -var chartType = $('#anystock-speed-test-chartType-select').find('option:selected').attr('data-chart'); - -var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame; - -anychart.onDocumentReady(function () { - var resultWidth = $('#anystock-speed-test-base-result').width(); - $('#anystock-speed-test-base-chart').css({'right': resultWidth + 50}); - - $('.select').selectpicker(); - - $('#start-base-chart-stream-btn').click(toggleStreaming); - - // first init - createChart(initialPointsCount, chartConfiguration, execCreateStock); - - $('.select[data-action-type]').on('change', changeChart); -}); - -function changeChart() { - var $optionSelected = $(this).find("option:selected"); - var value = $optionSelected.val(); - var type = $(this).attr('data-action-type'); - - if (type == 'configurationType') { - chartConfiguration = value; - stopStreaming(); - createChart(initialPointsCount, chartConfiguration, getFunctionForCreateChart(chartType)); - } else if (type == 'initialPointsCount') { - initialPointsCount = value; - stopStreaming(); - createChart(initialPointsCount, chartConfiguration, getFunctionForCreateChart(chartType)) - } else if (type == 'streamPointsCount') { - streamPointsCount = value; - removeStreamingStat(); - if (isStreaming()) { - stopStreaming(); - startStreaming(); - } - } else if (type == 'streamingInterval') { - streamingInterval = value; - removeStreamingStat(); - if (isStreaming()) { - stopStreaming(); - startStreaming(); - } - } -} - -function getFunctionForCreateChart(chartType) { - switch (chartType) { - case 'stock-ohlc': - return execCreateStock; - } -} - -function createChart(pointsCount, chartConfiguration, createChartFunc) { - if (chart) { - chart.dispose(); - chart = null; - index = 0; - $('#anystock-speed-test-base-chart').empty(); - } - - if (!isFirstInit) { - showPreloader(); - } - - setTimeout(function () { - isFirstInit = false; - createChartFunc(pointsCount, chartConfiguration); - }, 1); - -} - -function execCreateStock(pointsCount, chartConfiguration) { - rawData = generate5MinsOHLCData(new Date(new Date().getUTCFullYear() - 4, 0), pointsCount, 100, 10, 100); - - perfMeter.start('Total'); - - perfMeter.start('Creating data storage'); - dataTable = anychart.data.table(0); - dataTable.addData(rawData.data); - mainMapping = dataTable.mapAs(); - // first 3 fields for OHLC and Candlestick - mainMapping.addField('open', 1, 'first'); - mainMapping.addField('high', 2, 'max'); - mainMapping.addField('low', 3, 'min'); - mainMapping.addField('close', 4, 'last'); - // this one for line and spline - mainMapping.addField('value', 1, 'first'); - - columnMapping = dataTable.mapAs(); - columnMapping.addField('value', 5, 'sum'); - - scrollerMapping = dataTable.mapAs(); - scrollerMapping.addField('value', 4, 'last'); - - perfMeter.end('Creating data storage'); - - perfMeter.start('Creating chart instance'); - chart = anychart.stock(); - chart.listen('chartDraw', function () { - $('#loader-wrapper').remove(); - hidePreloader(); - }); - switch(chartConfiguration) { - case 'ohlc-basic': - setBasicChartSettings('ohlc'); - break; - case 'ohlc-advanced': - setAdvancedChartSettings('ohlc'); - break; - case 'candlestick-basic': - setBasicChartSettings('candlestick'); - break; - case 'candlestick-advanced': - setAdvancedChartSettings('candlestick'); - break; - case 'line-basic': - setBasicChartSettings('line'); - break; - case 'spline-basic': - setBasicChartSettings('spline'); - break; - default: - setBasicChartSettings(); - } - - chart.container('anystock-speed-test-base-chart'); - perfMeter.end('Creating chart instance'); - - perfMeter.start('Chart rendering'); - chart.draw(); - rawData.data = null; - perfMeter.end('Chart rendering'); - - perfMeter.end('Total'); - - var resultContainer = $('#anystock-speed-test-base-result'); - resultContainer.empty(); - resultContainer.append('
Rendering ' + addCommas(pointsCount) + ' Data Points
'); - resultContainer.append(generateHTMLStatRecord('Creating data storage', perfMeter.get('Creating data storage'))); - resultContainer.append(generateHTMLStatRecord('Creating chart instance', perfMeter.get('Creating chart instance'))); - resultContainer.append(generateHTMLStatRecord('Chart rendering', perfMeter.get('Chart rendering'))); - resultContainer.append(generateHTMLStatRecord('Total', perfMeter.get('Total'))); -} - -function setBasicChartSettings(type) { - chart.plot(0).defaultSeriesType(type); - chart.plot(0).addSeries(mainMapping); - chart.plot(0).getSeries(0).name('Main Series'); - chart.padding(10, 10, 10, 65); - chart.scroller().line(scrollerMapping); -} - -function setAdvancedChartSettings(type) { - chart.plot(0).defaultSeriesType(type); - var mainSeries = chart.plot(0).addSeries(mainMapping); - mainSeries[0].name('Main Series'); - - var columnSeies = chart.plot(1).column(columnMapping); - columnSeies.name('Column'); - - chart.padding(10, 10, 10, 50); - chart.plot(1).height('30%'); - - chart.plot(1).yAxis().labels().format(function () { - var val = this['tickValue']; - var neg = val < 0; - val = Math.abs(val); - if (val / 1e15 >= 1) { - return (val / 1e9).toFixed(0) + 'Q'; - } else if (val / 1e12 >= 1) { - return (val / 1e9).toFixed(0) + 'T'; - } else if (val / 1e9 >= 1) { - return (val / 1e9).toFixed(0) + 'B'; - } else if (val / 1e6 >= 1) { - return (val / 1e6).toFixed(0) + 'M'; - } else if (val / 1e3 >= 1) { - return (val / 1e3).toFixed(0) + 'K'; - } - return neg ? '-' + val : val; - }); - - - chart.scroller().line(scrollerMapping); -} - -function isStreaming() { - return !isNaN(streamingTimerId); -} - -function toggleStreaming() { - if (!isStreaming()) { - startStreaming(); - } else { - stopStreaming(); - } -} - -function startStreaming() { - $('#start-base-chart-stream-btn').html('Stop Data Streaming'); - - streamingTimerId = setInterval(function () { - - if (requestAnimationFrame) { - requestAnimationFrame(streamData); - } else { - streamData(); - } - - function streamData() { - rawData = generate5MinsOHLCData(rawData.lastDate, streamPointsCount, rawData.lastValue, 10, rawData.lastVolume); - - perfMeter.start(streamPointsCount); - dataTable.addData(rawData.data, true); - rawData.data = null; - perfMeter.end(streamPointsCount); - - if (isNaN(streamingAverage)) streamingAverage = perfMeter.get(streamPointsCount); - - var streamingStat = $('#anystock-speed-test-stream-stat').length; - if (!streamingStat) { - var resultContainer = $('#anystock-speed-test-base-result'); - resultContainer.append('
Streaming ' + addCommas(streamPointsCount) + ' Data Points
'); - resultContainer.append(generateHTMLStatRecord('Streaming interval', streamingInterval, undefined, 'anystock-speed-test-stream-interval')); - resultContainer.append(generateHTMLStatRecord('Average rendering time', Math.round(streamingAverage), undefined, 'anystock-speed-test-stream-average')); - } else { - streamingAverage = (streamingAverage + perfMeter.get(streamPointsCount)) / 2; - $('#anystock-speed-test-stream-average .anystock-speed-test-result-row-value').html(Math.round(streamingAverage) + 'ms'); - } - } - - }, streamingInterval); -} - -function stopStreaming() { - if (isStreaming()) { - clearInterval(streamingTimerId); - streamingTimerId = NaN; - } - streamingAverage = NaN; - $('#start-base-chart-stream-btn').html('Start Data Streaming'); -} - -function removeStreamingStat() { - $('#anystock-speed-test-stream-stat').remove(); - $('#anystock-speed-test-stream-interval').remove(); - $('#anystock-speed-test-stream-average').remove(); -} - -function showPreloader() { - $('#anystock-speed-test-base-chart').append('
'); -} - -function hidePreloader() { - $('#loader-wrapper-chart').hide(); -} - -function generateHTMLStatRecord(title, value, postfix, opt_id) { - if (postfix === undefined) postfix = 'ms'; - opt_id = opt_id ? 'id="' + opt_id + '" ' : ''; - return '
' + - '' + title + ': ' + - '' + Math.round(value) + postfix + '' + - '
'; -} - -function addCommas(nStr) { - nStr += ''; - x = nStr.split('.'); - x1 = x[0]; - x2 = x.length > 1 ? '.' + x[1] : ''; - var rgx = /(\d+)(\d{3})/; - while (rgx.test(x1)) { - x1 = x1.replace(rgx, '$1' + ',' + '$2'); - } - return x1 + x2; -} diff --git a/src/js/perfomance.js b/src/js/perfomance.js deleted file mode 100644 index bbdaef4..0000000 --- a/src/js/perfomance.js +++ /dev/null @@ -1,29 +0,0 @@ -(function() { - var PerfMeter = function () { - this.started = {}; - this.finished = {}; - }; - PerfMeter.prototype.start = function(label) { - this.started[label] = window.performance ? window.performance.now() : +new Date(); - }; - PerfMeter.prototype.end = function(label) { - var now = window.performance ? window.performance.now() : +new Date(); - this.finished[label] = now - (this.started[label] || now); - delete this.started[label]; - }; - PerfMeter.prototype.get = function(label) { - return this.finished[label]; - }; - PerfMeter.prototype.print = function(groupName, var_args) { - console.group(groupName || 'Performance log'); - for (var i = 1; i < arguments.length; i++) { - var val = this.get(arguments[i]); - if (typeof val == 'number') val = val.toFixed(3); - else val = '' + val; - console.log(arguments[i] + ': ' + val + 'ms'); - } - console.groupEnd(groupName || 'Performance log'); - }; - window.basePerfMeter = new PerfMeter(); - window.perfMeter = new PerfMeter(); -})(); diff --git a/src/sass/app.scss b/src/sass/app.scss deleted file mode 100644 index d61f0b7..0000000 --- a/src/sass/app.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import "modules/btn"; -@import "modules/loader"; - -@import "style"; \ No newline at end of file diff --git a/src/scripts/data-generator.js b/src/scripts/data-generator.js new file mode 100644 index 0000000..6a64f96 --- /dev/null +++ b/src/scripts/data-generator.js @@ -0,0 +1,65 @@ +function generate5MinsOHLCData( + startDate, + rowsCount, + openValue, + spread, + opt_startVolume +) { + function rand(opt_spreadMult, opt_spreadAdd) { + var spreadMult = (isNaN(opt_spreadMult) ? 1 : opt_spreadMult) * spread; + var spreadAdd = isNaN(opt_spreadAdd) ? -0.5 : +opt_spreadAdd; + return ( + Math.round((Math.random() * spreadMult + spreadMult * spreadAdd) * 100) * + 0.01 + ); + } + + function randVolume(opt_prev) { + if (!opt_prev) return Math.round(Math.random() * 1e3) + 1e3; + var diff = Math.round(Math.random() * 2e2) - 4e2; + return Math.abs(opt_prev + diff); + } + + function nextDate(date) { + date.setTime(date.getTime() + 1000 * 60 * 5); + if (date.getUTCHours() > 18) { + date.setUTCDate(date.getUTCDate() + 1); + date.setUTCHours(9); + } + if (date.getUTCDay() == 6) { + date.setUTCDate(date.getUTCDate() + 2); + } else if (date.getUTCDay() == 0) { + date.setUTCDate(date.getUTCDate() + 1); + } + } + + startDate = startDate instanceof Date ? startDate : new Date(startDate); + var current = new Date(startDate.getTime()); + var index = -1; + + var data = []; + var open = openValue, + close, + high, + low; + var volume = randVolume(opt_startVolume); + while (++index < rowsCount) { + var diff = rand(); + close = open + diff; + if (close < 0) close = open - diff; + high = Math.max(open, close) + rand(0.2, 0); + low = Math.max(Math.min(open, close) - rand(0.2, 0), 0); + volume = randVolume(volume); + data.push([current.getTime(), open, high, low, close, volume]); + open = close + rand(0.0001); + nextDate(current); + } + return { + data: data, + lastValue: open, + lastDate: current, + lastVolume: volume, + }; +} + +module.exports = { generate5MinsOHLCData }; diff --git a/src/scripts/main.js b/src/scripts/main.js new file mode 100644 index 0000000..231482a --- /dev/null +++ b/src/scripts/main.js @@ -0,0 +1,375 @@ +const { PerfMeter } = require('./perfomance'); +const { generate5MinsOHLCData } = require('./data-Generator'); + +var rawData; +var perfMeter = new PerfMeter(); +var columnMapping, scrollerMapping; +var streamingTimerId; +var streamingAverage = NaN; + +var isFirstInit = true; + +var chart; +var dataTable; + +var chartConfiguration = 'ohlc-basic'; +var initialPointsCount = 50000; +var streamPointsCount = 500; +var streamingInterval = 20; + +var index = 0; + +var chartType = $('#anystock-speed-test-chartType-select') + .find('option:selected') + .attr('data-chart'); + +var requestAnimationFrame = + window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame; + +anychart.onDocumentReady(function () { + var resultWidth = $('#anystock-speed-test-base-result').width(); + $('#anystock-speed-test-base-chart').css({ right: resultWidth + 50 }); + + $('.select').selectpicker(); + + $('#start-base-chart-stream-btn').click(toggleStreaming); + + // first init + createChart(initialPointsCount, chartConfiguration, execCreateStock); + + $('.select[data-action-type]').on('change', changeChart); +}); + +function changeChart() { + var $optionSelected = $(this).find('option:selected'); + var value = $optionSelected.val(); + var type = $(this).attr('data-action-type'); + + if (type == 'configurationType') { + chartConfiguration = value; + stopStreaming(); + createChart( + initialPointsCount, + chartConfiguration, + getFunctionForCreateChart(chartType) + ); + } else if (type == 'initialPointsCount') { + initialPointsCount = value; + stopStreaming(); + createChart( + initialPointsCount, + chartConfiguration, + getFunctionForCreateChart(chartType) + ); + } else if (type == 'streamPointsCount') { + streamPointsCount = value; + removeStreamingStat(); + if (isStreaming()) { + stopStreaming(); + startStreaming(); + } + } else if (type == 'streamingInterval') { + streamingInterval = value; + removeStreamingStat(); + if (isStreaming()) { + stopStreaming(); + startStreaming(); + } + } +} + +function getFunctionForCreateChart(chartType) { + switch (chartType) { + case 'stock-ohlc': + return execCreateStock; + } +} + +function createChart(pointsCount, chartConfiguration, createChartFunc) { + if (chart) { + chart.dispose(); + chart = null; + index = 0; + $('#anystock-speed-test-base-chart').empty(); + } + + if (!isFirstInit) { + showPreloader(); + } + + setTimeout(function () { + isFirstInit = false; + createChartFunc(pointsCount, chartConfiguration); + }, 1); +} + +function execCreateStock(pointsCount, chartConfiguration) { + rawData = generate5MinsOHLCData( + new Date(new Date().getUTCFullYear() - 4, 0), + pointsCount, + 100, + 10, + 100 + ); + + perfMeter.start('Total'); + + perfMeter.start('Creating data storage'); + dataTable = anychart.data.table(0); + dataTable.addData(rawData.data); + mainMapping = dataTable.mapAs(); + // first 3 fields for OHLC and Candlestick + mainMapping.addField('open', 1, 'first'); + mainMapping.addField('high', 2, 'max'); + mainMapping.addField('low', 3, 'min'); + mainMapping.addField('close', 4, 'last'); + // this one for line and spline + mainMapping.addField('value', 1, 'first'); + + columnMapping = dataTable.mapAs(); + columnMapping.addField('value', 5, 'sum'); + + scrollerMapping = dataTable.mapAs(); + scrollerMapping.addField('value', 4, 'last'); + + perfMeter.end('Creating data storage'); + + perfMeter.start('Creating chart instance'); + chart = anychart.stock(); + chart.listen('chartDraw', function () { + $('#loader-wrapper').remove(); + hidePreloader(); + }); + switch (chartConfiguration) { + case 'ohlc-basic': + setBasicChartSettings('ohlc'); + break; + case 'ohlc-advanced': + setAdvancedChartSettings('ohlc'); + break; + case 'candlestick-basic': + setBasicChartSettings('candlestick'); + break; + case 'candlestick-advanced': + setAdvancedChartSettings('candlestick'); + break; + case 'line-basic': + setBasicChartSettings('line'); + break; + case 'spline-basic': + setBasicChartSettings('spline'); + break; + default: + setBasicChartSettings(); + } + + chart.container('anystock-speed-test-base-chart'); + perfMeter.end('Creating chart instance'); + + perfMeter.start('Chart rendering'); + chart.draw(); + rawData.data = null; + perfMeter.end('Chart rendering'); + + perfMeter.end('Total'); + + var resultContainer = $('#anystock-speed-test-base-result'); + resultContainer.empty(); + resultContainer.append( + '
Rendering ' + addCommas(pointsCount) + ' Data Points
' + ); + resultContainer.append( + generateHTMLStatRecord( + 'Creating data storage', + perfMeter.get('Creating data storage') + ) + ); + resultContainer.append( + generateHTMLStatRecord( + 'Creating chart instance', + perfMeter.get('Creating chart instance') + ) + ); + resultContainer.append( + generateHTMLStatRecord('Chart rendering', perfMeter.get('Chart rendering')) + ); + resultContainer.append( + generateHTMLStatRecord('Total', perfMeter.get('Total')) + ); +} + +function setBasicChartSettings(type) { + chart.plot(0).defaultSeriesType(type); + chart.plot(0).addSeries(mainMapping); + chart.plot(0).getSeries(0).name('Main Series'); + chart.padding(10, 10, 10, 65); + chart.scroller().line(scrollerMapping); +} + +function setAdvancedChartSettings(type) { + chart.plot(0).defaultSeriesType(type); + var mainSeries = chart.plot(0).addSeries(mainMapping); + mainSeries[0].name('Main Series'); + + var columnSeies = chart.plot(1).column(columnMapping); + columnSeies.name('Column'); + + chart.padding(10, 10, 10, 50); + chart.plot(1).height('30%'); + + chart + .plot(1) + .yAxis() + .labels() + .format(function () { + var val = this['tickValue']; + var neg = val < 0; + val = Math.abs(val); + if (val / 1e15 >= 1) { + return (val / 1e9).toFixed(0) + 'Q'; + } else if (val / 1e12 >= 1) { + return (val / 1e9).toFixed(0) + 'T'; + } else if (val / 1e9 >= 1) { + return (val / 1e9).toFixed(0) + 'B'; + } else if (val / 1e6 >= 1) { + return (val / 1e6).toFixed(0) + 'M'; + } else if (val / 1e3 >= 1) { + return (val / 1e3).toFixed(0) + 'K'; + } + return neg ? '-' + val : val; + }); + + chart.scroller().line(scrollerMapping); +} + +function isStreaming() { + return !isNaN(streamingTimerId); +} + +function toggleStreaming() { + if (!isStreaming()) { + startStreaming(); + } else { + stopStreaming(); + } +} + +function startStreaming() { + $('#start-base-chart-stream-btn').html('Stop Data Streaming'); + + streamingTimerId = setInterval(function () { + if (requestAnimationFrame) { + requestAnimationFrame(streamData); + } else { + streamData(); + } + + function streamData() { + rawData = generate5MinsOHLCData( + rawData.lastDate, + streamPointsCount, + rawData.lastValue, + 10, + rawData.lastVolume + ); + + perfMeter.start(streamPointsCount); + dataTable.addData(rawData.data, true); + rawData.data = null; + perfMeter.end(streamPointsCount); + + if (isNaN(streamingAverage)) + streamingAverage = perfMeter.get(streamPointsCount); + + var streamingStat = $('#anystock-speed-test-stream-stat').length; + if (!streamingStat) { + var resultContainer = $('#anystock-speed-test-base-result'); + resultContainer.append( + '
Streaming ' + + addCommas(streamPointsCount) + + ' Data Points
' + ); + resultContainer.append( + generateHTMLStatRecord( + 'Streaming interval', + streamingInterval, + undefined, + 'anystock-speed-test-stream-interval' + ) + ); + resultContainer.append( + generateHTMLStatRecord( + 'Average rendering time', + Math.round(streamingAverage), + undefined, + 'anystock-speed-test-stream-average' + ) + ); + } else { + streamingAverage = + (streamingAverage + perfMeter.get(streamPointsCount)) / 2; + $( + '#anystock-speed-test-stream-average .anystock-speed-test-result-row-value' + ).html(Math.round(streamingAverage) + 'ms'); + } + } + }, streamingInterval); +} + +function stopStreaming() { + if (isStreaming()) { + clearInterval(streamingTimerId); + streamingTimerId = NaN; + } + streamingAverage = NaN; + $('#start-base-chart-stream-btn').html('Start Data Streaming'); +} + +function removeStreamingStat() { + $('#anystock-speed-test-stream-stat').remove(); + $('#anystock-speed-test-stream-interval').remove(); + $('#anystock-speed-test-stream-average').remove(); +} + +function showPreloader() { + $('#anystock-speed-test-base-chart').append( + '
' + ); +} + +function hidePreloader() { + $('#loader-wrapper-chart').hide(); +} + +function generateHTMLStatRecord(title, value, postfix, opt_id) { + if (postfix === undefined) postfix = 'ms'; + opt_id = opt_id ? 'id="' + opt_id + '" ' : ''; + return ( + '
' + + '' + + title + + ': ' + + '' + + Math.round(value) + + postfix + + '' + + '
' + ); +} + +function addCommas(nStr) { + nStr += ''; + x = nStr.split('.'); + x1 = x[0]; + x2 = x.length > 1 ? '.' + x[1] : ''; + var rgx = /(\d+)(\d{3})/; + while (rgx.test(x1)) { + x1 = x1.replace(rgx, '$1' + ',' + '$2'); + } + return x1 + x2; +} diff --git a/src/scripts/perfomance.js b/src/scripts/perfomance.js new file mode 100644 index 0000000..29a75d6 --- /dev/null +++ b/src/scripts/perfomance.js @@ -0,0 +1,29 @@ +var PerfMeter = function () { + this.started = {}; + this.finished = {}; +}; +PerfMeter.prototype.start = function (label) { + this.started[label] = window.performance + ? window.performance.now() + : +new Date(); +}; +PerfMeter.prototype.end = function (label) { + var now = window.performance ? window.performance.now() : +new Date(); + this.finished[label] = now - (this.started[label] || now); + delete this.started[label]; +}; +PerfMeter.prototype.get = function (label) { + return this.finished[label]; +}; +PerfMeter.prototype.print = function (groupName, var_args) { + console.group(groupName || 'Performance log'); + for (var i = 1; i < arguments.length; i++) { + var val = this.get(arguments[i]); + if (typeof val == 'number') val = val.toFixed(3); + else val = '' + val; + console.log(arguments[i] + ': ' + val + 'ms'); + } + console.groupEnd(groupName || 'Performance log'); +}; + +module.exports = { PerfMeter }; diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000..4f1a030 --- /dev/null +++ b/src/styles/main.scss @@ -0,0 +1,4 @@ +@import 'modules/btn'; +@import 'modules/loader'; + +@import 'style'; diff --git a/src/sass/modules/_btn.scss b/src/styles/modules/_btn.scss similarity index 99% rename from src/sass/modules/_btn.scss rename to src/styles/modules/_btn.scss index 45974d3..6ad498a 100644 --- a/src/sass/modules/_btn.scss +++ b/src/styles/modules/_btn.scss @@ -33,4 +33,3 @@ $primary-color: #1c75ba; line-height: 1.3333333; border-radius: 3px; } - diff --git a/src/sass/modules/_loader.scss b/src/styles/modules/_loader.scss similarity index 99% rename from src/sass/modules/_loader.scss rename to src/styles/modules/_loader.scss index 757e0ca..02de96b 100644 --- a/src/sass/modules/_loader.scss +++ b/src/styles/modules/_loader.scss @@ -110,4 +110,4 @@ 100% { opacity: 1; } -} \ No newline at end of file +} diff --git a/src/sass/style.scss b/src/styles/style.scss similarity index 93% rename from src/sass/style.scss rename to src/styles/style.scss index d3a8a2c..d95a752 100644 --- a/src/sass/style.scss +++ b/src/styles/style.scss @@ -16,23 +16,42 @@ body { background-color: #ffffff; } -h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { font-family: inherit; font-weight: 500; line-height: 1.1; color: inherit; } -h4, .h4, h5, .h5, h6, .h6 { +h4, +.h4, +h5, +.h5, +h6, +.h6 { margin-top: 0; margin-bottom: 20px; } -h5, .h5 { +h5, +.h5 { font-size: 20px; } -p, ul, ol { +p, +ul, +ol { margin: 0 0 20px; } diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..fec3177 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,104 @@ +const path = require('path'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const devMode = process.env.NODE_ENV !== 'production'; +module.exports = { + // Tells Webpack which built-in optimizations to use + // In 'production' mode, Webpack will minify and uglify our JS code + // If you leave this out, Webpack will default to 'production' + mode: devMode ? 'development' : 'production', + // Webpack needs to know where to start the bundling process, + // so we define the main JS and Sass files, both under + // the './src' directory + entry: ['./src/scripts/main.js', './src/styles/main.scss'], + // This is where we define the path where Webpack will place + // the bundled JS file + output: { + path: path.resolve(__dirname, 'public'), + // Specify the base path for all the assets within your + // application. This is relative to the output path, so in + // our case it will be ./public/assets + publicPath: '/assets', + // The name of the output bundle. Path is also relative + // to the output path + filename: 'assets/scripts/bundle.js', + }, + module: { + // Array of rules that tells Webpack how the modules (output) + // will be created + rules: [ + { + // Look for JavaScript files and apply the babel-loader + // excluding the './node_modules' directory. It uses the + // configuration in `.babelrc` + test: /\.(js)$/, + exclude: /node_modules/, + use: ['babel-loader'], + }, + { + // Look for Sass files and process them according to the + // rules specified in the different loaders + test: /\.(sa|sc)ss$/, + // Use the following loaders from right-to-left, so it will + // use sass-loader first and ending with MiniCssExtractPlugin + use: [ + { + // Extracts the CSS into a separate file and uses the + // defined configurations in the 'plugins' section + loader: MiniCssExtractPlugin.loader, + }, + { + // Interprets CSS + loader: 'css-loader', + options: { + importLoaders: 2, + }, + }, + { + // Use PostCSS to minify and autoprefix. This loader + // uses the configuration in `postcss.config.js` + loader: 'postcss-loader', + }, + { + // Adds support for Sass files, if using Less, then + // use the less-loader + loader: 'sass-loader', + }, + ], + }, + { + // Adds support to load images in your CSS rules. It looks + // for .png, .jpg, .jpeg and .gif + test: /\.(png|jpe?g|gif)$/, + use: [ + { + loader: 'file-loader', + options: { + // The image will be named with the original name and + // extension + name: '[name].[ext]', + // Indicates where the images are stored and will use + // this path when generating the CSS files. + // Example, in main.scss I have + // url('../../public/assets/images/venice-italy.jpg') + // and when generating the CSS file, it will be + // outputted as url(../images/venice-italy.jpg), which + // is relative to /styles/main.css + publicPath: '../images', + // When this option is 'true', the loader will emit + // the image to output.path + emitFile: false, + }, + }, + ], + }, + ], + }, + plugins: [ + // Configuration options for MiniCssExtractPlugin. Here I'm only + // indicating what the CSS outputted file name should be and + // the location + new MiniCssExtractPlugin({ + filename: 'assets/styles/main.css', + }), + ], +};