Repository: c3js/c3 Branch: master Commit: 76d7760ee8d6 Files: 559 Total size: 3.5 MB Directory structure: gitextract_oun3f7hq/ ├── .bmp.yml ├── .circleci/ │ └── config.yml ├── .editorconfig ├── .github/ │ ├── ISSUE_TEMPLATE.md │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .jshintrc ├── .prettierrc.json ├── .travis.yml ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── MAINTAINANCE.md ├── README.md ├── bower.json ├── c3.css ├── c3.esm.js ├── c3.js ├── codecov.yml ├── component.json ├── config.rb ├── data/ │ └── samples.yml ├── design/ │ └── c3-logo.sketch ├── docs/ │ ├── 404.html │ ├── CNAME │ ├── _footer.haml │ ├── _index_item.haml │ ├── _index_item_title.haml │ ├── _reference_item_link.haml │ ├── _reference_menu_item.haml │ ├── _sample.haml │ ├── _sample_editor.haml │ ├── _samples_header.haml │ ├── _script.haml │ ├── _script_scroll.haml │ ├── _sidemenu_item.haml │ ├── crossdomain.xml │ ├── css/ │ │ ├── c3.css │ │ ├── examples.css │ │ ├── foundation.css │ │ ├── gettingstarted.css │ │ ├── index.css │ │ ├── normalize.css │ │ ├── reference.css │ │ ├── samples/ │ │ │ ├── api_axis_label.css │ │ │ ├── api_axis_range.css │ │ │ ├── api_data_color.css │ │ │ ├── api_data_name.css │ │ │ ├── api_flow.css │ │ │ ├── api_grid_x.css │ │ │ ├── api_resize.css │ │ │ ├── axes_label.css │ │ │ ├── axes_label_position.css │ │ │ ├── axes_rotated.css │ │ │ ├── axes_x_localtime.css │ │ │ ├── axes_x_tick_count.css │ │ │ ├── axes_x_tick_culling.css │ │ │ ├── axes_x_tick_fit.css │ │ │ ├── axes_x_tick_format.css │ │ │ ├── axes_x_tick_rotate.css │ │ │ ├── axes_x_tick_values.css │ │ │ ├── axes_y2.css │ │ │ ├── axes_y_padding.css │ │ │ ├── axes_y_range.css │ │ │ ├── axes_y_tick_format.css │ │ │ ├── categorized.css │ │ │ ├── chart_area.css │ │ │ ├── chart_area_stacked.css │ │ │ ├── chart_bar.css │ │ │ ├── chart_bar_negative.css │ │ │ ├── chart_bar_stacked.css │ │ │ ├── chart_combination.css │ │ │ ├── chart_donut.css │ │ │ ├── chart_gauge.css │ │ │ ├── chart_pie.css │ │ │ ├── chart_scatter.css │ │ │ ├── chart_spline.css │ │ │ ├── chart_stanford.css │ │ │ ├── chart_step.css │ │ │ ├── data_color.css │ │ │ ├── data_columned.css │ │ │ ├── data_json.css │ │ │ ├── data_label.css │ │ │ ├── data_label_format.css │ │ │ ├── data_load.css │ │ │ ├── data_name.css │ │ │ ├── data_order.css │ │ │ ├── data_rowed.css │ │ │ ├── data_stringx.css │ │ │ ├── data_url.css │ │ │ ├── data_xformat.css │ │ │ ├── grid_x_lines.css │ │ │ ├── grid_y_lines.css │ │ │ ├── interaction_zoom.css │ │ │ ├── legend_custom.css │ │ │ ├── legend_position.css │ │ │ ├── options_color.css │ │ │ ├── options_gridline.css │ │ │ ├── options_legend.css │ │ │ ├── options_padding.css │ │ │ ├── options_size.css │ │ │ ├── options_subchart.css │ │ │ ├── pie_label_format.css │ │ │ ├── point_show.css │ │ │ ├── region.css │ │ │ ├── region_timeseries.css │ │ │ ├── simple_multiple.css │ │ │ ├── simple_regions.css │ │ │ ├── simple_xy.css │ │ │ ├── simple_xy_multiple.css │ │ │ ├── style_grid.css │ │ │ ├── style_region.css │ │ │ ├── timeseries.css │ │ │ ├── tooltip_format.css │ │ │ ├── tooltip_grouped.css │ │ │ ├── tooltip_show.css │ │ │ ├── transform_area.css │ │ │ ├── transform_areaspline.css │ │ │ ├── transform_bar.css │ │ │ ├── transform_donut.css │ │ │ ├── transform_line.css │ │ │ ├── transform_pie.css │ │ │ ├── transform_scatter.css │ │ │ ├── transform_spline.css │ │ │ └── transition_duration.css │ │ ├── style.css │ │ └── tomorrow.css │ ├── data/ │ │ ├── c3_string_x.csv │ │ ├── c3_test.csv │ │ ├── c3_test.json │ │ └── c3_test2.csv │ ├── examples.html.haml │ ├── gettingstarted.html.haml │ ├── img/ │ │ └── .gitignore │ ├── index.html.haml │ ├── js/ │ │ ├── ace/ │ │ │ ├── ace.js │ │ │ ├── mode-javascript.js │ │ │ ├── theme-tomorrow.js │ │ │ └── worker-javascript.js │ │ ├── c3.esm.js │ │ ├── c3.js │ │ ├── gettingstarted.js │ │ ├── highlight.pack.js │ │ ├── index.js │ │ ├── main.js │ │ ├── plugins.js │ │ └── samples/ │ │ ├── api_axis_label.js │ │ ├── api_axis_range.js │ │ ├── api_data_color.js │ │ ├── api_data_name.js │ │ ├── api_flow.js │ │ ├── api_grid_x.js │ │ ├── api_resize.js │ │ ├── axes_label.js │ │ ├── axes_label_position.js │ │ ├── axes_rotated.js │ │ ├── axes_x_localtime.js │ │ ├── axes_x_tick_count.js │ │ ├── axes_x_tick_culling.js │ │ ├── axes_x_tick_fit.js │ │ ├── axes_x_tick_format.js │ │ ├── axes_x_tick_rotate.js │ │ ├── axes_x_tick_values.js │ │ ├── axes_y2.js │ │ ├── axes_y_padding.js │ │ ├── axes_y_range.js │ │ ├── axes_y_tick_format.js │ │ ├── categorized.js │ │ ├── chart_area.js │ │ ├── chart_area_stacked.js │ │ ├── chart_bar.js │ │ ├── chart_bar_stacked.js │ │ ├── chart_combination.js │ │ ├── chart_donut.js │ │ ├── chart_gauge.js │ │ ├── chart_pie.js │ │ ├── chart_scatter.js │ │ ├── chart_spline.js │ │ ├── chart_stanford.js │ │ ├── chart_step.js │ │ ├── data_color.js │ │ ├── data_columned.js │ │ ├── data_json.js │ │ ├── data_label.js │ │ ├── data_label_format.js │ │ ├── data_load.js │ │ ├── data_name.js │ │ ├── data_number_format_l10n.js │ │ ├── data_order.js │ │ ├── data_rowed.js │ │ ├── data_stringx.js │ │ ├── data_url.js │ │ ├── data_xformat.js │ │ ├── grid_x_lines.js │ │ ├── grid_y_lines.js │ │ ├── interaction_zoom.js │ │ ├── interaction_zoom_by_drag.js │ │ ├── legend_custom.js │ │ ├── legend_position.js │ │ ├── options_color.js │ │ ├── options_gridline.js │ │ ├── options_legend.js │ │ ├── options_padding.js │ │ ├── options_size.js │ │ ├── options_subchart.js │ │ ├── pie_label_format.js │ │ ├── point_show.js │ │ ├── region.js │ │ ├── region_timeseries.js │ │ ├── simple.js │ │ ├── simple_multiple.js │ │ ├── simple_regions.js │ │ ├── simple_xy.js │ │ ├── simple_xy_multiple.js │ │ ├── style_grid.js │ │ ├── style_region.js │ │ ├── timeseries.js │ │ ├── tooltip_format.js │ │ ├── tooltip_grouped.js │ │ ├── tooltip_horizontal.js │ │ ├── tooltip_show.js │ │ ├── transform_area.js │ │ ├── transform_areaspline.js │ │ ├── transform_bar.js │ │ ├── transform_donut.js │ │ ├── transform_line.js │ │ ├── transform_pie.js │ │ ├── transform_scatter.js │ │ ├── transform_spline.js │ │ └── transition_duration.js │ ├── layouts/ │ │ └── layout.haml │ ├── reference.html.haml │ ├── robots.txt │ └── samples/ │ ├── api_axis_label.html.haml │ ├── api_axis_range.html.haml │ ├── api_data_color.html.haml │ ├── api_data_name.html.haml │ ├── api_flow.html.haml │ ├── api_grid_x.html.haml │ ├── api_resize.html.haml │ ├── axes_label.html.haml │ ├── axes_label_position.html.haml │ ├── axes_rotated.html.haml │ ├── axes_x_localtime.html.haml │ ├── axes_x_tick_count.html.haml │ ├── axes_x_tick_culling.html.haml │ ├── axes_x_tick_fit.html.haml │ ├── axes_x_tick_format.html.haml │ ├── axes_x_tick_rotate.html.haml │ ├── axes_x_tick_values.html.haml │ ├── axes_y2.html.haml │ ├── axes_y_padding.html.haml │ ├── axes_y_range.html.haml │ ├── axes_y_tick_format.html.haml │ ├── categorized.html.haml │ ├── chart_area.html.haml │ ├── chart_area_stacked.html.haml │ ├── chart_bar.html.haml │ ├── chart_bar_stacked.html.haml │ ├── chart_combination.html.haml │ ├── chart_donut.html.haml │ ├── chart_gauge.html.haml │ ├── chart_pie.html.haml │ ├── chart_scatter.html.haml │ ├── chart_spline.html.haml │ ├── chart_stanford.html.haml │ ├── chart_step.html.haml │ ├── data_color.html.haml │ ├── data_columned.html.haml │ ├── data_json.html.haml │ ├── data_label.html.haml │ ├── data_label_format.html.haml │ ├── data_load.html.haml │ ├── data_name.html.haml │ ├── data_number_format_l10n.html.haml │ ├── data_order.html.haml │ ├── data_rowed.html.haml │ ├── data_stringx.html.haml │ ├── data_url.html.haml │ ├── grid_x_lines.html.haml │ ├── grid_y_lines.html.haml │ ├── interaction_zoom.html.haml │ ├── interaction_zoom_by_drag.html.haml │ ├── legend_custom.html.haml │ ├── legend_position.html.haml │ ├── options_color.html.haml │ ├── options_gridline.html.haml │ ├── options_legend.html.haml │ ├── options_padding.html.haml │ ├── options_size.html.haml │ ├── options_subchart.html.haml │ ├── pie_label_format.html.haml │ ├── point_show.html.haml │ ├── region.html.haml │ ├── region_timeseries.html.haml │ ├── simple_multiple.html.haml │ ├── simple_regions.html.haml │ ├── simple_xy.html.haml │ ├── simple_xy_multiple.html.haml │ ├── style_grid.html.haml │ ├── style_region.html.haml │ ├── timeseries.html.haml │ ├── tooltip_format.html.haml │ ├── tooltip_grouped.html.haml │ ├── tooltip_horizontal.html.haml │ ├── tooltip_show.html.haml │ ├── transform_area.html.haml │ ├── transform_areaspline.html.haml │ ├── transform_bar.html.haml │ ├── transform_donut.html.haml │ ├── transform_line.html.haml │ ├── transform_pie.html.haml │ ├── transform_scatter.html.haml │ ├── transform_spline.html.haml │ └── transition_duration.html.haml ├── extensions/ │ ├── chart-bubble/ │ │ ├── bubble.js │ │ └── index.html │ ├── exporter/ │ │ ├── config.json │ │ └── phantom-exporter.js │ └── js/ │ └── c3ext.js ├── htdocs/ │ ├── css/ │ │ ├── index.css │ │ └── style.css │ ├── data/ │ │ ├── c3_stanford_data.json │ │ ├── c3_test.csv │ │ ├── c3_test.json │ │ ├── c3_test.tsv │ │ ├── c3_test2.csv │ │ ├── c3_test2_ts.csv │ │ ├── c3_test3.csv │ │ ├── c3_test_2.json │ │ ├── c3_test_3.json │ │ └── c3_test_ts.csv │ ├── index.html │ ├── js/ │ │ ├── require.js │ │ └── samples/ │ │ ├── plugin.js │ │ ├── requirejs.js │ │ └── zoom_reduction.js │ └── samples/ │ ├── api_axis_label.html │ ├── api_axis_range.html │ ├── api_category.html │ ├── api_data_colors.html │ ├── api_flow.html │ ├── api_flow_timeseries.html │ ├── api_legend.html │ ├── api_tooltip_show.html │ ├── api_transform.html │ ├── api_xgrid_lines.html │ ├── api_ygrid_lines.html │ ├── api_zoom.html │ ├── area_zerobased.html │ ├── axes_log_scales.html │ ├── axes_padding.html │ ├── axes_range.html │ ├── axes_x_localtime.html │ ├── axes_x_range_timeseries.html │ ├── axes_x_selection.html │ ├── axes_x_tick_culling.html │ ├── axes_x_tick_fit.html │ ├── axes_x_tick_rotate.html │ ├── axes_x_tick_values.html │ ├── axes_y2.html │ ├── axes_y_default.html │ ├── bar_zerobased.html │ ├── bindto.html │ ├── categorized.html │ ├── chart_arc.html │ ├── chart_area.html │ ├── chart_area_spline.html │ ├── chart_area_spline_stacked.html │ ├── chart_area_stacked.html │ ├── chart_area_step.html │ ├── chart_area_step_stacked.html │ ├── chart_bar.html │ ├── chart_bar_max_width.html │ ├── chart_bar_space.html │ ├── chart_bar_stacked.html │ ├── chart_bar_stacked_normalized.html │ ├── chart_combination.html │ ├── chart_combination_normalized.html │ ├── chart_donut.html │ ├── chart_gauge.html │ ├── chart_multi_arc_gauge.html │ ├── chart_pie.html │ ├── chart_pie_sort.html │ ├── chart_scatter.html │ ├── chart_spline.html │ ├── chart_stanford.html │ ├── chart_stanford_custom_elements.html │ ├── chart_step.html │ ├── chart_step_category.html │ ├── custom_x_categorized.html │ ├── custom_x_scale.html │ ├── custom_xs_scale.html │ ├── data_columned.html │ ├── data_hide.html │ ├── data_json.html │ ├── data_label.html │ ├── data_label_format.html │ ├── data_load.html │ ├── data_load_timeseries.html │ ├── data_region.html │ ├── data_region_timeseries.html │ ├── data_rowed.html │ ├── data_url.html │ ├── different_category_datasets.html │ ├── domain_y.html │ ├── element.html │ ├── emptydata.html │ ├── grid_focus.html │ ├── grid_x_lines.html │ ├── grid_x_lines_timeseries.html │ ├── grids.html │ ├── grids_timeseries.html │ ├── interaction_enabled.html │ ├── legend.html │ ├── padding.html │ ├── padding_update.html │ ├── plugin.html │ ├── point_r.html │ ├── point_show.html │ ├── regions.html │ ├── regions_timeseries.html │ ├── requirejs.html │ ├── resize.html │ ├── selection.html │ ├── simple.html │ ├── subchart.html │ ├── subchart_onbrush.html │ ├── timeseries.html │ ├── timeseries_date.html │ ├── timeseries_descendent.html │ ├── timeseries_raw.html │ ├── tooltip_grouped.html │ ├── tooltip_horizontal.html │ ├── tooltip_show.html │ ├── zoom.html │ ├── zoom_category.html │ ├── zoom_onzoom.html │ ├── zoom_reduction.html │ ├── zoom_type.html │ └── zoom_type_disable_default_behavior.html ├── karma.conf.js ├── package.json ├── rollup.config.js ├── spec/ │ ├── api.axis-spec.ts │ ├── api.data-spec.ts │ ├── api.donut-spec.ts │ ├── api.focus-spec.ts │ ├── api.grid-spec.ts │ ├── api.load-spec.ts │ ├── api.pie-spec.ts │ ├── api.region-spec.ts │ ├── api.x-spec.ts │ ├── api.zoom-spec.ts │ ├── arc-spec.ts │ ├── axis-spec.ts │ ├── c3-helper.ts │ ├── cache-spec.ts │ ├── class-spec.ts │ ├── core-spec.ts │ ├── data-spec.ts │ ├── data.convert-spec.ts │ ├── domain-spec.ts │ ├── drag-spec.ts │ ├── grid-spec.ts │ ├── interaction-spec.ts │ ├── legend-spec.ts │ ├── shape.bar-spec.ts │ ├── shape.line-spec.ts │ ├── stanford-spec.ts │ ├── subchart-spec.ts │ ├── svg-helper.ts │ ├── title-spec.ts │ ├── tooltip-spec.ts │ ├── type-spec.ts │ ├── util-spec.ts │ └── zoom-spec.ts ├── src/ │ ├── api.axis.ts │ ├── api.category.ts │ ├── api.chart.ts │ ├── api.color.ts │ ├── api.data.ts │ ├── api.donut.ts │ ├── api.flow.ts │ ├── api.focus.ts │ ├── api.grid.ts │ ├── api.group.ts │ ├── api.legend.ts │ ├── api.load.ts │ ├── api.pie.ts │ ├── api.region.ts │ ├── api.selection.ts │ ├── api.show.ts │ ├── api.subchart.ts │ ├── api.tooltip.ts │ ├── api.transform.ts │ ├── api.x.ts │ ├── api.zoom.ts │ ├── arc.ts │ ├── axis-internal.ts │ ├── axis.ts │ ├── cache.ts │ ├── category.ts │ ├── chart-internal.ts │ ├── chart.ts │ ├── class-utils.ts │ ├── class.ts │ ├── clip.ts │ ├── color.ts │ ├── colorscale.ts │ ├── config.ts │ ├── core.ts │ ├── data.convert.ts │ ├── data.load.ts │ ├── data.ts │ ├── domain.ts │ ├── drag.ts │ ├── format.ts │ ├── grid.ts │ ├── index.ts │ ├── interaction.ts │ ├── legend.ts │ ├── polyfill.ts │ ├── region.ts │ ├── scale.ts │ ├── scss/ │ │ ├── arc.scss │ │ ├── area.scss │ │ ├── axis.scss │ │ ├── bar.scss │ │ ├── brush.scss │ │ ├── chart.scss │ │ ├── focus.scss │ │ ├── grid.scss │ │ ├── legend.scss │ │ ├── line.scss │ │ ├── main.scss │ │ ├── point.scss │ │ ├── region.scss │ │ ├── select_drag.scss │ │ ├── text.scss │ │ ├── title.scss │ │ ├── tooltip.scss │ │ └── zoom.scss │ ├── selection.ts │ ├── shape.bar.ts │ ├── shape.line.ts │ ├── shape.ts │ ├── size.ts │ ├── stanford.ts │ ├── stanfordelements.ts │ ├── subchart.ts │ ├── text.ts │ ├── title.ts │ ├── tooltip.ts │ ├── type.ts │ ├── ua.ts │ ├── util.ts │ └── zoom.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .bmp.yml ================================================ --- version: 0.7.20 commit: 'chore(version): bump to v%.%.%' files: src/core.ts: 'version: ''%.%.%''' package.json: '"version": "%.%.%"' component.json: '"version": "%.%.%"' ================================================ FILE: .circleci/config.yml ================================================ version: 2 node12: &node12 working_directory: ~/c3 docker: - image: circleci/node:12-browsers node14: &node14 working_directory: ~/c3 docker: - image: circleci/node:14-browsers restore_modules_cache: &restore_modules_cache restore_cache: keys: - npm-4-{{ checksum "package.json" }} # fallback to using the latest cache if no exact match is found - npm-4- save_modules_cache: &save_modules_cache save_cache: key: npm-4-{{ checksum "package.json" }} paths: ./node_modules install_and_test: &install_and_test steps: - checkout - run: name: Display versions command: | echo "node $(node -v)" echo "npm v$(npm --version)" echo "$(google-chrome --version)" - *restore_modules_cache - run: name: Installing Dependencies command: yarn - *save_modules_cache - run: yarn test - run: yarn codecov - store_artifacts: path: htdocs destination: htdocs - run: npx status-back -s -c circleci/htdocs -r c3js/c3 "preview build succes!" "https://${CIRCLE_BUILD_NUM}-11496279-gh.circle-artifacts.com/0/htdocs/index.html" jobs: test_on_node12: <<: *node12 <<: *install_and_test test_on_node14: <<: *node14 <<: *install_and_test docs: docker: - image: circleci/ruby:2.4-node env: BUNDLE_PATH: vendor/bundle steps: - checkout - restore_cache: key: deps-bundle-{{ checksum "Gemfile.lock" }} - run: bundle install - save_cache: key: deps-bundle-{{ checksum "Gemfile.lock" }} paths: - vendor/bundle - *restore_modules_cache - run: yarn - *save_modules_cache - run: yarn build - run: yarn copy-to-docs - run: yarn build:docs - store_artifacts: path: build destination: docs workflows: version: 2 test: jobs: - test_on_node12 - test_on_node14 - docs ================================================ FILE: .editorconfig ================================================ # editorconfig.org root = true [*] indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_size = 2 [*.json] indent_size = 2 [*.haml] indent_size = 2 [*.html] indent_size = 2 ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ * **C3 version**: * **D3 version**: * **Browser**: * **OS**: ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ================================================ FILE: .gitignore ================================================ # npm modules node_modules bower_components d3.js d3.min.js components package-lock.json # build /htdocs/js/c3.js /htdocs/js/c3.min.js /htdocs/js/c3.esm.js /htdocs/css/c3.css /htdocs/css/c3.min.css /build # sass .sass-cache # jetbrains .idea/ # coverage report /coverage # OS related .DS_Store # IDE related .idea .iml # bundle .bundle/config vendor/bundle ================================================ FILE: .jshintrc ================================================ { "esversion": 6, "eqeqeq": true, "curly": true, "strict": false, "trailing": true, "white": true, "maxlen": 210, "undef": true, "unused": true, "indent": 2, "eqnull": true, "expr": true, "newcap": false, "loopfunc": true, "bitwise": false, "asi": true, "laxbreak": true, "browser": true, "jasmine": true, "globals": { "d3": false, "require": false } } ================================================ FILE: .prettierrc.json ================================================ { "tabWidth": 2, "semi": false, "singleQuote": true } ================================================ FILE: .travis.yml ================================================ language: ruby rvm: - 2.4 branches: only: - master script: - bundle exec middleman build deploy: provider: pages skip-cleanup: true local-dir: build github-token: $GITHUB_TOKEN keep-history: true on: branch: master ================================================ FILE: CONTRIBUTING.md ================================================ ## Filing an issue Before filing an issue, please [search the queue](https://github.com/c3js/c3/issues) to make sure it hasn't already been reported. If a bug, please include the following — 1. What version of C3? 1. What browsers have you confirmed it in? 1. Can you isolate the issue by providing a jsFiddle demonstrating it in a minimalist capacity? Please *do not* ask for support using the issue queue. For support, please ask [on chat](https://gitter.im/c3js/c3) or [the mailing list](groups.google.com/forum/#!forum/c3js). ## Setup 1. **Clone the repo from GitHub** git clone https://github.com/c3js/c3.git cd c3 2. **Acquire build dependencies.** Make sure you have [Node.js](http://nodejs.org/) installed on your workstation. This is only needed to _build_ C3 from sources. C3 itself has no dependency on Node.js once it is built. Now run: npm install -g grunt-cli npm install The first `npm` command sets up the popular [Grunt](http://gruntjs.com/) build tool. You might need to run this command with `sudo` if you're on Linux or Mac OS X, or in an Administrator command prompt on Windows. The second `npm` command fetches the remaining build dependencies. ## Building C3 from sources npm run build ## Distribution npm run dist Now you'll find the built files in `c3.js`, `c3.min.js`, `c3.css` & `c3.min.css`. ## Running the tests npm run test This command will automatically run the specification suite and report its results. If you want to see specs running live in browser (e.g., for debugging), simply open `http://localhost:9876/` in your browser when phantomjs starts. ## Building the document site (c3js.org) First you need ruby and [bundler][] to build the documentation site. **Note:** Currently the site doesn't build with ruby 2.5.x, so you need ruby 2.4.4 or below. ([rbenv][] is useful for switching between ruby versions.) ```console $ gem install bundler ``` Then you need to install bundler dependencies. ```console $ bundle install ``` Then hit the following command to build the site. ```console $ npm run watch:docs ``` Then access `http://0.0.0.0:4567`. ## Contributing your changes Add something about PRs here, indicate that PRs should not bump the version number & the build output files (`c3.js`, `c3.min.js`, `c3.css` & `c3.min.css`) should be excluded [bundler]: https://bundler.io [rbenv]: https://github.com/rbenv/rbenv ================================================ FILE: Gemfile ================================================ # If you have OpenSSL installed, we recommend updating # the following line to use "https" source 'http://rubygems.org' gem "middleman", "~>3.2.2" # Live-reloading plugin gem "middleman-livereload", "~> 3.1.0" # Sync plugin gem 'middleman-sync', '3.0.12' gem 'unf' # For faster file watcher updates on Windows: gem "wdm", "~> 0.1.0", :platforms => [:mswin, :mingw] ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Masayuki Tanaka Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MAINTAINANCE.md ================================================ # Release process If you don't have `bmp` command installed, first install `bmp` ruby gem: gem install bmp When master is ready for the next release, hit the command: bmp -p This automatically updates all the version numbers with a new one in the repository. Then hit the command: npm run dist This builds the scripts and stylesheets. Then hit: bmp -c This commits all the changes (including the built assets) and git-tags a new version (like v0.4.16): Then publish it to the npm registry (you need admin access to c3 module): npm publish At this point, the new version is available through npm. Then push master and the tag to github: git push origin master vX.Y.Z That's all. ================================================ FILE: README.md ================================================ # c3 [![CircleCI](https://circleci.com/gh/c3js/c3.svg?style=shield)](https://circleci.com/gh/c3js/c3) [![license](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/c3js/c3/blob/master/LICENSE) [![codecov.io](https://codecov.io/github/c3js/c3/coverage.svg?branch=master)](https://codecov.io/github/c3js/c3?branch=master) [![jsDelivr Hits](https://data.jsdelivr.com/v1/package/npm/c3/badge?style=rounded)](https://www.jsdelivr.com/package/npm/c3) > c3 is a D3-based reusable chart library that enables deeper integration of charts into web applications. Follow the link for more information: [http://c3js.org](http://c3js.org/) ## Documentation + [Getting Started](http://c3js.org/gettingstarted.html) + [Examples](http://c3js.org/examples.html) + [Full API Reference](https://c3js.org/reference.html) Additional samples can be found in this repository: + [https://github.com/c3js/c3/tree/master/htdocs/samples](https://github.com/c3js/c3/tree/master/htdocs/samples) You can run these samples as: ``` $ npm run serve-static ``` ## Google Group For general C3.js-related discussion, please visit our [Google Group at https://groups.google.com/forum/#!forum/c3js](https://groups.google.com/forum/#!forum/c3js). ## Gitter [![Join the chat at https://gitter.im/c3js/c3](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/c3js/c3?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Using the issue queue The [issue queue](https://github.com/c3js/c3/issues) is to be used for reporting defects and problems with C3.js, in addition to feature requests and ideas. It is **not** a catch-all support forum. **For general support enquiries, please use the [Google Group](https://groups.google.com/forum/#!forum/c3js) at https://groups.google.com/forum/#!forum/c3js.** All questions involving the interplay between C3.js and any other library (such as AngularJS) should be posted there first! Before reporting an issue, please do the following: 1. [Search for existing issues](https://github.com/c3js/c3/issues) to ensure you're not posting a duplicate. 1. [Search the Google Group](https://groups.google.com/forum/#!forum/c3js) to ensure it hasn't been addressed there already. 1. Create a JSFiddle or Plunkr highlighting the issue. Please don't include any unnecessary dependencies so we can isolate that the issue is in fact with C3. *Please be advised that custom CSS can modify C3.js output!* 1. When posting the issue, please use a descriptive title and include the version of C3 (or, if cloning from Git, the commit hash — C3 is under active development and the master branch contains the latest dev commits!), along with any platform/browser/OS information that may be relevant. ## Pull requests Pull requests are welcome, though please post an issue first to see whether such a change is desirable. If you choose to submit a pull request, please do not bump the version number unless asked to, and please include test cases for any new features. Squash all your commits as well, please. ## Playground Please fork this fiddle: + http://jsfiddle.net/7kYJu/4742/ ## Dependency + [D3.js](https://github.com/mbostock/d3) `^5.0.0` ## License MIT ================================================ FILE: bower.json ================================================ { "name": "c3", "main": ["c3.css", "c3.js"], "homepage": "https://github.com/c3js/c3", "authors": ["Masayuki Tanaka "], "description": "D3-based reusable chart library", "keywords": ["chart", "d3"], "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "htdocs", "spec", "src/**/*.js", "package.json", "component.json", "Gruntfile.*" ], "dependencies": { "d3": "^5.0.0" } } ================================================ FILE: c3.css ================================================ /*-- Chart --*/ .c3 svg { font: 10px sans-serif; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .c3 path, .c3 line { fill: none; stroke: #000; } .c3 text { -webkit-user-select: none; -moz-user-select: none; user-select: none; } .c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path { shape-rendering: crispEdges; } .c3-chart-arc path { stroke: #fff; } .c3-chart-arc rect { stroke: white; stroke-width: 1; } .c3-chart-arc text { fill: #fff; font-size: 13px; } /*-- Axis --*/ /*-- Grid --*/ .c3-grid line { stroke: #aaa; } .c3-grid text { fill: #aaa; } .c3-xgrid, .c3-ygrid { stroke-dasharray: 3 3; } /*-- Text on Chart --*/ .c3-text.c3-empty { fill: #808080; font-size: 2em; } /*-- Line --*/ .c3-line { stroke-width: 1px; } /*-- Point --*/ .c3-circle { fill: currentColor; } .c3-circle._expanded_ { stroke-width: 1px; stroke: white; } .c3-selected-circle { fill: white; stroke-width: 2px; } /*-- Bar --*/ .c3-bar { stroke-width: 0; } .c3-bar._expanded_ { fill-opacity: 1; fill-opacity: 0.75; } /*-- Focus --*/ .c3-target.c3-focused { opacity: 1; } .c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step { stroke-width: 2px; } .c3-target.c3-defocused { opacity: 0.3 !important; } /*-- Region --*/ .c3-region { fill: steelblue; fill-opacity: 0.1; } .c3-region text { fill-opacity: 1; } /*-- Brush --*/ .c3-brush .extent { fill-opacity: 0.1; } /*-- Select - Drag --*/ /*-- Legend --*/ .c3-legend-item { font-size: 12px; } .c3-legend-item-hidden { opacity: 0.15; } .c3-legend-background { opacity: 0.75; fill: white; stroke: lightgray; stroke-width: 1; } /*-- Title --*/ .c3-title { font: 14px sans-serif; } /*-- Tooltip --*/ .c3-tooltip-container { z-index: 10; } .c3-tooltip { border-collapse: collapse; border-spacing: 0; background-color: #fff; empty-cells: show; -webkit-box-shadow: 7px 7px 12px -9px #777777; -moz-box-shadow: 7px 7px 12px -9px #777777; box-shadow: 7px 7px 12px -9px #777777; opacity: 0.9; } .c3-tooltip tr { border: 1px solid #CCC; } .c3-tooltip th { background-color: #aaa; font-size: 14px; padding: 2px 5px; text-align: left; color: #FFF; } .c3-tooltip td { font-size: 13px; padding: 3px 6px; background-color: #fff; border-left: 1px dotted #999; } .c3-tooltip td > span { display: inline-block; width: 10px; height: 10px; margin-right: 6px; } .c3-tooltip .value { text-align: right; } /*-- Area --*/ .c3-area { stroke-width: 0; opacity: 0.2; } /*-- Arc --*/ .c3-chart-arcs-title { dominant-baseline: middle; font-size: 1.3em; } .c3-chart-arcs .c3-chart-arcs-background { fill: #e0e0e0; stroke: #FFF; } .c3-chart-arcs .c3-chart-arcs-gauge-unit { fill: #000; font-size: 16px; } .c3-chart-arcs .c3-chart-arcs-gauge-max { fill: #777; } .c3-chart-arcs .c3-chart-arcs-gauge-min { fill: #777; } .c3-chart-arc .c3-gauge-value { fill: #000; /* font-size: 28px !important;*/ } .c3-chart-arc.c3-target g path { opacity: 1; } .c3-chart-arc.c3-target.c3-focused g path { opacity: 1; } /*-- Zoom --*/ .c3-drag-zoom.enabled { pointer-events: all !important; visibility: visible; } .c3-drag-zoom.disabled { pointer-events: none !important; visibility: hidden; } .c3-drag-zoom .extent { fill-opacity: 0.1; } ================================================ FILE: c3.esm.js ================================================ /* @license C3.js v0.7.18 | (c) C3 Team and other contributors | http://c3js.org/ */ import * as d3 from 'd3'; function ChartInternal(api) { var $$ = this; // Note: This part will be replaced by rollup-plugin-modify // When bundling esm output. Beware of changing this line. // TODO: Maybe we should check that the modification by rollup-plugin-modify // is valid during unit tests. $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require('d3') : undefined; $$.api = api; $$.config = $$.getDefaultConfig(); $$.data = {}; $$.cache = {}; $$.axes = {}; } /** * The Chart class * * The methods of this class is the public APIs of the chart object. */ function Chart(config) { this.internal = new ChartInternal(this); this.internal.loadConfig(config); this.internal.beforeInit(config); this.internal.init(); this.internal.afterInit(config) // bind "this" to nested API ;(function bindThis(fn, target, argThis) { Object.keys(fn).forEach(function(key) { target[key] = fn[key].bind(argThis); if (Object.keys(fn[key]).length > 0) { bindThis(fn[key], target[key], argThis); } }); })(Chart.prototype, this, this); } var asHalfPixel = function(n) { return Math.ceil(n) + 0.5 }; var ceil10 = function(v) { return Math.ceil(v / 10) * 10 }; var diffDomain = function(d) { return d[1] - d[0] }; var getOption = function(options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue }; var getPathBox = function(path) { var box = getBBox(path), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], minX = items[0].x, minY = Math.min(items[0].y, items[1].y); return { x: minX, y: minY, width: box.width, height: box.height } }; var getBBox = function(element) { try { return element.getBBox() } catch (ignore) { // Firefox will throw an exception if getBBox() is called whereas the // element is rendered with display:none // See https://github.com/c3js/c3/issues/2692 // The previous code was using `getBoundingClientRect` which was returning // everything at 0 in this case so let's reproduce this behavior here. return { x: 0, y: 0, width: 0, height: 0 } } }; var hasValue = function(dict, value) { var found = false; Object.keys(dict).forEach(function(key) { if (dict[key] === value) { found = true; } }); return found }; var isArray = function(o) { return Array.isArray(o) }; var isDefined = function(v) { return typeof v !== 'undefined' }; var isEmpty = function(o) { return ( typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0) ) }; var isFunction = function(o) { return typeof o === 'function' }; var isNumber = function(o) { return typeof o === 'number' }; var isString = function(o) { return typeof o === 'string' }; var isUndefined = function(v) { return typeof v === 'undefined' }; var isValue = function(v) { return v || v === 0 }; var notEmpty = function(o) { return !isEmpty(o) }; var sanitise = function(str) { return typeof str === 'string' ? str.replace(//g, '>') : str }; var flattenArray = function(arr) { return Array.isArray(arr) ? [].concat(...arr) : [] }; /** * Returns whether the point is within the given box. * * @param {Array} point An [x,y] coordinate * @param {Object} box An object with {x, y, width, height} keys * @param {Number} sensitivity An offset to ease check on very small boxes */ var isWithinBox = function(point, box, sensitivity = 0) { const xStart = box.x - sensitivity; const xEnd = box.x + box.width + sensitivity; const yStart = box.y + box.height + sensitivity; const yEnd = box.y - sensitivity; return ( xStart < point[0] && point[0] < xEnd && yEnd < point[1] && point[1] < yStart ) }; /** * Returns Internet Explorer version number (or false if no Internet Explorer used). * * @param string agent Optional parameter to specify user agent */ var getIEVersion = function(agent) { // https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie if (typeof agent === 'undefined') { agent = window.navigator.userAgent; } let pos = agent.indexOf('MSIE '); // up to IE10 if (pos > 0) { return parseInt(agent.substring(pos + 5, agent.indexOf('.', pos)), 10) } pos = agent.indexOf('Trident/'); // IE11 if (pos > 0) { pos = agent.indexOf('rv:'); return parseInt(agent.substring(pos + 3, agent.indexOf('.', pos)), 10) } return false }; /** * Returns whether the used browser is Internet Explorer. * * @param {Number} version Optional parameter to specify IE version */ var isIE = function(version) { const ver = getIEVersion(); if (typeof version === 'undefined') { return !!ver } return version === ver }; function AxisInternal(component, params) { var internal = this; internal.component = component; internal.params = params || {}; internal.d3 = component.d3; internal.scale = internal.d3.scaleLinear(); internal.range; internal.orient = 'bottom'; internal.innerTickSize = 6; internal.outerTickSize = this.params.withOuterTick ? 6 : 0; internal.tickPadding = 3; internal.tickValues = null; internal.tickFormat; internal.tickArguments; internal.tickOffset = 0; internal.tickCulling = true; internal.tickCentered; internal.tickTextCharSize; internal.tickTextRotate = internal.params.tickTextRotate; internal.tickLength; internal.axis = internal.generateAxis(); } AxisInternal.prototype.axisX = function(selection, x, tickOffset) { selection.attr('transform', function(d) { return 'translate(' + Math.ceil(x(d) + tickOffset) + ', 0)' }); }; AxisInternal.prototype.axisY = function(selection, y) { selection.attr('transform', function(d) { return 'translate(0,' + Math.ceil(y(d)) + ')' }); }; AxisInternal.prototype.scaleExtent = function(domain) { var start = domain[0], stop = domain[domain.length - 1]; return start < stop ? [start, stop] : [stop, start] }; AxisInternal.prototype.generateTicks = function(scale) { var internal = this; var i, domain, ticks = []; if (scale.ticks) { return scale.ticks.apply(scale, internal.tickArguments) } domain = scale.domain(); for (i = Math.ceil(domain[0]); i < domain[1]; i++) { ticks.push(i); } if (ticks.length > 0 && ticks[0] > 0) { ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); } return ticks }; AxisInternal.prototype.copyScale = function() { var internal = this; var newScale = internal.scale.copy(), domain; if (internal.params.isCategory) { domain = internal.scale.domain(); newScale.domain([domain[0], domain[1] - 1]); } return newScale }; AxisInternal.prototype.textFormatted = function(v) { var internal = this, formatted = internal.tickFormat ? internal.tickFormat(v) : v; return typeof formatted !== 'undefined' ? formatted : '' }; AxisInternal.prototype.updateRange = function() { var internal = this; internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()); return internal.range }; AxisInternal.prototype.updateTickTextCharSize = function(tick) { var internal = this; if (internal.tickTextCharSize) { return internal.tickTextCharSize } var size = { h: 11.5, w: 5.5 }; tick .select('text') .text(function(d) { return internal.textFormatted(d) }) .each(function(d) { var box = getBBox(this), text = internal.textFormatted(d), h = box.height, w = text ? box.width / text.length : undefined; if (h && w) { size.h = h; size.w = w; } }) .text(''); internal.tickTextCharSize = size; return size }; AxisInternal.prototype.isVertical = function() { return this.orient === 'left' || this.orient === 'right' }; AxisInternal.prototype.tspanData = function(d, i, scale) { var internal = this; var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d)); if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) { splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax); } return splitted.map(function(s) { return { index: i, splitted: s, length: splitted.length } }) }; AxisInternal.prototype.splitTickText = function(d, scale) { var internal = this, tickText = internal.textFormatted(d), maxWidth = internal.params.tickWidth, subtext, spaceIndex, textWidth, splitted = []; if (Object.prototype.toString.call(tickText) === '[object Array]') { return tickText } if (!maxWidth || maxWidth <= 0) { maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110; } function split(splitted, text) { spaceIndex = undefined; for (var i = 1; i < text.length; i++) { if (text.charAt(i) === ' ') { spaceIndex = i; } subtext = text.substr(0, i + 1); textWidth = internal.tickTextCharSize.w * subtext.length; // if text width gets over tick width, split by space index or crrent index if (maxWidth < textWidth) { return split( splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i) ) } } return splitted.concat(text) } return split(splitted, tickText + '') }; AxisInternal.prototype.ellipsify = function(splitted, max) { if (splitted.length <= max) { return splitted } var ellipsified = splitted.slice(0, max); var remaining = 3; for (var i = max - 1; i >= 0; i--) { var available = ellipsified[i].length; ellipsified[i] = ellipsified[i] .substr(0, available - remaining) .padEnd(available, '.'); remaining -= available; if (remaining <= 0) { break } } return ellipsified }; AxisInternal.prototype.updateTickLength = function() { var internal = this; internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding; }; AxisInternal.prototype.lineY2 = function(d) { var internal = this, tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset); return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0 }; AxisInternal.prototype.textY = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength }; AxisInternal.prototype.textTransform = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 'rotate(' + rotate + ')' : '' }; AxisInternal.prototype.textTextAnchor = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? (rotate > 0 ? 'start' : 'end') : 'middle' }; AxisInternal.prototype.tspanDx = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0 }; AxisInternal.prototype.tspanDy = function(d, i) { var internal = this, dy = internal.tickTextCharSize.h; if (i === 0) { if (internal.isVertical()) { dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3); } else { dy = '.71em'; } } return dy }; AxisInternal.prototype.generateAxis = function() { var internal = this, d3 = internal.d3, params = internal.params; function axis(g, transition) { var self; g.each(function() { var g = (axis.g = d3.select(this)); var scale0 = this.__chart__ || internal.scale, scale1 = (this.__chart__ = internal.copyScale()); var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), ticks = g.selectAll('.tick').data(ticksValues, scale1), tickEnter = ticks .enter() .insert('g', '.domain') .attr('class', 'tick') .style('opacity', 1e-6), // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. tickExit = ticks.exit().remove(), tickUpdate = ticks.merge(tickEnter), tickTransform, tickX, tickY; if (params.isCategory) { internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); tickX = internal.tickCentered ? 0 : internal.tickOffset; tickY = internal.tickCentered ? internal.tickOffset : 0; } else { internal.tickOffset = tickX = 0; } internal.updateRange(); internal.updateTickLength(); internal.updateTickTextCharSize(g.select('.tick')); var lineUpdate = tickUpdate .select('line') .merge(tickEnter.append('line')), textUpdate = tickUpdate.select('text').merge(tickEnter.append('text')); var tspans = tickUpdate .selectAll('text') .selectAll('tspan') .data(function(d, i) { return internal.tspanData(d, i, scale1) }), tspanEnter = tspans.enter().append('tspan'), tspanUpdate = tspanEnter.merge(tspans).text(function(d) { return d.splitted }); tspans.exit().remove(); var path = g.selectAll('.domain').data([0]), pathUpdate = path .enter() .append('path') .merge(path) .attr('class', 'domain'); // TODO: each attr should be one function and change its behavior by internal.orient, probably switch (internal.orient) { case 'bottom': { tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return internal.lineY2(d, i) }); textUpdate .attr('x', 0) .attr('y', function(d, i) { return internal.textY(d, i) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }); tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }); pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + internal.outerTickSize ); break } case 'top': { // TODO: rotated tick text tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return -1 * internal.lineY2(d, i) }); textUpdate .attr('x', 0) .attr('y', function(d, i) { return ( -1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2) ) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }); tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }); pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + -internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + -internal.outerTickSize ); break } case 'left': { tickTransform = internal.axisY; lineUpdate .attr('x2', -internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', -internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'end'); tspanUpdate .attr('x', -internal.tickLength) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }); pathUpdate.attr( 'd', 'M' + -internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + -internal.outerTickSize ); break } case 'right': { tickTransform = internal.axisY; lineUpdate .attr('x2', internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'start'); tspanUpdate.attr('x', internal.tickLength).attr('dy', function(d, i) { return internal.tspanDy(d, i) }); pathUpdate.attr( 'd', 'M' + internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + internal.outerTickSize ); break } } if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2; scale0 = scale1 = function(d) { return x(d) + dx }; } else if (scale0.rangeBand) { scale0 = scale1; } else { tickExit.call(tickTransform, scale1, internal.tickOffset); } tickEnter.call(tickTransform, scale0, internal.tickOffset); self = (transition ? tickUpdate.transition(transition) : tickUpdate) .style('opacity', 1) .call(tickTransform, scale1, internal.tickOffset); }); return self } axis.scale = function(x) { if (!arguments.length) { return internal.scale } internal.scale = x; return axis }; axis.orient = function(x) { if (!arguments.length) { return internal.orient } internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + '' : 'bottom'; return axis }; axis.tickFormat = function(format) { if (!arguments.length) { return internal.tickFormat } internal.tickFormat = format; return axis }; axis.tickCentered = function(isCentered) { if (!arguments.length) { return internal.tickCentered } internal.tickCentered = isCentered; return axis }; axis.tickOffset = function() { return internal.tickOffset }; axis.tickInterval = function() { var interval, length; if (params.isCategory) { interval = internal.tickOffset * 2; } else { length = axis.g .select('path.domain') .node() .getTotalLength() - internal.outerTickSize * 2; interval = length / axis.g.selectAll('line').size(); } return interval === Infinity ? 0 : interval }; axis.ticks = function() { if (!arguments.length) { return internal.tickArguments } internal.tickArguments = arguments; return axis }; axis.tickCulling = function(culling) { if (!arguments.length) { return internal.tickCulling } internal.tickCulling = culling; return axis }; axis.tickValues = function(x) { if (typeof x === 'function') { internal.tickValues = function() { return x(internal.scale.domain()) }; } else { if (!arguments.length) { return internal.tickValues } internal.tickValues = x; } return axis }; return axis }; var CLASS = { target: 'c3-target', chart: 'c3-chart', chartLine: 'c3-chart-line', chartLines: 'c3-chart-lines', chartBar: 'c3-chart-bar', chartBars: 'c3-chart-bars', chartText: 'c3-chart-text', chartTexts: 'c3-chart-texts', chartArc: 'c3-chart-arc', chartArcs: 'c3-chart-arcs', chartArcsTitle: 'c3-chart-arcs-title', chartArcsBackground: 'c3-chart-arcs-background', chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', selectedCircle: 'c3-selected-circle', selectedCircles: 'c3-selected-circles', eventRect: 'c3-event-rect', eventRects: 'c3-event-rects', eventRectsSingle: 'c3-event-rects-single', eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', regions: 'c3-regions', title: 'c3-title', tooltipContainer: 'c3-tooltip-container', tooltip: 'c3-tooltip', tooltipName: 'c3-tooltip-name', shape: 'c3-shape', shapes: 'c3-shapes', line: 'c3-line', lines: 'c3-lines', bar: 'c3-bar', bars: 'c3-bars', circle: 'c3-circle', circles: 'c3-circles', arc: 'c3-arc', arcLabelLine: 'c3-arc-label-line', arcs: 'c3-arcs', area: 'c3-area', areas: 'c3-areas', empty: 'c3-empty', text: 'c3-text', texts: 'c3-texts', gaugeValue: 'c3-gauge-value', grid: 'c3-grid', gridLines: 'c3-grid-lines', xgrid: 'c3-xgrid', xgrids: 'c3-xgrids', xgridLine: 'c3-xgrid-line', xgridLines: 'c3-xgrid-lines', xgridFocus: 'c3-xgrid-focus', ygrid: 'c3-ygrid', ygrids: 'c3-ygrids', ygridLine: 'c3-ygrid-line', ygridLines: 'c3-ygrid-lines', colorScale: 'c3-colorscale', stanfordElements: 'c3-stanford-elements', stanfordLine: 'c3-stanford-line', stanfordLines: 'c3-stanford-lines', stanfordRegion: 'c3-stanford-region', stanfordRegions: 'c3-stanford-regions', stanfordText: 'c3-stanford-text', stanfordTexts: 'c3-stanford-texts', axis: 'c3-axis', axisX: 'c3-axis-x', axisXLabel: 'c3-axis-x-label', axisY: 'c3-axis-y', axisYLabel: 'c3-axis-y-label', axisY2: 'c3-axis-y2', axisY2Label: 'c3-axis-y2-label', legendBackground: 'c3-legend-background', legendItem: 'c3-legend-item', legendItemEvent: 'c3-legend-item-event', legendItemTile: 'c3-legend-item-tile', legendItemHidden: 'c3-legend-item-hidden', legendItemFocused: 'c3-legend-item-focused', dragarea: 'c3-dragarea', EXPANDED: '_expanded_', SELECTED: '_selected_', INCLUDED: '_included_' }; class Axis { constructor(owner) { this.owner = owner; this.d3 = owner.d3; this.internal = AxisInternal; } } Axis.prototype.init = function init() { var $$ = this.owner, config = $$.config, main = $$.main; $$.axes.x = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisX) .attr('clip-path', config.axis_x_inner ? '' : $$.clipPathForXAxis) .attr('transform', $$.getTranslate('x')) .style('visibility', config.axis_x_show ? 'visible' : 'hidden'); $$.axes.x .append('text') .attr('class', CLASS.axisXLabel) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .style('text-anchor', this.textAnchorForXAxisLabel.bind(this)); $$.axes.y = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY) .attr('clip-path', config.axis_y_inner ? '' : $$.clipPathForYAxis) .attr('transform', $$.getTranslate('y')) .style('visibility', config.axis_y_show ? 'visible' : 'hidden'); $$.axes.y .append('text') .attr('class', CLASS.axisYLabel) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForYAxisLabel.bind(this)); $$.axes.y2 = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY2) // clip-path? .attr('transform', $$.getTranslate('y2')) .style('visibility', config.axis_y2_show ? 'visible' : 'hidden'); $$.axes.y2 .append('text') .attr('class', CLASS.axisY2Label) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForY2AxisLabel.bind(this)); }; Axis.prototype.getXAxis = function getXAxis( scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { var $$ = this.owner, config = $$.config, axisParams = { isCategory: $$.isCategorized(), withOuterTick: withOuterTick, tickMultiline: config.axis_x_tick_multiline, tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0, tickWidth: config.axis_x_tick_width, tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, withoutTransition: withoutTransition }, axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient); if ($$.isTimeSeries() && tickValues && typeof tickValues !== 'function') { tickValues = tickValues.map(function(v) { return $$.parseDate(v) }); } // Set tick axis.tickFormat(tickFormat).tickValues(tickValues); if ($$.isCategorized()) { axis.tickCentered(config.axis_x_tick_centered); if (isEmpty(config.axis_x_tick_culling)) { config.axis_x_tick_culling = false; } } return axis }; Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues( targets, axis ) { var $$ = this.owner, config = $$.config, tickValues; if (config.axis_x_tick_fit || config.axis_x_tick_count) { tickValues = this.generateTickValues( $$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries() ); } if (axis) { axis.tickValues(tickValues); } else { $$.xAxis.tickValues(tickValues); $$.subXAxis.tickValues(tickValues); } return tickValues }; Axis.prototype.getYAxis = function getYAxis( axisId, scale, orient, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { const $$ = this.owner; const config = $$.config; let tickFormat = config[`axis_${axisId}_tick_format`]; if (!tickFormat && $$.isAxisNormalized(axisId)) { tickFormat = x => `${x}%`; } const axis = new this.internal(this, { withOuterTick: withOuterTick, withoutTransition: withoutTransition, tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate }).axis .scale(scale) .orient(orient); if (tickFormat) { axis.tickFormat(tickFormat); } if ($$.isTimeSeriesY()) { axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval); } else { axis.tickValues(tickValues); } return axis }; Axis.prototype.getId = function getId(id) { var config = this.owner.config; return id in config.data_axes ? config.data_axes[id] : 'y' }; Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { // #2251 previously set any negative values to a whole number, // however both should be truncated according to the users format specification var $$ = this.owner, config = $$.config; let format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function(v) { return v }; if (config.axis_x_tick_format) { if (isFunction(config.axis_x_tick_format)) { format = config.axis_x_tick_format; } else if ($$.isTimeSeries()) { format = function(date) { return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : '' }; } } return isFunction(format) ? function(v) { return format.call($$, v) } : format }; Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { return tickValues ? tickValues : axis ? axis.tickValues() : undefined }; Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { return this.getTickValues( this.owner.config.axis_x_tick_values, this.owner.xAxis ) }; Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { return this.getTickValues( this.owner.config.axis_y_tick_values, this.owner.yAxis ) }; Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { return this.getTickValues( this.owner.config.axis_y2_tick_values, this.owner.y2Axis ) }; Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId( axisId ) { var $$ = this.owner, config = $$.config, option; if (axisId === 'y') { option = config.axis_y_label; } else if (axisId === 'y2') { option = config.axis_y2_label; } else if (axisId === 'x') { option = config.axis_x_label; } return option }; Axis.prototype.getLabelText = function getLabelText(axisId) { var option = this.getLabelOptionByAxisId(axisId); return isString(option) ? option : option ? option.text : null }; Axis.prototype.setLabelText = function setLabelText(axisId, text) { var $$ = this.owner, config = $$.config, option = this.getLabelOptionByAxisId(axisId); if (isString(option)) { if (axisId === 'y') { config.axis_y_label = text; } else if (axisId === 'y2') { config.axis_y2_label = text; } else if (axisId === 'x') { config.axis_x_label = text; } } else if (option) { option.text = text; } }; Axis.prototype.getLabelPosition = function getLabelPosition( axisId, defaultPosition ) { var option = this.getLabelOptionByAxisId(axisId), position = option && typeof option === 'object' && option.position ? option.position : defaultPosition; return { isInner: position.indexOf('inner') >= 0, isOuter: position.indexOf('outer') >= 0, isLeft: position.indexOf('left') >= 0, isCenter: position.indexOf('center') >= 0, isRight: position.indexOf('right') >= 0, isTop: position.indexOf('top') >= 0, isMiddle: position.indexOf('middle') >= 0, isBottom: position.indexOf('bottom') >= 0 } }; Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { return this.getLabelPosition( 'x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right' ) }; Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { return this.getLabelPosition( 'y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) }; Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { return this.getLabelPosition( 'y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) }; Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition() }; Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { return this.getLabelText('x') }; Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { return this.getLabelText('y') }; Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { return this.getLabelText('y2') }; Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { var $$ = this.owner; if (forHorizontal) { return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width } else { return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0 } }; Axis.prototype.dxForAxisLabel = function dxForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? '0.5em' : position.isRight ? '-0.5em' : '0' } else { return position.isTop ? '-0.5em' : position.isBottom ? '0.5em' : '0' } }; Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end' } else { return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end' } }; Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { return this.xForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { return this.dxForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { var $$ = this.owner, config = $$.config, position = this.getXAxisLabelPosition(); if (config.axis_rotated) { return position.isInner ? '1.2em' : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x')) } else { return position.isInner ? '-0.5em' : $$.getHorizontalAxisHeight('x') - 10 } }; Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { var $$ = this.owner, position = this.getYAxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '-0.5em' : '3em' } else { return position.isInner ? '1.2em' : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10) } }; Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { var $$ = this.owner, position = this.getY2AxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '1.2em' : '-2.2em' } else { return position.isInner ? '-0.5em' : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15) } }; Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( !$$.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.getMaxTickWidth = function getMaxTickWidth( id, withoutRecompute ) { var $$ = this.owner, maxWidth = 0, targetsToShow, scale, axis, dummy, svg; if (withoutRecompute && $$.currentMaxTickWidths[id]) { return $$.currentMaxTickWidths[id] } if ($$.svg) { targetsToShow = $$.filterTargetsToShow($$.data.targets); if (id === 'y') { scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); axis = this.getYAxis( id, scale, $$.yOrient, $$.yAxisTickValues, false, true, true ); } else if (id === 'y2') { scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); axis = this.getYAxis( id, scale, $$.y2Orient, $$.y2AxisTickValues, false, true, true ); } else { scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); axis = this.getXAxis( scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true ); this.updateXAxisTickValues(targetsToShow, axis); } dummy = $$.d3 .select('body') .append('div') .classed('c3', true) ;(svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0)), svg .append('g') .call(axis) .each(function() { $$.d3 .select(this) .selectAll('text') .each(function() { var box = getBBox(this); if (maxWidth < box.width) { maxWidth = box.width; } }); dummy.remove(); }); } $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; return $$.currentMaxTickWidths[id] }; Axis.prototype.updateLabels = function updateLabels(withTransition) { var $$ = this.owner; var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label) ;(withTransition ? axisXLabel.transition() : axisXLabel) .attr('x', this.xForXAxisLabel.bind(this)) .attr('dx', this.dxForXAxisLabel.bind(this)) .attr('dy', this.dyForXAxisLabel.bind(this)) .text(this.textForXAxisLabel.bind(this)) ;(withTransition ? axisYLabel.transition() : axisYLabel) .attr('x', this.xForYAxisLabel.bind(this)) .attr('dx', this.dxForYAxisLabel.bind(this)) .attr('dy', this.dyForYAxisLabel.bind(this)) .text(this.textForYAxisLabel.bind(this)) ;(withTransition ? axisY2Label.transition() : axisY2Label) .attr('x', this.xForY2AxisLabel.bind(this)) .attr('dx', this.dxForY2AxisLabel.bind(this)) .attr('dy', this.dyForY2AxisLabel.bind(this)) .text(this.textForY2AxisLabel.bind(this)); }; Axis.prototype.getPadding = function getPadding( padding, key, defaultValue, domainLength ) { var p = typeof padding === 'number' ? padding : padding[key]; if (!isValue(p)) { return defaultValue } if (padding.unit === 'ratio') { return padding[key] * domainLength } // assume padding is pixels if unit is not specified return this.convertPixelsToAxisPadding(p, domainLength) }; Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding( pixels, domainLength ) { var $$ = this.owner, length = $$.config.axis_rotated ? $$.width : $$.height; return domainLength * (pixels / length) }; Axis.prototype.generateTickValues = function generateTickValues( values, tickCount, forTimeSeries ) { var tickValues = values, targetCount, start, end, count, interval, i, tickValue; if (tickCount) { targetCount = isFunction(tickCount) ? tickCount() : tickCount; // compute ticks according to tickCount if (targetCount === 1) { tickValues = [values[0]]; } else if (targetCount === 2) { tickValues = [values[0], values[values.length - 1]]; } else if (targetCount > 2) { count = targetCount - 2; start = values[0]; end = values[values.length - 1]; interval = (end - start) / (count + 1); // re-construct unique values tickValues = [start]; for (i = 0; i < count; i++) { tickValue = +start + interval * (i + 1); tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); } tickValues.push(end); } } if (!forTimeSeries) { tickValues = tickValues.sort(function(a, b) { return a - b }); } return tickValues }; Axis.prototype.generateTransitions = function generateTransitions(duration) { var $$ = this.owner, axes = $$.axes; return { axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx } }; Axis.prototype.redraw = function redraw(duration, isHidden) { var $$ = this.owner, transition = duration ? $$.d3.transition().duration(duration) : null; $$.axes.x.style('opacity', isHidden ? 0 : 1).call($$.xAxis, transition); $$.axes.y.style('opacity', isHidden ? 0 : 1).call($$.yAxis, transition); $$.axes.y2.style('opacity', isHidden ? 0 : 1).call($$.y2Axis, transition); $$.axes.subx.style('opacity', isHidden ? 0 : 1).call($$.subXAxis, transition); }; var c3 = { version: '0.7.18', chart: { fn: Chart.prototype, internal: { fn: ChartInternal.prototype, axis: { fn: Axis.prototype, internal: { fn: AxisInternal.prototype } } } }, generate: function(config) { return new Chart(config) } }; ChartInternal.prototype.beforeInit = function() { // can do something }; ChartInternal.prototype.afterInit = function() { // can do something }; ChartInternal.prototype.init = function() { var $$ = this, config = $$.config; $$.initParams(); if (config.data_url) { $$.convertUrlToData( config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData ); } else if (config.data_json) { $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); } else if (config.data_rows) { $$.initWithData($$.convertRowsToData(config.data_rows)); } else if (config.data_columns) { $$.initWithData($$.convertColumnsToData(config.data_columns)); } else { throw Error('url or json or rows or columns is required.') } }; ChartInternal.prototype.initParams = function() { var $$ = this, d3 = $$.d3, config = $$.config; // MEMO: clipId needs to be unique because it conflicts when multiple charts exist $$.clipId = 'c3-' + new Date().valueOf() + '-clip'; $$.clipIdForXAxis = $$.clipId + '-xaxis'; $$.clipIdForYAxis = $$.clipId + '-yaxis'; $$.clipIdForGrid = $$.clipId + '-grid'; $$.clipIdForSubchart = $$.clipId + '-subchart'; $$.clipPath = $$.getClipPath($$.clipId); $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis); $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid); $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart); $$.dragStart = null; $$.dragging = false; $$.flowing = false; $$.cancelClick = false; $$.mouseover = undefined; $$.transiting = false; $$.color = $$.generateColor(); $$.levelColor = $$.generateLevelColor(); $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)( $$.config.data_xFormat ); $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat; $$.defaultAxisTimeFormat = function(date) { if (date.getMilliseconds()) { return d3.timeFormat('.%L')(date) } if (date.getSeconds()) { return d3.timeFormat(':%S')(date) } if (date.getMinutes()) { return d3.timeFormat('%I:%M')(date) } if (date.getHours()) { return d3.timeFormat('%I %p')(date) } if (date.getDay() && date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getMonth()) { return d3.timeFormat('%-m/%-d')(date) } return d3.timeFormat('%Y/%-m/%-d')(date) }; $$.hiddenTargetIds = []; $$.hiddenLegendIds = []; $$.focusedTargetIds = []; $$.defocusedTargetIds = []; $$.xOrient = config.axis_rotated ? config.axis_x_inner ? 'right' : 'left' : config.axis_x_inner ? 'top' : 'bottom'; $$.yOrient = config.axis_rotated ? config.axis_y_inner ? 'top' : 'bottom' : config.axis_y_inner ? 'right' : 'left'; $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? 'bottom' : 'top' : config.axis_y2_inner ? 'left' : 'right'; $$.subXOrient = config.axis_rotated ? 'left' : 'bottom'; $$.isLegendRight = config.legend_position === 'right'; $$.isLegendInset = config.legend_position === 'inset'; $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; $$.legendStep = 0; $$.legendItemWidth = 0; $$.legendItemHeight = 0; $$.currentMaxTickWidths = { x: 0, y: 0, y2: 0 }; $$.rotated_padding_left = 30; $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; $$.rotated_padding_top = 5; $$.withoutFadeIn = {}; $$.intervalForObserveInserted = undefined; $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js }; ChartInternal.prototype.initChartElements = function() { if (this.initBar) { this.initBar(); } if (this.initLine) { this.initLine(); } if (this.initArc) { this.initArc(); } if (this.initGauge) { this.initGauge(); } if (this.initText) { this.initText(); } }; ChartInternal.prototype.initWithData = function(data) { var $$ = this, d3 = $$.d3, config = $$.config; var defs, main, binding = true; $$.axis = new Axis($$); if (!config.bindto) { $$.selectChart = d3.selectAll([]); } else if (typeof config.bindto.node === 'function') { $$.selectChart = config.bindto; } else { $$.selectChart = d3.select(config.bindto); } if ($$.selectChart.empty()) { $$.selectChart = d3 .select(document.createElement('div')) .style('opacity', 0); $$.observeInserted($$.selectChart); binding = false; } $$.selectChart.html('').classed('c3', true); // Init data as targets $$.data.xs = {}; $$.data.targets = $$.convertDataToTargets(data); if (config.data_filter) { $$.data.targets = $$.data.targets.filter(config.data_filter); } // Set targets to hide if needed if (config.data_hide) { $$.addHiddenTargetIds( config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide ); } if (config.legend_hide) { $$.addHiddenLegendIds( config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide ); } if ($$.isStanfordGraphType()) { $$.initStanfordData(); } // Init sizes and scales $$.updateSizes(); $$.updateScales(); // Set domains for each scale $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); $$.y.domain($$.getYDomain($$.data.targets, 'y')); $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); $$.subX.domain($$.x.domain()); $$.subY.domain($$.y.domain()); $$.subY2.domain($$.y2.domain()); // Save original x domain for zoom update $$.orgXDomain = $$.x.domain(); /*-- Basic Elements --*/ // Define svgs $$.svg = $$.selectChart .append('svg') .style('overflow', 'hidden') .on('mouseenter', function() { return config.onmouseover.call($$) }) .on('mouseleave', function() { return config.onmouseout.call($$) }); if ($$.config.svg_classname) { $$.svg.attr('class', $$.config.svg_classname); } // Define defs defs = $$.svg.append('defs'); $$.clipChart = $$.appendClip(defs, $$.clipId); $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); $$.updateSvgSize(); // Define regions main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main')); if ($$.initPie) { $$.initPie(); } if ($$.initDragZoom) { $$.initDragZoom(); } if (config.subchart_show && $$.initSubchart) { $$.initSubchart(); } if ($$.initTooltip) { $$.initTooltip(); } if ($$.initLegend) { $$.initLegend(); } if ($$.initTitle) { $$.initTitle(); } if ($$.initZoom) { $$.initZoom(); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // Update selection based on size and scale // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart. if (config.subchart_show && $$.initSubchartBrush) { $$.initSubchartBrush(); } /*-- Main Region --*/ // text when empty main .append('text') .attr('class', CLASS.text + ' ' + CLASS.empty) .attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers. .attr('dominant-baseline', 'middle'); // vertical centering of text at y position in all browsers, except IE. // Regions $$.initRegion(); // Grids $$.initGrid(); // Define g for chart area main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.chart); // Grid lines if (config.grid_lines_front) { $$.initGridLines(); } $$.initStanfordElements(); // Cover whole with rects for events $$.initEventRect(); // Define g for chart $$.initChartElements(); // Add Axis $$.axis.init(); // Set targets $$.updateTargets($$.data.targets); // Set default extent if defined if (config.axis_x_selection) { $$.brush.selectionAsValue($$.getDefaultSelection()); } // Draw with targets if (binding) { $$.updateDimension(); $$.config.oninit.call($$); $$.redraw({ withTransition: false, withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransitionForAxis: false }); } // Bind to resize event $$.bindResize(); // Bind to window focus event $$.bindWindowFocus(); // export element of the chart $$.api.element = $$.selectChart.node(); }; ChartInternal.prototype.smoothLines = function(el, type) { var $$ = this; if (type === 'grid') { el.each(function() { var g = $$.d3.select(this), x1 = g.attr('x1'), x2 = g.attr('x2'), y1 = g.attr('y1'), y2 = g.attr('y2'); g.attr({ x1: Math.ceil(x1), x2: Math.ceil(x2), y1: Math.ceil(y1), y2: Math.ceil(y2) }); }); } }; ChartInternal.prototype.updateSizes = function() { var $$ = this, config = $$.config; var legendHeight = $$.legend ? $$.getLegendHeight() : 0, legendWidth = $$.legend ? $$.getLegendWidth() : 0, legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, hasArc = $$.hasArcType(), xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + xAxisHeight : 0; $$.currentWidth = $$.getCurrentWidth(); $$.currentHeight = $$.getCurrentHeight(); // for main $$.margin = config.axis_rotated ? { top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) } : { top: 4 + $$.getCurrentPaddingTop(), // for top tick text right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: hasArc ? 0 : $$.getCurrentPaddingLeft() }; // for subchart $$.margin2 = config.axis_rotated ? { top: $$.margin.top, right: NaN, bottom: 20 + legendHeightForBottom, left: $$.rotated_padding_left } : { top: $$.currentHeight - subchartHeight - legendHeightForBottom, right: NaN, bottom: xAxisHeight + legendHeightForBottom, left: $$.margin.left }; // for legend $$.margin3 = { top: 0, right: NaN, bottom: 0, left: 0 }; if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); } $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; if ($$.width < 0) { $$.width = 0; } if ($$.height < 0) { $$.height = 0; } $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; if ($$.width2 < 0) { $$.width2 = 0; } if ($$.height2 < 0) { $$.height2 = 0; } // for arc $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); if ($$.hasType('gauge') && !config.gauge_fullCircle) { $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); } if ($$.updateRadius) { $$.updateRadius(); } if ($$.isLegendRight && hasArc) { $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; } }; ChartInternal.prototype.updateTargets = function(targets) { var $$ = this, config = $$.config; /*-- Main --*/ //-- Text --// $$.updateTargetsForText(targets); //-- Bar --// $$.updateTargetsForBar(targets); //-- Line --// $$.updateTargetsForLine(targets); //-- Arc --// if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); } /*-- Sub --*/ if (config.subchart_show && $$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); } // Fade-in each chart $$.showTargets(); }; ChartInternal.prototype.showTargets = function() { var $$ = this; $$.svg .selectAll('.' + CLASS.target) .filter(function(d) { return $$.isTargetToShow(d.id) }) .transition() .duration($$.config.transition_duration) .style('opacity', 1); }; ChartInternal.prototype.redraw = function(options, transitions) { var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config; var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType); var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis; var hideAxis = $$.hasArcType(); var drawArea, drawBar, drawLine, xForText, yForText; var duration, durationForExit, durationForAxis; var transitionsToWait, waitForDraw, flow, transition; var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom; var xv = $$.xv.bind($$), cx, cy; options = options || {}; withY = getOption(options, 'withY', true); withSubchart = getOption(options, 'withSubchart', true); withTransition = getOption(options, 'withTransition', true); withTransform = getOption(options, 'withTransform', false); withUpdateXDomain = getOption(options, 'withUpdateXDomain', false); withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', false); withTrimXDomain = getOption(options, 'withTrimXDomain', true); withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain); withLegend = getOption(options, 'withLegend', false); withEventRect = getOption(options, 'withEventRect', true); withDimension = getOption(options, 'withDimension', true); withTransitionForExit = getOption( options, 'withTransitionForExit', withTransition ); withTransitionForAxis = getOption( options, 'withTransitionForAxis', withTransition ); duration = withTransition ? config.transition_duration : 0; durationForExit = withTransitionForExit ? duration : 0; durationForAxis = withTransitionForAxis ? duration : 0; transitions = transitions || $$.axis.generateTransitions(durationForAxis); // update legend and transform each g if (withLegend && config.legend_show) { $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); } else if (withDimension) { // need to update dimension (e.g. axis.y.tick.values) because y tick values should change // no need to update axis in it because they will be updated in redraw() $$.updateDimension(true); } // MEMO: needed for grids calculation if ($$.isCategorized() && targetsToShow.length === 0) { $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); } if (targetsToShow.length) { $$.updateXDomain( targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain ); if (!config.axis_x_tick_values) { tickValues = $$.axis.updateXAxisTickValues(targetsToShow); } } else { $$.xAxis.tickValues([]); $$.subXAxis.tickValues([]); } if (config.zoom_rescale && !options.flow) { xDomainForZoom = $$.x.orgDomain(); } $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); if (!config.axis_y_tick_values && config.axis_y_tick_count) { $$.yAxis.tickValues( $$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count) ); } if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { $$.y2Axis.tickValues( $$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count) ); } // axes $$.axis.redraw(durationForAxis, hideAxis); // Update axis label $$.axis.updateLabels(withTransition); // show/hide if manual culling needed if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if (config.axis_x_tick_culling && tickValues) { for (i = 1; i < tickValues.length; i++) { if (tickValues.length / i < config.axis_x_tick_culling_max) { intervalForCulling = i; break } } $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function(e) { var index = tickValues.indexOf(e); if (index >= 0) { d3.select(this).style( 'display', index % intervalForCulling ? 'none' : 'block' ); } }); } else { $$.svg .selectAll('.' + CLASS.axisX + ' .tick text') .style('display', 'block'); } } // setup drawer - MEMO: these must be called after axis updated drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); // update circleY based on updated parameters $$.updateCircleY(); // generate circle x/y functions depending on updated params cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); // Update sub domain if (withY) { $$.subY.domain($$.getYDomain(targetsToShow, 'y')); $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); } // xgrid focus $$.updateXgridFocus(); // Data empty label positioning and text. main .select('text.' + CLASS.text + '.' + CLASS.empty) .attr('x', $$.width / 2) .attr('y', $$.height / 2) .text(config.data_empty_label_text) .transition() .style('opacity', targetsToShow.length ? 0 : 1); // event rect if (withEventRect) { $$.redrawEventRect(); } // grid $$.updateGrid(duration); $$.updateStanfordElements(duration); // rect for regions $$.updateRegion(duration); // bars $$.updateBar(durationForExit); // lines, areas and circles $$.updateLine(durationForExit); $$.updateArea(durationForExit); $$.updateCircle(cx, cy); // text if ($$.hasDataLabel()) { $$.updateText(xForText, yForText, durationForExit); } // title if ($$.redrawTitle) { $$.redrawTitle(); } // arc if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } // subchart if (config.subchart_show && $$.redrawSubchart) { $$.redrawSubchart( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // circles for select main .selectAll('.' + CLASS.selectedCircles) .filter($$.isBarType.bind($$)) .selectAll('circle') .remove(); if (options.flow) { flow = $$.generateFlow({ targets: targetsToShow, flow: options.flow, duration: options.flow.duration, drawBar: drawBar, drawLine: drawLine, drawArea: drawArea, cx: cx, cy: cy, xv: xv, xForText: xForText, yForText: yForText }); } if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. // transition should be derived from one transition transition = d3.transition().duration(duration); transitionsToWait = [] ;[ $$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition) ].forEach(function(transitions) { transitions.forEach(function(transition) { transitionsToWait.push(transition); }); }); // Wait for end of transitions to call flow and onrendered callback waitForDraw = $$.generateWait(); transitionsToWait.forEach(function(t) { waitForDraw.add(t); }); waitForDraw(function() { if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } }); } else { $$.redrawBar(drawBar); $$.redrawLine(drawLine); $$.redrawArea(drawArea); $$.redrawCircle(cx, cy); $$.redrawText(xForText, yForText, options.flow); $$.redrawRegion(); $$.redrawGrid(); if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } } // update fadein condition $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = true; }); }; ChartInternal.prototype.updateAndRedraw = function(options) { var $$ = this, config = $$.config, transitions; options = options || {}; // same with redraw options.withTransition = getOption(options, 'withTransition', true); options.withTransform = getOption(options, 'withTransform', false); options.withLegend = getOption(options, 'withLegend', false); // NOT same with redraw options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true); options.withUpdateOrgXDomain = getOption( options, 'withUpdateOrgXDomain', true ); options.withTransitionForExit = false; options.withTransitionForTransform = getOption( options, 'withTransitionForTransform', options.withTransition ); // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) $$.updateSizes(); // MEMO: called in updateLegend in redraw if withLegend if (!(options.withLegend && config.legend_show)) { transitions = $$.axis.generateTransitions( options.withTransitionForAxis ? config.transition_duration : 0 ); // Update scales $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(options.withTransitionForTransform, transitions); } // Draw with new sizes & scales $$.redraw(options, transitions); }; ChartInternal.prototype.redrawWithoutRescale = function() { this.redraw({ withY: false, withSubchart: false, withEventRect: false, withTransitionForAxis: false }); }; ChartInternal.prototype.isTimeSeries = function() { return this.config.axis_x_type === 'timeseries' }; ChartInternal.prototype.isCategorized = function() { return this.config.axis_x_type.indexOf('categor') >= 0 }; ChartInternal.prototype.isCustomX = function() { var $$ = this, config = $$.config; return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)) }; ChartInternal.prototype.isTimeSeriesY = function() { return this.config.axis_y_type === 'timeseries' }; ChartInternal.prototype.getTranslate = function(target) { var $$ = this, config = $$.config, x, y; if (target === 'main') { x = asHalfPixel($$.margin.left); y = asHalfPixel($$.margin.top); } else if (target === 'context') { x = asHalfPixel($$.margin2.left); y = asHalfPixel($$.margin2.top); } else if (target === 'legend') { x = $$.margin3.left; y = $$.margin3.top; } else if (target === 'x') { x = 0; y = config.axis_rotated ? 0 : $$.height; } else if (target === 'y') { x = 0; y = config.axis_rotated ? $$.height : 0; } else if (target === 'y2') { x = config.axis_rotated ? 0 : $$.width; y = config.axis_rotated ? 1 : 0; } else if (target === 'subx') { x = 0; y = config.axis_rotated ? 0 : $$.height2; } else if (target === 'arc') { x = $$.arcWidth / 2; y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label } return 'translate(' + x + ',' + y + ')' }; ChartInternal.prototype.initialOpacity = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0 }; ChartInternal.prototype.initialOpacityForCircle = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0 }; ChartInternal.prototype.opacityForCircle = function(d) { var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show; var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0; return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0 }; ChartInternal.prototype.opacityForText = function() { return this.hasDataLabel() ? 1 : 0 }; ChartInternal.prototype.xx = function(d) { return d ? this.x(d.x) : null }; ChartInternal.prototype.xvCustom = function(d, xyValue) { var $$ = this, value = xyValue ? d[xyValue] : d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)) }; ChartInternal.prototype.yvCustom = function(d, xyValue) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y, value = xyValue ? d[xyValue] : d.value; return Math.ceil(yScale(value)) }; ChartInternal.prototype.xv = function(d) { var $$ = this, value = d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)) }; ChartInternal.prototype.yv = function(d) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; return Math.ceil(yScale(d.value)) }; ChartInternal.prototype.subxx = function(d) { return d ? this.subX(d.x) : null }; ChartInternal.prototype.transformMain = function(withTransition, transitions) { var $$ = this, xAxis, yAxis, y2Axis; if (transitions && transitions.axisX) { xAxis = transitions.axisX; } else { xAxis = $$.main.select('.' + CLASS.axisX); if (withTransition) { xAxis = xAxis.transition(); } } if (transitions && transitions.axisY) { yAxis = transitions.axisY; } else { yAxis = $$.main.select('.' + CLASS.axisY); if (withTransition) { yAxis = yAxis.transition(); } } if (transitions && transitions.axisY2) { y2Axis = transitions.axisY2; } else { y2Axis = $$.main.select('.' + CLASS.axisY2); if (withTransition) { y2Axis = y2Axis.transition(); } } (withTransition ? $$.main.transition() : $$.main).attr( 'transform', $$.getTranslate('main') ); xAxis.attr('transform', $$.getTranslate('x')); yAxis.attr('transform', $$.getTranslate('y')); y2Axis.attr('transform', $$.getTranslate('y2')); $$.main .select('.' + CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); }; ChartInternal.prototype.transformAll = function(withTransition, transitions) { var $$ = this; $$.transformMain(withTransition, transitions); if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); } if ($$.legend) { $$.transformLegend(withTransition); } }; ChartInternal.prototype.updateSvgSize = function() { var $$ = this, brush = $$.svg.select(`.${CLASS.brush} .overlay`); $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); $$.svg .selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]) .select('rect') .attr('width', $$.width) .attr('height', $$.height); $$.svg .select('#' + $$.clipIdForXAxis) .select('rect') .attr('x', $$.getXAxisClipX.bind($$)) .attr('y', $$.getXAxisClipY.bind($$)) .attr('width', $$.getXAxisClipWidth.bind($$)) .attr('height', $$.getXAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForYAxis) .select('rect') .attr('x', $$.getYAxisClipX.bind($$)) .attr('y', $$.getYAxisClipY.bind($$)) .attr('width', $$.getYAxisClipWidth.bind($$)) .attr('height', $$.getYAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForSubchart) .select('rect') .attr('width', $$.width) .attr('height', (brush.size() && brush.attr('height')) || 0); // MEMO: parent div's height will be bigger than svg when $$.selectChart.style('max-height', $$.currentHeight + 'px'); }; ChartInternal.prototype.updateDimension = function(withoutAxis) { var $$ = this; if (!withoutAxis) { if ($$.config.axis_rotated) { $$.axes.x.call($$.xAxis); $$.axes.subx.call($$.subXAxis); } else { $$.axes.y.call($$.yAxis); $$.axes.y2.call($$.y2Axis); } } $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); $$.transformAll(false); }; ChartInternal.prototype.observeInserted = function(selection) { var $$ = this, observer; if (typeof MutationObserver === 'undefined') { window.console.error('MutationObserver not defined.'); return } observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.previousSibling) { observer.disconnect(); // need to wait for completion of load because size calculation requires the actual sizes determined after that completion $$.intervalForObserveInserted = window.setInterval(function() { // parentNode will NOT be null when completed if (selection.node().parentNode) { window.clearInterval($$.intervalForObserveInserted); $$.updateDimension(); if ($$.brush) { $$.brush.update(); } $$.config.oninit.call($$); $$.redraw({ withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransition: false, withTransitionForTransform: false, withLegend: true }); selection.transition().style('opacity', 1); } }, 10); } }); }); observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }); }; /** * Binds handlers to the window resize event. */ ChartInternal.prototype.bindResize = function() { var $$ = this, config = $$.config; $$.resizeFunction = $$.generateResize(); // need to call .remove $$.resizeFunction.add(function() { config.onresize.call($$); }); if (config.resize_auto) { $$.resizeFunction.add(function() { if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } $$.resizeTimeout = window.setTimeout(function() { delete $$.resizeTimeout; $$.updateAndRedraw({ withUpdateXDomain: false, withUpdateOrgXDomain: false, withTransition: false, withTransitionForTransform: false, withLegend: true }); if ($$.brush) { $$.brush.update(); } }, 100); }); } $$.resizeFunction.add(function() { config.onresized.call($$); }); $$.resizeIfElementDisplayed = function() { // if element not displayed skip it if ($$.api == null || !$$.api.element.offsetParent) { return } $$.resizeFunction(); }; if (window.attachEvent) { window.attachEvent('onresize', $$.resizeIfElementDisplayed); } else if (window.addEventListener) { window.addEventListener('resize', $$.resizeIfElementDisplayed, false); } else { // fallback to this, if this is a very old browser var wrapper = window.onresize; if (!wrapper) { // create a wrapper that will call all charts wrapper = $$.generateResize(); } else if (!wrapper.add || !wrapper.remove) { // there is already a handler registered, make sure we call it too wrapper = $$.generateResize(); wrapper.add(window.onresize); } // add this graph to the wrapper, we will be removed if the user calls destroy wrapper.add($$.resizeFunction); window.onresize = function() { // if element not displayed skip it if (!$$.api.element.offsetParent) { return } wrapper(); }; } }; /** * Binds handlers to the window focus event. */ ChartInternal.prototype.bindWindowFocus = function() { if (this.windowFocusHandler) { // The handler is already set return } this.windowFocusHandler = () => { this.redraw(); }; window.addEventListener('focus', this.windowFocusHandler); }; /** * Unbinds from the window focus event. */ ChartInternal.prototype.unbindWindowFocus = function() { window.removeEventListener('focus', this.windowFocusHandler); delete this.windowFocusHandler; }; ChartInternal.prototype.generateResize = function() { var resizeFunctions = []; function callResizeFunctions() { resizeFunctions.forEach(function(f) { f(); }); } callResizeFunctions.add = function(f) { resizeFunctions.push(f); }; callResizeFunctions.remove = function(f) { for (var i = 0; i < resizeFunctions.length; i++) { if (resizeFunctions[i] === f) { resizeFunctions.splice(i, 1); break } } }; return callResizeFunctions }; ChartInternal.prototype.endall = function(transition, callback) { var n = 0; transition .each(function() { ++n; }) .on('end', function() { if (!--n) { callback.apply(this, arguments); } }); }; ChartInternal.prototype.generateWait = function() { var $$ = this; var transitionsToWait = [], f = function(callback) { var timer = setInterval(function() { if (!$$.isTabVisible()) { return } var done = 0; transitionsToWait.forEach(function(t) { if (t.empty()) { done += 1; return } try { t.transition(); } catch (e) { done += 1; } }); if (done === transitionsToWait.length) { clearInterval(timer); if (callback) { callback(); } } }, 50); }; f.add = function(transition) { transitionsToWait.push(transition); }; return f }; ChartInternal.prototype.parseDate = function(date) { var $$ = this, parsedDate; if (date instanceof Date) { parsedDate = date; } else if (typeof date === 'string') { parsedDate = $$.dataTimeParse(date); } else if (typeof date === 'object') { parsedDate = new Date(+date); } else if (typeof date === 'number' && !isNaN(date)) { parsedDate = new Date(+date); } if (!parsedDate || isNaN(+parsedDate)) { window.console.error("Failed to parse x '" + date + "' to Date object"); } return parsedDate }; ChartInternal.prototype.isTabVisible = function() { return !document.hidden }; ChartInternal.prototype.getPathBox = getPathBox; ChartInternal.prototype.CLASS = CLASS; /* jshint ignore:start */ (function() { if (!('SVGPathSeg' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) { this.pathSegType = type; this.pathSegTypeAsLetter = typeAsLetter; this._owningPathSegList = owningPathSegList; }; window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; window.SVGPathSeg.PATHSEG_UNKNOWN = 0; window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; window.SVGPathSeg.PATHSEG_LINETO_REL = 5; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; window.SVGPathSeg.PATHSEG_ARC_ABS = 10; window.SVGPathSeg.PATHSEG_ARC_REL = 11; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; // Notify owning PathSegList on any changes so they can be synchronized back to the path element. window.SVGPathSeg.prototype._segmentChanged = function() { if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this); }; window.SVGPathSegClosePath = function(owningPathSegList) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList ); }; window.SVGPathSegClosePath.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegClosePath.prototype.toString = function() { return '[object SVGPathSegClosePath]' }; window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter }; window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined) }; window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegMovetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegMovetoAbs.prototype.toString = function() { return '[object SVGPathSegMovetoAbs]' }; window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegMovetoRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegMovetoRel.prototype.toString = function() { return '[object SVGPathSegMovetoRel]' }; window.SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegLinetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoAbs.prototype.toString = function() { return '[object SVGPathSegLinetoAbs]' }; window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegLinetoRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoRel.prototype.toString = function() { return '[object SVGPathSegLinetoRel]' }; window.SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicAbs = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicAbs]' }; window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) }; Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicRel = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicRel]' }; window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) }; Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticAbs = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticAbs]' }; window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs( undefined, this._x, this._y, this._x1, this._y1 ) }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticRel = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticRel]' }; window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel( undefined, this._x, this._y, this._x1, this._y1 ) }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegArcAbs = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList ); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegArcAbs.prototype.toString = function() { return '[object SVGPathSegArcAbs]' }; window.SVGPathSegArcAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) }; Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcRel = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList ); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegArcRel.prototype.toString = function() { return '[object SVGPathSegArcRel]' }; window.SVGPathSegArcRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) }; Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList ); this._x = x; }; window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalAbs]' }; window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x }; window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x) }; Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList ); this._x = x; }; window.SVGPathSegLinetoHorizontalRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalRel]' }; window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x }; window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x) }; Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList ); this._y = y; }; window.SVGPathSegLinetoVerticalAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalAbs]' }; window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y }; window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y) }; Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList ); this._y = y; }; window.SVGPathSegLinetoVerticalRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalRel]' }; window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y }; window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y) }; Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothAbs = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList ); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothAbs]' }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs( undefined, this._x, this._y, this._x2, this._y2 ) }; Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoCubicSmoothRel = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList ); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothRel]' }; window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel( undefined, this._x, this._y, this._x2, this._y2 ) }; Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticSmoothAbs = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]' }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs( undefined, this._x, this._y ) }; Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticSmoothRel = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothRel]' }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel( undefined, this._x, this._y ) }; Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); // Add createSVGPathSeg* functions to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined) }; window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicAbs( undefined, x, y, x1, y1, x2, y2 ) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicRel( undefined, x, y, x1, y1, x2, y2 ) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1) }; window.SVGPathElement.prototype.createSVGPathSegArcAbs = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcAbs( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) }; window.SVGPathElement.prototype.createSVGPathSegArcRel = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcRel( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function( x ) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x) }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function( x ) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x) }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function( y ) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function( y ) { return new window.SVGPathSegLinetoVerticalRel(undefined, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y) }; if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { // Add getPathSegAtLength to SVGPathElement. // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. window.SVGPathElement.prototype.getPathSegAtLength = function(distance) { if (distance === undefined || !isFinite(distance)) throw 'Invalid arguments.' var measurementElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); measurementElement.setAttribute('d', this.getAttribute('d')); var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; // If the path is empty, return 0. if (lastPathSegment <= 0) return 0 do { measurementElement.pathSegList.removeItem(lastPathSegment); if (distance > measurementElement.getTotalLength()) break lastPathSegment--; } while (lastPathSegment > 0) return lastPathSegment }; } } if (!('SVGPathSegList' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList window.SVGPathSegList = function(pathElement) { this._pathElement = pathElement; this._list = this._parsePath(this._pathElement.getAttribute('d')); // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { attributes: true, attributeFilter: ['d'] }; this._pathElementMutationObserver = new MutationObserver( this._updateListFromPathMutations.bind(this) ); this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ); }; window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { get: function() { this._checkPathSynchronizedToList(); return this._list.length }, enumerable: true }); // Add the pathSegList accessors to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { get: function() { if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this); return this._pathSegList }, enumerable: true }); // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. Object.defineProperty( window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); Object.defineProperty( window.SVGPathElement.prototype, 'animatedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); Object.defineProperty( window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); // Process any pending mutations to the path element and update the list as needed. // This should be the first call of all public functions and is needed because // MutationObservers are not synchronous so we can have pending asynchronous mutations. window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() { this._updateListFromPathMutations( this._pathElementMutationObserver.takeRecords() ); }; window.SVGPathSegList.prototype._updateListFromPathMutations = function( mutationRecords ) { if (!this._pathElement) return var hasPathMutations = false; mutationRecords.forEach(function(record) { if (record.attributeName == 'd') hasPathMutations = true; }); if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute('d')); }; // Serialize the list and update the path's 'd' attribute. window.SVGPathSegList.prototype._writeListToPath = function() { this._pathElementMutationObserver.disconnect(); this._pathElement.setAttribute( 'd', window.SVGPathSegList._pathSegArrayAsString(this._list) ); this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ); }; // When a path segment changes the list needs to be synchronized back to the path element. window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) { this._writeListToPath(); }; window.SVGPathSegList.prototype.clear = function() { this._checkPathSynchronizedToList(); this._list.forEach(function(pathSeg) { pathSeg._owningPathSegList = null; }); this._list = []; this._writeListToPath(); }; window.SVGPathSegList.prototype.initialize = function(newItem) { this._checkPathSynchronizedToList(); this._list = [newItem]; newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype._checkValidIndex = function(index) { if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw 'INDEX_SIZE_ERR' }; window.SVGPathSegList.prototype.getItem = function(index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); return this._list[index] }; window.SVGPathSegList.prototype.insertItemBefore = function( newItem, index ) { this._checkPathSynchronizedToList(); // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. if (index > this.numberOfItems) index = this.numberOfItems; if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.splice(index, 0, newItem); newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype.replaceItem = function(newItem, index) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._checkValidIndex(index); this._list[index] = newItem; newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype.removeItem = function(index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); var item = this._list[index]; this._list.splice(index, 1); this._writeListToPath(); return item }; window.SVGPathSegList.prototype.appendItem = function(newItem) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.push(newItem); newItem._owningPathSegList = this; // TODO: Optimize this to just append to the existing attribute. this._writeListToPath(); return newItem }; window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) { var string = ''; var first = true; pathSegArray.forEach(function(pathSeg) { if (first) { first = false; string += pathSeg._asPathString(); } else { string += ' ' + pathSeg._asPathString(); } }); return string }; // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. window.SVGPathSegList.prototype._parsePath = function(string) { if (!string || string.length == 0) return [] var owningPathSegList = this; var Builder = function() { this.pathSegList = []; }; Builder.prototype.appendSegment = function(pathSeg) { this.pathSegList.push(pathSeg); }; var Source = function(string) { this._string = string; this._currentIndex = 0; this._endIndex = this._string.length; this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; this._skipOptionalSpaces(); }; Source.prototype._isCurrentSpace = function() { var character = this._string[this._currentIndex]; return ( character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f') ) }; Source.prototype._skipOptionalSpaces = function() { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) this._currentIndex++; return this._currentIndex < this._endIndex }; Source.prototype._skipOptionalSpacesOrDelimiter = function() { if ( this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ',' ) return false if (this._skipOptionalSpaces()) { if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ',' ) { this._currentIndex++; this._skipOptionalSpaces(); } } return this._currentIndex < this._endIndex }; Source.prototype.hasMoreData = function() { return this._currentIndex < this._endIndex }; Source.prototype.peekSegmentType = function() { var lookahead = this._string[this._currentIndex]; return this._pathSegTypeFromChar(lookahead) }; Source.prototype._pathSegTypeFromChar = function(lookahead) { switch (lookahead) { case 'Z': case 'z': return window.SVGPathSeg.PATHSEG_CLOSEPATH case 'M': return window.SVGPathSeg.PATHSEG_MOVETO_ABS case 'm': return window.SVGPathSeg.PATHSEG_MOVETO_REL case 'L': return window.SVGPathSeg.PATHSEG_LINETO_ABS case 'l': return window.SVGPathSeg.PATHSEG_LINETO_REL case 'C': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS case 'c': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL case 'Q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS case 'q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL case 'A': return window.SVGPathSeg.PATHSEG_ARC_ABS case 'a': return window.SVGPathSeg.PATHSEG_ARC_REL case 'H': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS case 'h': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL case 'V': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS case 'v': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL case 'S': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS case 's': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL case 'T': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS case 't': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL default: return window.SVGPathSeg.PATHSEG_UNKNOWN } }; Source.prototype._nextCommandHelper = function( lookahead, previousCommand ) { // Check for remaining coordinates in the current command. if ( (lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH ) { if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL return previousCommand } return window.SVGPathSeg.PATHSEG_UNKNOWN }; Source.prototype.initialCommandIsMoveTo = function() { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) return true var command = this.peekSegmentType(); // Path must start with moveTo. return ( command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL ) }; // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF Source.prototype._parseNumber = function() { var exponent = 0; var integer = 0; var frac = 1; var decimal = 0; var sign = 1; var expsign = 1; var startIndex = this._currentIndex; this._skipOptionalSpaces(); // Read the sign. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '+' ) this._currentIndex++; else if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '-' ) { this._currentIndex++; sign = -1; } if ( this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) != '.') ) // The first character of a number must be one of [0-9+-.]. return undefined // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex; while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) this._currentIndex++; // Advance to first non-digit. if (this._currentIndex != startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1; var multiplier = 1; while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); multiplier *= 10; } } // Read the decimals. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '.' ) { this._currentIndex++; // There must be a least one digit following the . if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { frac *= 10; decimal += (this._string.charAt(this._currentIndex) - '0') / frac; this._currentIndex += 1; } } // Read the exponent part. if ( this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == 'e' || this._string.charAt(this._currentIndex) == 'E') && this._string.charAt(this._currentIndex + 1) != 'x' && this._string.charAt(this._currentIndex + 1) != 'm' ) { this._currentIndex++; // Read the sign of the exponent. if (this._string.charAt(this._currentIndex) == '+') { this._currentIndex++; } else if (this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; expsign = -1; } // There must be an exponent. if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { exponent *= 10; exponent += this._string.charAt(this._currentIndex) - '0'; this._currentIndex++; } } var number = integer + decimal; number *= sign; if (exponent) number *= Math.pow(10, expsign * exponent); if (startIndex == this._currentIndex) return undefined this._skipOptionalSpacesOrDelimiter(); return number }; Source.prototype._parseArcFlag = function() { if (this._currentIndex >= this._endIndex) return undefined var flag = false; var flagChar = this._string.charAt(this._currentIndex++); if (flagChar == '0') flag = false; else if (flagChar == '1') flag = true; else return undefined this._skipOptionalSpacesOrDelimiter(); return flag }; Source.prototype.parseSegment = function() { var lookahead = this._string[this._currentIndex]; var command = this._pathSegTypeFromChar(lookahead); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { // Possibly an implicit command. Not allowed if this is the first command. if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null command = this._nextCommandHelper(lookahead, this._previousCommand); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null } else { this._currentIndex++; } this._previousCommand = command; switch (command) { case window.SVGPathSeg.PATHSEG_MOVETO_REL: return new window.SVGPathSegMovetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_MOVETO_ABS: return new window.SVGPathSegMovetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_REL: return new window.SVGPathSegLinetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_ABS: return new window.SVGPathSegLinetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: return new window.SVGPathSegLinetoHorizontalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: return new window.SVGPathSegLinetoHorizontalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: return new window.SVGPathSegLinetoVerticalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: return new window.SVGPathSegLinetoVerticalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CLOSEPATH: this._skipOptionalSpaces(); return new window.SVGPathSegClosePath(owningPathSegList) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothRel( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothAbs( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticRel( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticAbs( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new window.SVGPathSegCurvetoQuadraticSmoothRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new window.SVGPathSegCurvetoQuadraticSmoothAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_ARC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) case window.SVGPathSeg.PATHSEG_ARC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) default: throw 'Unknown path seg type.' } }; var builder = new Builder(); var source = new Source(string); if (!source.initialCommandIsMoveTo()) return [] while (source.hasMoreData()) { var pathSeg = source.parseSegment(); if (!pathSeg) return [] builder.appendSegment(pathSeg); } return builder.pathSegList }; } })(); // String.padEnd polyfill for IE11 // // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== 'undefined' ? padString : ' '); if (this.length > targetLength) { return String(this) } else { targetLength = targetLength - this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed } return String(this) + padString.slice(0, targetLength) } }; } // Object.assign polyfill for IE11 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object') } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to }, writable: true, configurable: true }); } /* jshint ignore:end */ Chart.prototype.axis = function() {}; Chart.prototype.axis.labels = function(labels) { var $$ = this.internal; if (arguments.length) { Object.keys(labels).forEach(function(axisId) { $$.axis.setLabelText(axisId, labels[axisId]); }); $$.axis.updateLabels(); } // TODO: return some values? }; Chart.prototype.axis.max = function(max) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof max === 'object') { if (isValue(max.x)) { config.axis_x_max = max.x; } if (isValue(max.y)) { config.axis_y_max = max.y; } if (isValue(max.y2)) { config.axis_y2_max = max.y2; } } else { config.axis_y_max = config.axis_y2_max = max; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_max, y: config.axis_y_max, y2: config.axis_y2_max } } }; Chart.prototype.axis.min = function(min) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof min === 'object') { if (isValue(min.x)) { config.axis_x_min = min.x; } if (isValue(min.y)) { config.axis_y_min = min.y; } if (isValue(min.y2)) { config.axis_y2_min = min.y2; } } else { config.axis_y_min = config.axis_y2_min = min; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_min, y: config.axis_y_min, y2: config.axis_y2_min } } }; Chart.prototype.axis.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.axis.max(range.max); } if (isDefined(range.min)) { this.axis.min(range.min); } } else { return { max: this.axis.max(), min: this.axis.min() } } }; Chart.prototype.axis.types = function(types) { const $$ = this.internal; if (types === undefined) { return { y: $$.config.axis_y_type, y2: $$.config.axis_y2_type } } else { if (isDefined(types.y)) { $$.config.axis_y_type = types.y; } if (isDefined(types.y2)) { $$.config.axis_y2_type = types.y2; } $$.updateScales(); $$.redraw(); } }; Chart.prototype.category = function(i, category) { var $$ = this.internal, config = $$.config; if (arguments.length > 1) { config.axis_x_categories[i] = category; $$.redraw(); } return config.axis_x_categories[i] }; Chart.prototype.categories = function(categories) { var $$ = this.internal, config = $$.config; if (!arguments.length) { return config.axis_x_categories } config.axis_x_categories = categories; $$.redraw(); return config.axis_x_categories }; Chart.prototype.resize = function(size) { var $$ = this.internal, config = $$.config; config.size_width = size ? size.width : null; config.size_height = size ? size.height : null; this.flush(); }; Chart.prototype.flush = function() { var $$ = this.internal; $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); }; Chart.prototype.destroy = function() { var $$ = this.internal; window.clearInterval($$.intervalForObserveInserted); if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } if (window.detachEvent) { window.detachEvent('onresize', $$.resizeIfElementDisplayed); } else if (window.removeEventListener) { window.removeEventListener('resize', $$.resizeIfElementDisplayed); } else { var wrapper = window.onresize; // check if no one else removed our wrapper and remove our resizeFunction from it if (wrapper && wrapper.add && wrapper.remove) { wrapper.remove($$.resizeFunction); } } // Removes the inner resize functions $$.resizeFunction.remove(); // Unbinds from the window focus event $$.unbindWindowFocus(); $$.selectChart.classed('c3', false).html(''); // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. Object.keys($$).forEach(function(key) { $$[key] = null; }); return null }; // TODO: fix Chart.prototype.color = function(id) { var $$ = this.internal; return $$.color(id) // more patterns }; Chart.prototype.data = function(targetIds) { var targets = this.internal.data.targets; return typeof targetIds === 'undefined' ? targets : targets.filter(function(t) { return [].concat(targetIds).indexOf(t.id) >= 0 }) }; Chart.prototype.data.shown = function(targetIds) { return this.internal.filterTargetsToShow(this.data(targetIds)) }; /** * Get values of the data loaded in the chart. * * @param {String|Array} targetId This API returns the value of specified target. * @param flat * @return {Array} Data values */ Chart.prototype.data.values = function(targetId, flat = true) { let values = null; if (targetId) { const targets = this.data(targetId); if (targets && isArray(targets)) { values = targets.reduce((ret, v) => { const dataValue = v.values.map(d => d.value); if (flat) { ret = ret.concat(dataValue); } else { ret.push(dataValue); } return ret }, []); } } return values }; Chart.prototype.data.names = function(names) { this.internal.clearLegendItemTextBoxCache(); return this.internal.updateDataAttributes('names', names) }; Chart.prototype.data.colors = function(colors) { return this.internal.updateDataAttributes('colors', colors) }; Chart.prototype.data.axes = function(axes) { return this.internal.updateDataAttributes('axes', axes) }; Chart.prototype.data.stackNormalized = function(normalized) { if (normalized === undefined) { return this.internal.isStackNormalized() } this.internal.config.data_stack_normalize = !!normalized; this.internal.redraw(); }; Chart.prototype.donut = function() {}; Chart.prototype.donut.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.donut_padAngle } this.internal.config.donut_padAngle = padAngle; this.flush(); }; Chart.prototype.flow = function(args) { var $$ = this.internal, targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; if (args.json) { data = $$.convertJsonToData(args.json, args.keys); } else if (args.rows) { data = $$.convertRowsToData(args.rows); } else if (args.columns) { data = $$.convertColumnsToData(args.columns); } else { return } targets = $$.convertDataToTargets(data, true); // Update/Add data $$.data.targets.forEach(function(t) { var found = false, i, j; for (i = 0; i < targets.length; i++) { if (t.id === targets[i].id) { found = true; if (t.values[t.values.length - 1]) { tail = t.values[t.values.length - 1].index + 1; } length = targets[i].values.length; for (j = 0; j < length; j++) { targets[i].values[j].index = tail + j; if (!$$.isTimeSeries()) { targets[i].values[j].x = tail + j; } } t.values = t.values.concat(targets[i].values); targets.splice(i, 1); break } } if (!found) { notfoundIds.push(t.id); } }); // Append null for not found targets $$.data.targets.forEach(function(t) { var i, j; for (i = 0; i < notfoundIds.length; i++) { if (t.id === notfoundIds[i]) { tail = t.values[t.values.length - 1].index + 1; for (j = 0; j < length; j++) { t.values.push({ id: t.id, index: tail + j, x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, value: null }); } } } }); // Generate null values for new target if ($$.data.targets.length) { targets.forEach(function(t) { var i, missing = []; for (i = $$.data.targets[0].values[0].index; i < tail; i++) { missing.push({ id: t.id, index: i, x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, value: null }); } t.values.forEach(function(v) { v.index += tail; if (!$$.isTimeSeries()) { v.x += tail; } }); t.values = missing.concat(t.values); }); } $$.data.targets = $$.data.targets.concat(targets); // add remained // check data count because behavior needs to change when it's only one dataCount = $$.getMaxDataCount(); baseTarget = $$.data.targets[0]; baseValue = baseTarget.values[0]; // Update length to flow if needed if (isDefined(args.to)) { length = 0; to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; baseTarget.values.forEach(function(v) { if (v.x < to) { length++; } }); } else if (isDefined(args.length)) { length = args.length; } // If only one data, update the domain to flow from left edge of the chart if (!orgDataCount) { if ($$.isTimeSeries()) { if (baseTarget.values.length > 1) { diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; } else { diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; } } else { diff = 1; } domain = [baseValue.x - diff, baseValue.x]; $$.updateXDomain(null, true, true, false, domain); } else if (orgDataCount === 1) { if ($$.isTimeSeries()) { diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; $$.updateXDomain(null, true, true, false, domain); } } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ flow: { index: baseValue.index, length: length, duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, done: args.done, orgDataCount: orgDataCount }, withLegend: true, withTransition: orgDataCount > 1, withTrimXDomain: false, withUpdateXAxis: true }); }; ChartInternal.prototype.generateFlow = function(args) { var $$ = this, config = $$.config, d3 = $$.d3; return function() { var targets = args.targets, flow = args.flow, drawBar = args.drawBar, drawLine = args.drawLine, drawArea = args.drawArea, cx = args.cx, cy = args.cy, xv = args.xv, xForText = args.xForText, yForText = args.yForText, duration = args.duration; var translateX, scaleX = 1, transform, flowIndex = flow.index, flowLength = flow.length, flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, flowIndex + flowLength ), orgDomain = $$.x.domain(), domain, durationForFlow = flow.duration || duration, done = flow.done || function() {}, wait = $$.generateWait(); var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle; // set flag $$.flowing = true; // remove head data after rendered $$.data.targets.forEach(function(d) { d.values.splice(0, flowLength); }); // update x domain to generate axis elements for flow domain = $$.updateXDomain(targets, true, true); // update elements related to x scale if ($$.updateXGrid) { $$.updateXGrid(true); } xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid xgridLines = $$.xgridLines || d3.selectAll([]); mainRegion = $$.mainRegion || d3.selectAll([]); mainText = $$.mainText || d3.selectAll([]); mainBar = $$.mainBar || d3.selectAll([]); mainLine = $$.mainLine || d3.selectAll([]); mainArea = $$.mainArea || d3.selectAll([]); mainCircle = $$.mainCircle || d3.selectAll([]); // generate transform to flow if (!flow.orgDataCount) { // if empty if ($$.data.targets[0].values.length !== 1) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, $$.data.targets[0].values.length - 1 ); translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } else { translateX = diffDomain(domain) / 2; } } } else if ( flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x) ) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } } scaleX = diffDomain(orgDomain) / diffDomain(domain); transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; $$.hideXGridFocus(); var flowTransition = d3 .transition() .ease(d3.easeLinear) .duration(durationForFlow); wait.add($$.xAxis($$.axes.x, flowTransition)); wait.add(mainBar.transition(flowTransition).attr('transform', transform)); wait.add(mainLine.transition(flowTransition).attr('transform', transform)); wait.add(mainArea.transition(flowTransition).attr('transform', transform)); wait.add(mainCircle.transition(flowTransition).attr('transform', transform)); wait.add(mainText.transition(flowTransition).attr('transform', transform)); wait.add( mainRegion .filter($$.isRegionOnX) .transition(flowTransition) .attr('transform', transform) ); wait.add(xgrid.transition(flowTransition).attr('transform', transform)); wait.add(xgridLines.transition(flowTransition).attr('transform', transform)); wait(function() { var i, shapes = [], texts = []; // remove flowed elements if (flowLength) { for (i = 0; i < flowLength; i++) { shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); texts.push('.' + CLASS.text + '-' + (flowIndex + i)); } $$.svg .selectAll('.' + CLASS.shapes) .selectAll(shapes) .remove(); $$.svg .selectAll('.' + CLASS.texts) .selectAll(texts) .remove(); $$.svg.select('.' + CLASS.xgrid).remove(); } // draw again for removing flowed elements and reverting attr xgrid .attr('transform', null) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); xgridLines.attr('transform', null); xgridLines .select('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv); xgridLines .select('text') .attr('x', config.axis_rotated ? $$.width : 0) .attr('y', xv); mainBar.attr('transform', null).attr('d', drawBar); mainLine.attr('transform', null).attr('d', drawLine); mainArea.attr('transform', null).attr('d', drawArea); mainCircle .attr('transform', null) .attr('cx', cx) .attr('cy', cy); mainText .attr('transform', null) .attr('x', xForText) .attr('y', yForText) .style('fill-opacity', $$.opacityForText.bind($$)); mainRegion.attr('transform', null); mainRegion .filter($$.isRegionOnX) .attr('x', $$.regionX.bind($$)) .attr('width', $$.regionWidth.bind($$)); // callback for end of flow done(); $$.flowing = false; }); } }; Chart.prototype.focus = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), this.revert(); this.defocus(); candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.expandArc(targetIds); } $$.toggleFocusLegend(targetIds, true); $$.focusedTargetIds = targetIds; $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); }; Chart.prototype.defocus = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } $$.toggleFocusLegend(targetIds, false); $$.focusedTargetIds = $$.focusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); $$.defocusedTargetIds = targetIds; }; Chart.prototype.revert = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } if ($$.config.legend_show) { $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); $$.legend .selectAll($$.selectorLegends(targetIds)) .filter(function() { return $$.d3.select(this).classed(CLASS.legendItemFocused) }) .classed(CLASS.legendItemFocused, false); } $$.focusedTargetIds = []; $$.defocusedTargetIds = []; }; Chart.prototype.xgrids = function(grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_x_lines } config.grid_x_lines = grids; $$.redrawWithoutRescale(); return config.grid_x_lines }; Chart.prototype.xgrids.add = function(grids) { var $$ = this.internal; return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])) }; Chart.prototype.xgrids.remove = function(params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, true); }; Chart.prototype.ygrids = function(grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_y_lines } config.grid_y_lines = grids; $$.redrawWithoutRescale(); return config.grid_y_lines }; Chart.prototype.ygrids.add = function(grids) { var $$ = this.internal; return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])) }; Chart.prototype.ygrids.remove = function(params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, false); }; Chart.prototype.groups = function(groups) { var $$ = this.internal, config = $$.config; if (isUndefined(groups)) { return config.data_groups } config.data_groups = groups; $$.redraw(); return config.data_groups }; Chart.prototype.legend = function() {}; Chart.prototype.legend.show = function(targetIds) { var $$ = this.internal; $$.showLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: true }); }; Chart.prototype.legend.hide = function(targetIds) { var $$ = this.internal; $$.hideLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: false }); }; Chart.prototype.load = function(args) { var $$ = this.internal, config = $$.config; // update xs if specified if (args.xs) { $$.addXs(args.xs); } // update names if exists if ('names' in args) { Chart.prototype.data.names.bind(this)(args.names); } // update classes if exists if ('classes' in args) { Object.keys(args.classes).forEach(function(id) { config.data_classes[id] = args.classes[id]; }); } // update categories if exists if ('categories' in args && $$.isCategorized()) { config.axis_x_categories = args.categories; } // update axes if exists if ('axes' in args) { Object.keys(args.axes).forEach(function(id) { config.data_axes[id] = args.axes[id]; }); } // update colors if exists if ('colors' in args) { Object.keys(args.colors).forEach(function(id) { config.data_colors[id] = args.colors[id]; }); } // use cache if exists if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { $$.load($$.getCaches(args.cacheIds), args.done); return } // unload if needed if (args.unload) { // TODO: do not unload if target will load (included in url/rows/columns) $$.unload( $$.mapToTargetIds(args.unload === true ? null : args.unload), function() { $$.loadFromArgs(args); } ); } else { $$.loadFromArgs(args); } }; Chart.prototype.unload = function(args) { var $$ = this.internal; args = args || {}; if (args instanceof Array) { args = { ids: args }; } else if (typeof args === 'string') { args = { ids: [args] }; } $$.unload($$.mapToTargetIds(args.ids), function() { $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }); }; Chart.prototype.pie = function() {}; Chart.prototype.pie.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.pie_padAngle } this.internal.config.pie_padAngle = padAngle; this.flush(); }; Chart.prototype.regions = function(regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions } config.regions = regions; $$.redrawWithoutRescale(); return config.regions }; Chart.prototype.regions.add = function(regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions } config.regions = config.regions.concat(regions); $$.redrawWithoutRescale(); return config.regions }; Chart.prototype.regions.remove = function(options) { var $$ = this.internal, config = $$.config, duration, classes, regions; options = options || {}; duration = getOption(options, 'duration', config.transition_duration); classes = getOption(options, 'classes', [CLASS.region]); regions = $$.main.select('.' + CLASS.regions).selectAll( classes.map(function(c) { return '.' + c }) ) ;(duration ? regions.transition().duration(duration) : regions) .style('opacity', 0) .remove(); config.regions = config.regions.filter(function(region) { var found = false; if (!region['class']) { return true } region['class'].split(' ').forEach(function(c) { if (classes.indexOf(c) >= 0) { found = true; } }); return !found }); return config.regions }; Chart.prototype.selected = function(targetId) { var $$ = this.internal, d3 = $$.d3; return $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)) .selectAll('.' + CLASS.shape) .filter(function() { return d3.select(this).classed(CLASS.SELECTED) }) .nodes() .map(function(d) { var data = d.__data__; return data.data ? data.data : data }) }; Chart.prototype.select = function(ids, indices, resetOther) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d) && !isSelected) { toggle(true, shape.classed(CLASS.SELECTED, true), d, i); } } else if (isDefined(resetOther) && resetOther) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } }); }; Chart.prototype.unselect = function(ids, indices) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d)) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } } }); }; Chart.prototype.show = function(targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.removeHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('display', isIE() ? 'block' : 'initial', 'important') .style('opacity', 1, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 1); }); if (options.withLegend) { $$.showLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.hide = function(targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.addHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('opacity', 0, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 0); targets.style('display', 'none'); }); if (options.withLegend) { $$.hideLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.toggle = function(targetIds, options) { var that = this, $$ = this.internal; $$.mapToTargetIds(targetIds).forEach(function(targetId) { $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); }); }; Chart.prototype.subchart = function() {}; Chart.prototype.subchart.isShown = function() { const $$ = this.internal; return $$.config.subchart_show }; Chart.prototype.subchart.show = function() { const $$ = this.internal; if ($$.config.subchart_show) { return } $$.config.subchart_show = true; // insert DOM $$.initSubchart(); // update dimensions with sub chart now visible $$.updateDimension(); // insert brush (depends on sizes previously updated) $$.initSubchartBrush(); // attach data $$.updateTargetsForSubchart($$.getTargets()); // reset fade-in state $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = false; }); // redraw chart ! $$.updateAndRedraw(); // update visible targets ! $$.showTargets(); }; Chart.prototype.subchart.hide = function() { const $$ = this.internal; if (!$$.config.subchart_show) { return } $$.config.subchart_show = false; // remove DOM $$.removeSubchart(); // re-render chart $$.redraw(); }; Chart.prototype.tooltip = function() {}; Chart.prototype.tooltip.show = function(args) { var $$ = this.internal, targets, data, mouse = {}; // determine mouse position on the chart if (args.mouse) { mouse = args.mouse; } else { // determine focus data if (args.data) { data = args.data; } else if (typeof args.x !== 'undefined') { if (args.id) { targets = $$.data.targets.filter(function(t) { return t.id === args.id }); } else { targets = $$.data.targets; } data = $$.filterByX(targets, args.x).slice(0, 1)[0]; } mouse = data ? $$.getMousePosition(data) : null; } // emulate mouse events to show $$.dispatchEvent('mousemove', mouse); $$.config.tooltip_onshow.call($$, data); }; Chart.prototype.tooltip.hide = function() { // TODO: get target data by checking the state of focus this.internal.dispatchEvent('mouseout', 0); this.internal.config.tooltip_onhide.call(this); }; Chart.prototype.transform = function(type, targetIds) { var $$ = this.internal, options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; $$.transformTo(targetIds, type, options); }; ChartInternal.prototype.transformTo = function( targetIds, type, optionsForRedraw ) { var $$ = this, withTransitionForAxis = !$$.hasArcType(), options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis }; options.withTransitionForTransform = false; $$.transiting = false; $$.setTargetType(targetIds, type); $$.updateTargets($$.data.targets); // this is needed when transforming to arc $$.updateAndRedraw(options); }; Chart.prototype.x = function(x) { var $$ = this.internal; if (arguments.length) { $$.updateTargetX($$.data.targets, x); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs }; Chart.prototype.xs = function(xs) { var $$ = this.internal; if (arguments.length) { $$.updateTargetXs($$.data.targets, xs); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs }; Chart.prototype.zoom = function(domain) { var $$ = this.internal; if (domain) { if ($$.isTimeSeries()) { domain = domain.map(function(x) { return $$.parseDate(x) }); } if ($$.config.subchart_show) { $$.brush.selectionAsValue(domain, true); } else { $$.updateXDomain(null, true, false, false, domain); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); return domain } else { return $$.x.domain() } }; Chart.prototype.zoom.enable = function(enabled) { var $$ = this.internal; $$.config.zoom_enabled = enabled; $$.updateAndRedraw(); }; Chart.prototype.unzoom = function() { var $$ = this.internal; if ($$.config.subchart_show) { $$.brush.clear(); } else { $$.updateXDomain(null, true, false, false, $$.subX.domain()); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } }; Chart.prototype.zoom.max = function(max) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (max === 0 || max) { config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); } else { return config.zoom_x_max } }; Chart.prototype.zoom.min = function(min) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (min === 0 || min) { config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); } else { return config.zoom_x_min } }; Chart.prototype.zoom.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.domain.max(range.max); } if (isDefined(range.min)) { this.domain.min(range.min); } } else { return { max: this.domain.max(), min: this.domain.min() } } }; ChartInternal.prototype.initPie = function() { var $$ = this, d3 = $$.d3; $$.pie = d3 .pie() .padAngle(this.getPadAngle.bind(this)) .value(function(d) { return d.values.reduce(function(a, b) { return a + b.value }, 0) }); let orderFct = $$.getOrderFunction(); // we need to reverse the returned order if asc or desc to have the slice in expected order. if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) { let defaultSort = orderFct; orderFct = (t1, t2) => defaultSort(t1, t2) * -1; } $$.pie.sort(orderFct || null); }; ChartInternal.prototype.updateRadius = function() { var $$ = this, config = $$.config, w = config.gauge_width || config.donut_width, gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth; $$.radiusExpanded = (Math.min($$.arcWidth, $$.arcHeight) / 2) * ($$.hasType('gauge') ? 0.85 : 1); $$.radius = $$.radiusExpanded * 0.95; $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius; }; ChartInternal.prototype.getPadAngle = function() { if (this.hasType('pie')) { return this.config.pie_padAngle || 0 } else if (this.hasType('donut')) { return this.config.donut_padAngle || 0 } else { return 0 } }; ChartInternal.prototype.updateArc = function() { var $$ = this; $$.svgArc = $$.getSvgArc(); $$.svgArcExpanded = $$.getSvgArcExpanded(); $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); }; ChartInternal.prototype.updateAngle = function(d) { var $$ = this, config = $$.config, found = false, index = 0, gMin, gMax, gTic, gValue; if (!config) { return null } $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function(t) { if (!found && t.data.id === d.data.id) { found = true; d = t; d.index = index; } index++; }); if (isNaN(d.startAngle)) { d.startAngle = 0; } if (isNaN(d.endAngle)) { d.endAngle = d.startAngle; } if ($$.isGaugeType(d.data)) { gMin = config.gauge_min; gMax = config.gauge_max; gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin); gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin; d.startAngle = config.gauge_startingAngle; d.endAngle = d.startAngle + gTic * gValue; } return found ? d : null }; ChartInternal.prototype.getSvgArc = function() { var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }), newArc = function(d, withoutUpdate) { var updated; if (withoutUpdate) { return arc(d) } // for interpolate updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0' }; // TODO: extends all function newArc.centroid = arc.centroid; return newArc }; ChartInternal.prototype.getSvgArcExpanded = function(rate) { rate = rate || 1; var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, expandWidth = Math.min( $$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100 ), arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }); return function(d) { var updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0' } }; ChartInternal.prototype.getArc = function(d, withoutUpdate, force) { return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : 'M 0 0' }; ChartInternal.prototype.transformForArcLabel = function(d) { var $$ = this, config = $$.config, updated = $$.updateAngle(d), c, x, y, h, ratio, translate = '', hasGauge = $$.hasType('gauge'); if (updated && !hasGauge) { c = this.svgArc.centroid(updated); x = isNaN(c[0]) ? 0 : c[0]; y = isNaN(c[1]) ? 0 : c[1]; h = Math.sqrt(x * x + y * y); if ($$.hasType('donut') && config.donut_label_ratio) { ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; } else if ($$.hasType('pie') && config.pie_label_ratio) { ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; } else { ratio = $$.radius && h ? ((36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius) / h : 0; } translate = 'translate(' + x * ratio + ',' + y * ratio + ')'; } else if ( updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1 ) { var y1 = Math.sin(updated.endAngle - Math.PI / 2); x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25); y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3; translate = 'translate(' + x + ',' + y + ')'; } return translate }; /** * @deprecated Use `getRatio('arc', d)` instead. */ ChartInternal.prototype.getArcRatio = function(d) { return this.getRatio('arc', d) }; ChartInternal.prototype.convertToArcData = function(d) { return this.addName({ id: d.data.id, value: d.value, ratio: this.getRatio('arc', d), index: d.index }) }; ChartInternal.prototype.textForArcLabel = function(d) { var $$ = this, updated, value, ratio, id, format; if (!$$.shouldShowArcLabel()) { return '' } updated = $$.updateAngle(d); value = updated ? updated.value : null; ratio = $$.getRatio('arc', updated); id = d.data.id; if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { return '' } format = $$.getArcLabelFormat(); return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio) }; ChartInternal.prototype.textForGaugeMinMax = function(value, isMax) { var $$ = this, format = $$.getGaugeLabelExtents(); return format ? format(value, isMax) : value }; ChartInternal.prototype.expandArc = function(targetIds) { var $$ = this, interval; // MEMO: avoid to cancel transition if ($$.transiting) { interval = window.setInterval(function() { if (!$$.transiting) { window.clearInterval(interval); if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { $$.expandArc(targetIds); } } }, 10); return } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .each(function(d) { if (!$$.shouldExpand(d.data.id)) { return } $$.d3 .select(this) .selectAll('path') .transition() .duration($$.expandDuration(d.data.id)) .attr('d', $$.svgArcExpanded) .transition() .duration($$.expandDuration(d.data.id) * 2) .attr('d', $$.svgArcExpandedSub) .each(function(d) { if ($$.isDonutType(d.data)) ; }); }); }; ChartInternal.prototype.unexpandArc = function(targetIds) { var $$ = this; if ($$.transiting) { return } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .selectAll('path') .transition() .duration(function(d) { return $$.expandDuration(d.data.id) }) .attr('d', $$.svgArc); $$.svg.selectAll('.' + CLASS.arc); }; ChartInternal.prototype.expandDuration = function(id) { var $$ = this, config = $$.config; if ($$.isDonutType(id)) { return config.donut_expand_duration } else if ($$.isGaugeType(id)) { return config.gauge_expand_duration } else if ($$.isPieType(id)) { return config.pie_expand_duration } else { return 50 } }; ChartInternal.prototype.shouldExpand = function(id) { var $$ = this, config = $$.config; return ( ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand) ) }; ChartInternal.prototype.shouldShowArcLabel = function() { var $$ = this, config = $$.config, shouldShow = true; if ($$.hasType('donut')) { shouldShow = config.donut_label_show; } else if ($$.hasType('pie')) { shouldShow = config.pie_label_show; } // when gauge, always true return shouldShow }; ChartInternal.prototype.meetsArcLabelThreshold = function(ratio) { var $$ = this, config = $$.config, threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; return ratio >= threshold }; ChartInternal.prototype.getArcLabelFormat = function() { var $$ = this, config = $$.config, format = config.pie_label_format; if ($$.hasType('gauge')) { format = config.gauge_label_format; } else if ($$.hasType('donut')) { format = config.donut_label_format; } return format }; ChartInternal.prototype.getGaugeLabelExtents = function() { var $$ = this, config = $$.config; return config.gauge_label_extents }; ChartInternal.prototype.getArcTitle = function() { var $$ = this; return $$.hasType('donut') ? $$.config.donut_title : '' }; ChartInternal.prototype.updateTargetsForArc = function(targets) { var $$ = this, main = $$.main, mainPies, mainPieEnter, classChartArc = $$.classChartArc.bind($$), classArcs = $$.classArcs.bind($$), classFocus = $$.classFocus.bind($$); mainPies = main .select('.' + CLASS.chartArcs) .selectAll('.' + CLASS.chartArc) .data($$.pie(targets)) .attr('class', function(d) { return classChartArc(d) + classFocus(d.data) }); mainPieEnter = mainPies .enter() .append('g') .attr('class', classChartArc); mainPieEnter.append('g').attr('class', classArcs); mainPieEnter .append('text') .attr('dy', $$.hasType('gauge') ? '-.1em' : '.35em') .style('opacity', 0) .style('text-anchor', 'middle') .style('pointer-events', 'none'); // MEMO: can not keep same color..., but not bad to update color in redraw //mainPieUpdate.exit().remove(); }; ChartInternal.prototype.initArc = function() { var $$ = this; $$.arcs = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); $$.arcs .append('text') .attr('class', CLASS.chartArcsTitle) .style('text-anchor', 'middle') .text($$.getArcTitle()); }; ChartInternal.prototype.redrawArc = function( duration, durationForExit, withTransform ) { var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, arcs, mainArc, arcLabelLines, mainArcLabelLine, hasGaugeType = $$.hasType('gauge'); arcs = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arc) .data($$.arcData.bind($$)); mainArc = arcs .enter() .append('path') .attr('class', $$.classArc.bind($$)) .style('fill', function(d) { return $$.color(d.data) }) .style('cursor', function(d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? 'pointer' : null }) .each(function(d) { if ($$.isGaugeType(d.data)) { d.startAngle = d.endAngle = config.gauge_startingAngle; } this._current = d; }) .merge(arcs); if (hasGaugeType) { arcLabelLines = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arcLabelLine) .data($$.arcData.bind($$)); mainArcLabelLine = arcLabelLines .enter() .append('rect') .attr('class', function(d) { return ( CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id ) }) .merge(arcLabelLines); if ($$.filterTargetsToShow($$.data.targets).length === 1) { mainArcLabelLine.style('display', 'none'); } else { mainArcLabelLine .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data) }) .style('display', config.gauge_labelLine_show ? '' : 'none') .each(function(d) { var lineLength = 0, lineThickness = 2, x = 0, y = 0, transform = ''; if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) { var updated = $$.updateAngle(d), innerLineLength = ($$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length) * (updated.index + 1), lineAngle = updated.endAngle - Math.PI / 2, arcInnerRadius = $$.radius - innerLineLength, linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius); lineLength = $$.radiusExpanded - $$.radius + innerLineLength; x = Math.cos(linePositioningAngle) * arcInnerRadius; y = Math.sin(linePositioningAngle) * arcInnerRadius; transform = 'rotate(' + (lineAngle * 180) / Math.PI + ', ' + x + ', ' + y + ')'; } d3.select(this) .attr('x', x) .attr('y', y) .attr('width', lineLength) .attr('height', lineThickness) .attr('transform', transform) .style( 'stroke-dasharray', '0, ' + (lineLength + lineThickness) + ', 0' ); }); } } mainArc .attr('transform', function(d) { return !$$.isGaugeType(d.data) && withTransform ? 'scale(0)' : '' }) .on( 'mouseover', config.interaction_enabled ? function(d) { var updated, arcData; if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.expandArc(updated.data.id); $$.api.focus(updated.data.id); $$.toggleFocusLegend(updated.data.id, true); $$.config.data_onmouseover(arcData, this); } } : null ) .on( 'mousemove', config.interaction_enabled ? function(d) { var updated = $$.updateAngle(d), arcData, selectedData; if (updated) { (arcData = $$.convertToArcData(updated)), (selectedData = [arcData]); $$.showTooltip(selectedData, this); } } : null ) .on( 'mouseout', config.interaction_enabled ? function(d) { var updated, arcData; if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.unexpandArc(updated.data.id); $$.api.revert(); $$.revertLegend(); $$.hideTooltip(); $$.config.data_onmouseout(arcData, this); } } : null ) .on( 'click', config.interaction_enabled ? function(d, i) { var updated = $$.updateAngle(d), arcData; if (updated) { arcData = $$.convertToArcData(updated); if ($$.toggleShape) { $$.toggleShape(this, arcData, i); } $$.config.data_onclick.call($$.api, arcData, this); } } : null ) .each(function() { $$.transiting = true; }) .transition() .duration(duration) .attrTween('d', function(d) { var updated = $$.updateAngle(d), interpolate; if (!updated) { return function() { return 'M 0 0' } } // if (this._current === d) { // this._current = { // startAngle: Math.PI*2, // endAngle: Math.PI*2, // }; // } if (isNaN(this._current.startAngle)) { this._current.startAngle = 0; } if (isNaN(this._current.endAngle)) { this._current.endAngle = this._current.startAngle; } interpolate = d3.interpolate(this._current, updated); this._current = interpolate(0); return function(t) { // prevents crashing the charts once in transition and chart.destroy() has been called if ($$.config === null) { return 'M 0 0' } var interpolated = interpolate(t); interpolated.data = d.data; // data.id will be updated by interporator return $$.getArc(interpolated, true) } }) .attr('transform', withTransform ? 'scale(1)' : '') .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data.id) }) // Where gauge reading color would receive customization. .call($$.endall, function() { $$.transiting = false; }); arcs .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); main .selectAll('.' + CLASS.chartArc) .select('text') .style('opacity', 0) .attr('class', function(d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : '' }) .text($$.textForArcLabel.bind($$)) .attr('transform', $$.transformForArcLabel.bind($$)) .style('font-size', function(d) { return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : '' }) .transition() .duration(duration) .style('opacity', function(d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0 }); main .select('.' + CLASS.chartArcsTitle) .style('opacity', $$.hasType('donut') || hasGaugeType ? 1 : 0); if (hasGaugeType) { let index = 0; const backgroundArc = $$.arcs .select('g.' + CLASS.chartArcsBackground) .selectAll('path.' + CLASS.chartArcsBackground) .data($$.data.targets); backgroundArc .enter() .append('path') .attr( 'class', (d, i) => CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i ) .merge(backgroundArc) .attr('d', d1 => { if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) { return 'M 0 0' } var d = { data: [{ value: config.gauge_max }], startAngle: config.gauge_startingAngle, endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1), index: index++ }; return $$.getArc(d, true, true) }); backgroundArc.exit().remove(); $$.arcs .select('.' + CLASS.chartArcsGaugeUnit) .attr('dy', '.75em') .text(config.gauge_label_show ? config.gauge_units : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMin) .attr( 'dx', -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : '' ); $$.arcs .select('.' + CLASS.chartArcsGaugeMax) .attr( 'dx', $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : '' ); } }; ChartInternal.prototype.initGauge = function() { var arcs = this.arcs; if (this.hasType('gauge')) { arcs.append('g').attr('class', CLASS.chartArcsBackground); arcs .append('text') .attr('class', CLASS.chartArcsGaugeUnit) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMin) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMax) .style('text-anchor', 'middle') .style('pointer-events', 'none'); } }; ChartInternal.prototype.getGaugeLabelHeight = function() { return this.config.gauge_label_show ? 20 : 0 }; /** * Store value into cache * * @param key * @param value */ ChartInternal.prototype.addToCache = function(key, value) { this.cache[`$${key}`] = value; }; /** * Returns a cached value or undefined * * @param key * @return {*} */ ChartInternal.prototype.getFromCache = function(key) { return this.cache[`$${key}`] }; /** * Reset cached data */ ChartInternal.prototype.resetCache = function() { Object.keys(this.cache) .filter(key => /^\$/.test(key)) .forEach(key => { delete this.cache[key]; }); }; // Old API that stores Targets ChartInternal.prototype.hasCaches = function(ids) { for (var i = 0; i < ids.length; i++) { if (!(ids[i] in this.cache)) { return false } } return true }; ChartInternal.prototype.addCache = function(id, target) { this.cache[id] = this.cloneTarget(target); }; ChartInternal.prototype.getCaches = function(ids) { var targets = [], i; for (i = 0; i < ids.length; i++) { if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); } } return targets }; ChartInternal.prototype.categoryName = function(i) { var config = this.config; return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i }; ChartInternal.prototype.generateTargetClass = function(targetId) { return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '' }; ChartInternal.prototype.generateClass = function(prefix, targetId) { return ' ' + prefix + ' ' + prefix + this.generateTargetClass(targetId) }; ChartInternal.prototype.classText = function(d) { return this.generateClass(CLASS.text, d.index) }; ChartInternal.prototype.classTexts = function(d) { return this.generateClass(CLASS.texts, d.id) }; ChartInternal.prototype.classShape = function(d) { return this.generateClass(CLASS.shape, d.index) }; ChartInternal.prototype.classShapes = function(d) { return this.generateClass(CLASS.shapes, d.id) }; ChartInternal.prototype.classLine = function(d) { return this.classShape(d) + this.generateClass(CLASS.line, d.id) }; ChartInternal.prototype.classLines = function(d) { return this.classShapes(d) + this.generateClass(CLASS.lines, d.id) }; ChartInternal.prototype.classCircle = function(d) { return this.classShape(d) + this.generateClass(CLASS.circle, d.index) }; ChartInternal.prototype.classCircles = function(d) { return this.classShapes(d) + this.generateClass(CLASS.circles, d.id) }; ChartInternal.prototype.classBar = function(d) { return this.classShape(d) + this.generateClass(CLASS.bar, d.index) }; ChartInternal.prototype.classBars = function(d) { return this.classShapes(d) + this.generateClass(CLASS.bars, d.id) }; ChartInternal.prototype.classArc = function(d) { return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id) }; ChartInternal.prototype.classArcs = function(d) { return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id) }; ChartInternal.prototype.classArea = function(d) { return this.classShape(d) + this.generateClass(CLASS.area, d.id) }; ChartInternal.prototype.classAreas = function(d) { return this.classShapes(d) + this.generateClass(CLASS.areas, d.id) }; ChartInternal.prototype.classRegion = function(d, i) { return ( this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '') ) }; ChartInternal.prototype.classEvent = function(d) { return this.generateClass(CLASS.eventRect, d.index) }; ChartInternal.prototype.classTarget = function(id) { var $$ = this; var additionalClassSuffix = $$.config.data_classes[id], additionalClass = ''; if (additionalClassSuffix) { additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; } return $$.generateClass(CLASS.target, id) + additionalClass }; ChartInternal.prototype.classFocus = function(d) { return this.classFocused(d) + this.classDefocused(d) }; ChartInternal.prototype.classFocused = function(d) { return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '') }; ChartInternal.prototype.classDefocused = function(d) { return ( ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '') ) }; ChartInternal.prototype.classChartText = function(d) { return CLASS.chartText + this.classTarget(d.id) }; ChartInternal.prototype.classChartLine = function(d) { return CLASS.chartLine + this.classTarget(d.id) }; ChartInternal.prototype.classChartBar = function(d) { return CLASS.chartBar + this.classTarget(d.id) }; ChartInternal.prototype.classChartArc = function(d) { return CLASS.chartArc + this.classTarget(d.data.id) }; ChartInternal.prototype.getTargetSelectorSuffix = function(targetId) { const targetClass = this.generateTargetClass(targetId); if (window.CSS && window.CSS.escape) { return window.CSS.escape(targetClass) } // fallback on imperfect method for old browsers (does not handles unicode) return targetClass.replace(/([?!@#$%^&*()=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1') }; ChartInternal.prototype.selectorTarget = function(id, prefix) { return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id) }; ChartInternal.prototype.selectorTargets = function(ids, prefix) { var $$ = this; ids = ids || []; return ids.length ? ids.map(function(id) { return $$.selectorTarget(id, prefix) }) : null }; ChartInternal.prototype.selectorLegend = function(id) { return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id) }; ChartInternal.prototype.selectorLegends = function(ids) { var $$ = this; return ids && ids.length ? ids.map(function(id) { return $$.selectorLegend(id) }) : null }; ChartInternal.prototype.getClipPath = function(id) { return 'url(' + (isIE(9) ? '' : document.URL.split('#')[0]) + '#' + id + ')' }; ChartInternal.prototype.appendClip = function(parent, id) { return parent .append('clipPath') .attr('id', id) .append('rect') }; ChartInternal.prototype.getAxisClipX = function(forHorizontal) { // axis line width + padding for left var left = Math.max(30, this.margin.left); return forHorizontal ? -(1 + left) : -(left - 1) }; ChartInternal.prototype.getAxisClipY = function(forHorizontal) { return forHorizontal ? -20 : -this.margin.top }; ChartInternal.prototype.getXAxisClipX = function() { var $$ = this; return $$.getAxisClipX(!$$.config.axis_rotated) }; ChartInternal.prototype.getXAxisClipY = function() { var $$ = this; return $$.getAxisClipY(!$$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipX = function() { var $$ = this; return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipY = function() { var $$ = this; return $$.getAxisClipY($$.config.axis_rotated) }; ChartInternal.prototype.getAxisClipWidth = function(forHorizontal) { var $$ = this, left = Math.max(30, $$.margin.left), right = Math.max(30, $$.margin.right); // width + axis line width + padding for left/right return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20 }; ChartInternal.prototype.getAxisClipHeight = function(forHorizontal) { // less than 20 is not enough to show the axis label 'outer' without legend return ( (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20 ) }; ChartInternal.prototype.getXAxisClipWidth = function() { var $$ = this; return $$.getAxisClipWidth(!$$.config.axis_rotated) }; ChartInternal.prototype.getXAxisClipHeight = function() { var $$ = this; return $$.getAxisClipHeight(!$$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipWidth = function() { var $$ = this; return ( $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0) ) }; ChartInternal.prototype.getYAxisClipHeight = function() { var $$ = this; return $$.getAxisClipHeight($$.config.axis_rotated) }; ChartInternal.prototype.generateColor = function() { var $$ = this, config = $$.config, d3 = $$.d3, colors = config.data_colors, pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10, callback = config.data_color, ids = []; return function(d) { var id = d.id || (d.data && d.data.id) || d, color; // if callback function is provided if (colors[id] instanceof Function) { color = colors[id](d); } // if specified, choose that color else if (colors[id]) { color = colors[id]; } // if not specified, choose from pattern else { if (ids.indexOf(id) < 0) { ids.push(id); } color = pattern[ids.indexOf(id) % pattern.length]; colors[id] = color; } return callback instanceof Function ? callback(color, d) : color } }; ChartInternal.prototype.generateLevelColor = function() { var $$ = this, config = $$.config, colors = config.color_pattern, threshold = config.color_threshold, asValue = threshold.unit === 'value', values = threshold.values && threshold.values.length ? threshold.values : [], max = threshold.max || 100; return notEmpty(threshold) && notEmpty(colors) ? function(value) { var i, v, color = colors[colors.length - 1]; for (i = 0; i < values.length; i++) { v = asValue ? value : (value * 100) / max; if (v < values[i]) { color = colors[i]; break } } return color } : null }; ChartInternal.prototype.getDefaultConfig = function() { var config = { bindto: '#chart', svg_classname: undefined, size_width: undefined, size_height: undefined, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, resize_auto: true, zoom_enabled: false, zoom_initialRange: undefined, zoom_type: 'scroll', zoom_disableDefaultBehavior: false, zoom_privileged: false, zoom_rescale: false, zoom_onzoom: function() {}, zoom_onzoomstart: function() {}, zoom_onzoomend: function() {}, zoom_x_min: undefined, zoom_x_max: undefined, interaction_brighten: true, interaction_enabled: true, onmouseover: function() {}, onmouseout: function() {}, onresize: function() {}, onresized: function() {}, oninit: function() {}, onrendered: function() {}, transition_duration: 350, data_epochs: 'epochs', data_x: undefined, data_xs: {}, data_xFormat: '%Y-%m-%d', data_xLocaltime: true, data_xSort: true, data_idConverter: function(id) { return id }, data_names: {}, data_classes: {}, data_groups: [], data_axes: {}, data_type: undefined, data_types: {}, data_labels: {}, data_order: 'desc', data_regions: {}, data_color: undefined, data_colors: {}, data_hide: false, data_filter: undefined, data_selection_enabled: false, data_selection_grouped: false, data_selection_isselectable: function() { return true }, data_selection_multiple: true, data_selection_draggable: false, data_stack_normalize: false, data_onclick: function() {}, data_onmouseover: function() {}, data_onmouseout: function() {}, data_onselected: function() {}, data_onunselected: function() {}, data_url: undefined, data_headers: undefined, data_json: undefined, data_rows: undefined, data_columns: undefined, data_mimeType: undefined, data_keys: undefined, // configuration for no plot-able data supplied. data_empty_label_text: '', // subchart subchart_show: false, subchart_size_height: 60, subchart_axis_x_show: true, subchart_onbrush: function() {}, // color color_pattern: [], color_threshold: {}, // legend legend_show: true, legend_hide: false, legend_position: 'bottom', legend_inset_anchor: 'top-left', legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, legend_item_onclick: undefined, legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, axis_x_type: 'indexed', axis_x_localtime: true, axis_x_categories: [], axis_x_tick_centered: false, axis_x_tick_format: undefined, axis_x_tick_culling: {}, axis_x_tick_culling_max: 10, axis_x_tick_count: undefined, axis_x_tick_fit: true, axis_x_tick_values: null, axis_x_tick_rotate: 0, axis_x_tick_outer: true, axis_x_tick_multiline: true, axis_x_tick_multilineMax: 0, axis_x_tick_width: null, axis_x_max: undefined, axis_x_min: undefined, axis_x_padding: {}, axis_x_height: undefined, axis_x_selection: undefined, axis_x_label: {}, axis_x_inner: undefined, axis_y_show: true, axis_y_type: 'linear', axis_y_max: undefined, axis_y_min: undefined, axis_y_inverted: false, axis_y_center: undefined, axis_y_inner: undefined, axis_y_label: {}, axis_y_tick_format: undefined, axis_y_tick_outer: true, axis_y_tick_values: null, axis_y_tick_rotate: 0, axis_y_tick_count: undefined, axis_y_tick_time_type: undefined, axis_y_tick_time_interval: undefined, axis_y_padding: {}, axis_y_default: undefined, axis_y2_show: false, axis_y2_type: 'linear', axis_y2_max: undefined, axis_y2_min: undefined, axis_y2_inverted: false, axis_y2_center: undefined, axis_y2_inner: undefined, axis_y2_label: {}, axis_y2_tick_format: undefined, axis_y2_tick_outer: true, axis_y2_tick_values: null, axis_y2_tick_count: undefined, axis_y2_padding: {}, axis_y2_default: undefined, // grid grid_x_show: false, grid_x_type: 'tick', grid_x_lines: [], grid_y_show: false, // not used // grid_y_type: 'tick', grid_y_lines: [], grid_y_ticks: 10, grid_focus_show: true, grid_lines_front: true, // point - point of each data point_show: true, point_r: 2.5, point_sensitivity: 10, point_focus_expand_enabled: true, point_focus_expand_r: undefined, point_select_r: undefined, // line line_connectNull: false, line_step_type: 'step', // bar bar_width: undefined, bar_width_ratio: 0.6, bar_width_max: undefined, bar_zerobased: true, bar_space: 0, // area area_zerobased: true, area_above: false, // pie pie_label_show: true, pie_label_format: undefined, pie_label_threshold: 0.05, pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, pie_padAngle: 0, // gauge gauge_fullCircle: false, gauge_label_show: true, gauge_labelLine_show: true, gauge_label_format: undefined, gauge_min: 0, gauge_max: 100, gauge_startingAngle: (-1 * Math.PI) / 2, gauge_label_extents: undefined, gauge_units: undefined, gauge_width: undefined, gauge_arcs_minWidth: 5, gauge_expand: {}, gauge_expand_duration: 50, // donut donut_label_show: true, donut_label_format: undefined, donut_label_threshold: 0.05, donut_label_ratio: undefined, donut_width: undefined, donut_title: '', donut_expand: {}, donut_expand_duration: 50, donut_padAngle: 0, // spline spline_interpolation_type: 'cardinal', // stanford stanford_lines: [], stanford_regions: [], stanford_texts: [], stanford_scaleMin: undefined, stanford_scaleMax: undefined, stanford_scaleWidth: undefined, stanford_scaleFormat: undefined, stanford_scaleValues: undefined, stanford_colors: undefined, stanford_padding: { top: 0, right: 0, bottom: 0, left: 0 }, // region - region to change style regions: [], // tooltip - show when mouseover on each data tooltip_show: true, tooltip_grouped: true, tooltip_order: undefined, tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, tooltip_horizontal: undefined, tooltip_position: undefined, tooltip_contents: function( d, defaultTitleFormat, defaultValueFormat, color ) { return this.getTooltipContent ? this.getTooltipContent( d, defaultTitleFormat, defaultValueFormat, color ) : '' }, tooltip_init_show: false, tooltip_init_x: 0, tooltip_init_position: { top: '0px', left: '50px' }, tooltip_onshow: function() {}, tooltip_onhide: function() {}, // title title_text: undefined, title_padding: { top: 0, right: 0, bottom: 0, left: 0 }, title_position: 'top-center' }; Object.keys(this.additionalConfig).forEach(function(key) { config[key] = this.additionalConfig[key]; }, this); return config }; ChartInternal.prototype.additionalConfig = {}; ChartInternal.prototype.loadConfig = function(config) { var this_config = this.config, target, keys, read; function find() { var key = keys.shift(); // console.log("key =>", key, ", target =>", target); if (key && target && typeof target === 'object' && key in target) { target = target[key]; return find() } else if (!key) { return target } else { return undefined } } Object.keys(this_config).forEach(function(key) { target = config; keys = key.split('_'); read = find(); // console.log("CONFIG : ", key, read); if (isDefined(read)) { this_config[key] = read; } }); }; ChartInternal.prototype.convertUrlToData = function( url, mimeType, headers, keys, done ) { var $$ = this, type = mimeType ? mimeType : 'csv', f, converter; if (type === 'json') { f = $$.d3.json; converter = $$.convertJsonToData; } else if (type === 'tsv') { f = $$.d3.tsv; converter = $$.convertXsvToData; } else { f = $$.d3.csv; converter = $$.convertXsvToData; } f(url, headers) .then(function(data) { done.call($$, converter.call($$, data, keys)); }) .catch(function(error) { throw error }); }; ChartInternal.prototype.convertXsvToData = function(xsv) { var keys = xsv.columns, rows = xsv; if (rows.length === 0) { return { keys, rows: [keys.reduce((row, key) => Object.assign(row, { [key]: null }), {})] } } else { // [].concat() is to convert result into a plain array otherwise // test is not happy because rows have properties. return { keys, rows: [].concat(xsv) } } }; ChartInternal.prototype.convertJsonToData = function(json, keys) { var $$ = this, new_rows = [], targetKeys, data; if (keys) { // when keys specified, json would be an array that includes objects if (keys.x) { targetKeys = keys.value.concat(keys.x); $$.config.data_x = keys.x; } else { targetKeys = keys.value; } new_rows.push(targetKeys); json.forEach(function(o) { var new_row = []; targetKeys.forEach(function(key) { // convert undefined to null because undefined data will be removed in convertDataToTargets() var v = $$.findValueInJson(o, key); if (isUndefined(v)) { v = null; } new_row.push(v); }); new_rows.push(new_row); }); data = $$.convertRowsToData(new_rows); } else { Object.keys(json).forEach(function(key) { new_rows.push([key].concat(json[key])); }); data = $$.convertColumnsToData(new_rows); } return data }; /** * Finds value from the given nested object by the given path. * If it's not found, then this returns undefined. * @param {Object} object the object * @param {string} path the path */ ChartInternal.prototype.findValueInJson = function(object, path) { if (path in object) { // If object has a key that contains . or [], return the key's value // instead of searching for an inner object. // See https://github.com/c3js/c3/issues/1691 for details. return object[path] } path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) path = path.replace(/^\./, ''); // strip a leading dot var pathArray = path.split('.'); for (var i = 0; i < pathArray.length; ++i) { var k = pathArray[i]; if (k in object) { object = object[k]; } else { return } } return object }; /** * Converts the rows to normalized data. * @param {any[][]} rows The row data * @return {Object} */ ChartInternal.prototype.convertRowsToData = rows => { const newRows = []; const keys = rows[0]; for (let i = 1; i < rows.length; i++) { const newRow = {}; for (let j = 0; j < rows[i].length; j++) { if (isUndefined(rows[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRow[keys[j]] = rows[i][j]; } newRows.push(newRow); } return { keys, rows: newRows } }; /** * Converts the columns to normalized data. * @param {any[][]} columns The column data * @return {Object} */ ChartInternal.prototype.convertColumnsToData = columns => { const newRows = []; const keys = []; for (let i = 0; i < columns.length; i++) { const key = columns[i][0]; for (let j = 1; j < columns[i].length; j++) { if (isUndefined(newRows[j - 1])) { newRows[j - 1] = {}; } if (isUndefined(columns[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRows[j - 1][key] = columns[i][j]; } keys.push(key); } return { keys, rows: newRows } }; /** * Converts the data format into the target format. * @param {!Object} data * @param {!Array} data.keys Ordered list of target IDs. * @param {!Array} data.rows Rows of data to convert. * @param {boolean} appendXs True to append to $$.data.xs, False to replace. * @return {!Array} */ ChartInternal.prototype.convertDataToTargets = function(data, appendXs) { var $$ = this, config = $$.config, targets, ids, xs, keys, epochs; // handles format where keys are not orderly provided if (isArray(data)) { keys = Object.keys(data[0]); } else { keys = data.keys; data = data.rows; } xs = keys.filter($$.isX, $$); if (!$$.isStanfordGraphType()) { ids = keys.filter($$.isNotX, $$); } else { epochs = keys.filter($$.isEpochs, $$); ids = keys.filter($$.isNotXAndNotEpochs, $$); if (xs.length !== 1 || epochs.length !== 1 || ids.length !== 1) { throw new Error( "You must define the 'x' key name and the 'epochs' for Stanford Diagrams" ) } } // save x for update data by load when custom x and c3.x API ids.forEach(function(id) { var xKey = $$.getXKey(id); if ($$.isCustomX() || $$.isTimeSeries()) { // if included in input data if (xs.indexOf(xKey) >= 0) { $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : [] ).concat( data .map(function(d) { return d[xKey] }) .filter(isValue) .map(function(rawX, i) { return $$.generateTargetX(rawX, id, i) }) ); } // if not included in input data, find from preloaded data of other id's x else if (config.data_x) { $$.data.xs[id] = $$.getOtherTargetXs(); } // if not included in input data, find from preloaded data else if (notEmpty(config.data_xs)) { $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); } // MEMO: if no x included, use same x of current will be used } else { $$.data.xs[id] = data.map(function(d, i) { return i }); } }); // check x is defined ids.forEach(function(id) { if (!$$.data.xs[id]) { throw new Error('x is not defined for id = "' + id + '".') } }); // convert to target targets = ids.map(function(id, index) { var convertedId = config.data_idConverter(id); return { id: convertedId, id_org: id, values: data .map(function(d, i) { var xKey = $$.getXKey(id), rawX = d[xKey], value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x, returnData; // use x as categories if custom x and categorized if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { if (index === 0 && i === 0) { config.axis_x_categories = []; } x = config.axis_x_categories.indexOf(rawX); if (x === -1) { x = config.axis_x_categories.length; config.axis_x_categories.push(rawX); } } else { x = $$.generateTargetX(rawX, id, i); } // mark as x = undefined if value is undefined and filter to remove after mapped if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { x = undefined; } returnData = { x: x, value: value, id: convertedId }; if ($$.isStanfordGraphType()) { returnData.epochs = d[epochs]; } return returnData }) .filter(function(v) { return isDefined(v.x) }) } }); // finish targets targets.forEach(function(t) { var i; // sort values by its x if (config.data_xSort) { t.values = t.values.sort(function(v1, v2) { var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, x2 = v2.x || v2.x === 0 ? v2.x : Infinity; return x1 - x2 }); } // indexing each value i = 0; t.values.forEach(function(v) { v.index = i++; }); // this needs to be sorted because its index and value.index is identical $$.data.xs[t.id].sort(function(v1, v2) { return v1 - v2 }); }); // cache information about values $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); // set target types if (config.data_type) { $$.setTargetType( $$.mapToIds(targets).filter(function(id) { return !(id in config.data_types) }), config.data_type ); } // cache as original id keyed targets.forEach(function(d) { $$.addCache(d.id_org, d); }); return targets }; ChartInternal.prototype.isEpochs = function(key) { var $$ = this, config = $$.config; return config.data_epochs && key === config.data_epochs }; ChartInternal.prototype.isX = function(key) { var $$ = this, config = $$.config; return ( (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key)) ) }; ChartInternal.prototype.isNotX = function(key) { return !this.isX(key) }; ChartInternal.prototype.isNotXAndNotEpochs = function(key) { return !this.isX(key) && !this.isEpochs(key) }; /** * Returns whether the normalized stack option is enabled or not. * * To be enabled it must also have data.groups defined. * * @return {boolean} */ ChartInternal.prototype.isStackNormalized = function() { return this.config.data_stack_normalize && this.config.data_groups.length > 0 }; /** * Returns whether the axis is normalized or not. * * An axis is normalized as long as one of its associated target * is normalized. * * @param axisId Axis ID (y or y2) * @return {Boolean} */ ChartInternal.prototype.isAxisNormalized = function(axisId) { const $$ = this; if (!$$.isStackNormalized()) { // shortcut return false } return $$.data.targets .filter(target => $$.axis.getId(target.id) === axisId) .some(target => $$.isTargetNormalized(target.id)) }; /** * Returns whether the values for this target ID is normalized or not. * * To be normalized the option needs to be enabled and target needs * to be defined in `data.groups`. * * @param targetId ID of the target * @return {Boolean} True if the target is normalized, false otherwise. */ ChartInternal.prototype.isTargetNormalized = function(targetId) { const $$ = this; return ( $$.isStackNormalized() && $$.config.data_groups.some(group => group.includes(targetId)) ) }; ChartInternal.prototype.getXKey = function(id) { var $$ = this, config = $$.config; return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null }; /** * Get sum of visible data per index for given axis. * * Expect axisId to be either 'y' or 'y2'. * * @private * @param axisId Compute sum for data associated to given axis. * @return {Array} */ ChartInternal.prototype.getTotalPerIndex = function(axisId) { const $$ = this; if (!$$.isStackNormalized()) { return null } const cached = $$.getFromCache('getTotalPerIndex'); if (cached !== undefined) { return cached[axisId] } const sum = { y: [], y2: [] }; $$.data.targets // keep only target that are normalized .filter(target => $$.isTargetNormalized(target.id)) // keep only target that are visible .filter(target => $$.isTargetToShow(target.id)) // compute sum per axis .forEach(target => { const sumByAxis = sum[$$.axis.getId(target.id)]; target.values.forEach((v, i) => { if (!sumByAxis[i]) { sumByAxis[i] = 0; } sumByAxis[i] += isNumber(v.value) ? v.value : 0; }); }); $$.addToCache('getTotalPerIndex', sum); return sum[axisId] }; /** * Get sum of visible data. * * Should be used for normalised data only since all values * are expected to be positive. * * @private * @return {Number} */ ChartInternal.prototype.getTotalDataSum = function() { const $$ = this; const cached = $$.getFromCache('getTotalDataSum'); if (cached !== undefined) { return cached } const totalDataSum = flattenArray( $$.data.targets .filter(target => $$.isTargetToShow(target.id)) .map(target => target.values) ) .map(d => d.value) .reduce((p, c) => p + c, 0); $$.addToCache('getTotalDataSum', totalDataSum); return totalDataSum }; ChartInternal.prototype.getXValuesOfXKey = function(key, targets) { var $$ = this, xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; ids.forEach(function(id) { if ($$.getXKey(id) === key) { xValues = $$.data.xs[id]; } }); return xValues }; ChartInternal.prototype.getXValue = function(id, i) { var $$ = this; return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i }; ChartInternal.prototype.getOtherTargetXs = function() { var $$ = this, idsForX = Object.keys($$.data.xs); return idsForX.length ? $$.data.xs[idsForX[0]] : null }; ChartInternal.prototype.getOtherTargetX = function(index) { var xs = this.getOtherTargetXs(); return xs && index < xs.length ? xs[index] : null }; ChartInternal.prototype.addXs = function(xs) { var $$ = this; Object.keys(xs).forEach(function(id) { $$.config.data_xs[id] = xs[id]; }); }; ChartInternal.prototype.addName = function(data) { var $$ = this, name; if (data) { name = $$.config.data_names[data.id]; data.name = name !== undefined ? name : data.id; } return data }; ChartInternal.prototype.getValueOnIndex = function(values, index) { var valueOnIndex = values.filter(function(v) { return v.index === index }); return valueOnIndex.length ? valueOnIndex[0] : null }; ChartInternal.prototype.updateTargetX = function(targets, x) { var $$ = this; targets.forEach(function(t) { t.values.forEach(function(v, i) { v.x = $$.generateTargetX(x[i], t.id, i); }); $$.data.xs[t.id] = x; }); }; ChartInternal.prototype.updateTargetXs = function(targets, xs) { var $$ = this; targets.forEach(function(t) { if (xs[t.id]) { $$.updateTargetX([t], xs[t.id]); } }); }; ChartInternal.prototype.generateTargetX = function(rawX, id, index) { var $$ = this, x; if ($$.isTimeSeries()) { x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); } else if ($$.isCustomX() && !$$.isCategorized()) { x = isValue(rawX) ? +rawX : $$.getXValue(id, index); } else { x = index; } return x }; ChartInternal.prototype.cloneTarget = function(target) { return { id: target.id, id_org: target.id_org, values: target.values.map(function(d) { return { x: d.x, value: d.value, id: d.id } }) } }; ChartInternal.prototype.getMaxDataCount = function() { var $$ = this; return $$.d3.max($$.data.targets, function(t) { return t.values.length }) }; ChartInternal.prototype.mapToIds = function(targets) { return targets.map(function(d) { return d.id }) }; ChartInternal.prototype.mapToTargetIds = function(ids) { var $$ = this; return ids ? [].concat(ids) : $$.mapToIds($$.data.targets) }; ChartInternal.prototype.hasTarget = function(targets, id) { var ids = this.mapToIds(targets), i; for (i = 0; i < ids.length; i++) { if (ids[i] === id) { return true } } return false }; ChartInternal.prototype.isTargetToShow = function(targetId) { return this.hiddenTargetIds.indexOf(targetId) < 0 }; ChartInternal.prototype.isLegendToShow = function(targetId) { return this.hiddenLegendIds.indexOf(targetId) < 0 }; /** * Returns only visible targets. * * This is the same as calling {@link filterTargetsToShow} on $$.data.targets. * * @return {Array} */ ChartInternal.prototype.getTargetsToShow = function() { const $$ = this; return $$.filterTargetsToShow($$.data.targets) }; ChartInternal.prototype.filterTargetsToShow = function(targets) { var $$ = this; return targets.filter(function(t) { return $$.isTargetToShow(t.id) }) }; /** * @return {Array} Returns all the targets attached to the chart, visible or not */ ChartInternal.prototype.getTargets = function() { const $$ = this; return $$.data.targets }; ChartInternal.prototype.mapTargetsToUniqueXs = function(targets) { var $$ = this; var xs = $$.d3 .set( $$.d3.merge( targets.map(function(t) { return t.values.map(function(v) { return +v.x }) }) ) ) .values(); xs = $$.isTimeSeries() ? xs.map(function(x) { return new Date(+x) }) : xs.map(function(x) { return +x }); return xs.sort(function(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN }) }; ChartInternal.prototype.addHiddenTargetIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]); } } this.resetCache(); }; ChartInternal.prototype.removeHiddenTargetIds = function(targetIds) { this.hiddenTargetIds = this.hiddenTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); this.resetCache(); }; ChartInternal.prototype.addHiddenLegendIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]); } } }; ChartInternal.prototype.removeHiddenLegendIds = function(targetIds) { this.hiddenLegendIds = this.hiddenLegendIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); }; ChartInternal.prototype.getValuesAsIdKeyed = function(targets) { var ys = {}; targets.forEach(function(t) { ys[t.id] = []; t.values.forEach(function(v) { ys[t.id].push(v.value); }); }); return ys }; ChartInternal.prototype.checkValueInTargets = function(targets, checker) { var ids = Object.keys(targets), i, j, values; for (i = 0; i < ids.length; i++) { values = targets[ids[i]].values; for (j = 0; j < values.length; j++) { if (checker(values[j].value)) { return true } } } return false }; ChartInternal.prototype.hasNegativeValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v < 0 }) }; ChartInternal.prototype.hasPositiveValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v > 0 }) }; ChartInternal.prototype.isOrderDesc = function() { var config = this.config; return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc' ) }; ChartInternal.prototype.isOrderAsc = function() { var config = this.config; return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc' ) }; ChartInternal.prototype.getOrderFunction = function() { var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc(); if (orderAsc || orderDesc) { var reducer = function(p, c) { return p + Math.abs(c.value) }; return function(t1, t2) { var t1Sum = t1.values.reduce(reducer, 0), t2Sum = t2.values.reduce(reducer, 0); return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum } } else if (isFunction(config.data_order)) { return config.data_order } else if (isArray(config.data_order)) { var order = config.data_order; return function(t1, t2) { return order.indexOf(t1.id) - order.indexOf(t2.id) } } }; ChartInternal.prototype.orderTargets = function(targets) { var fct = this.getOrderFunction(); if (fct) { targets.sort(fct); } return targets }; /** * Returns all the values from the given targets at the given index. * * @param {Array} targets * @param {Number} index * @return {Array} */ ChartInternal.prototype.filterByIndex = function(targets, index) { return this.d3.merge( targets.map(t => t.values.filter(v => v.index === index)) ) }; ChartInternal.prototype.filterByX = function(targets, x) { return this.d3 .merge( targets.map(function(t) { return t.values }) ) .filter(function(v) { return v.x - x === 0 }) }; ChartInternal.prototype.filterRemoveNull = function(data) { return data.filter(function(d) { return isValue(d.value) }) }; ChartInternal.prototype.filterByXDomain = function(targets, xDomain) { return targets.map(function(t) { return { id: t.id, id_org: t.id_org, values: t.values.filter(function(v) { return xDomain[0] <= v.x && v.x <= xDomain[1] }) } }) }; ChartInternal.prototype.hasDataLabel = function() { var config = this.config; if (typeof config.data_labels === 'boolean' && config.data_labels) { return true } else if ( typeof config.data_labels === 'object' && notEmpty(config.data_labels) ) { return true } return false }; ChartInternal.prototype.getDataLabelLength = function(min, max, key) { var $$ = this, lengths = [0, 0], paddingCoef = 1.3; $$.selectChart .select('svg') .selectAll('.dummy') .data([min, max]) .enter() .append('text') .text(function(d) { return $$.dataLabelFormat(d.id)(d) }) .each(function(d, i) { lengths[i] = getBBox(this)[key] * paddingCoef; }) .remove(); return lengths }; /** * Returns true if the given data point is not arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isNoneArc = function(d) { return this.hasTarget(this.data.targets, d.id) }; /** * Returns true if the given data point is arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isArc = function(d) { return 'data' in d && this.hasTarget(this.data.targets, d.data.id) }; /** * Find the closest point from the given pos among the given targets or * undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} pos An [x,y] coordinate * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargets = function(targets, pos) { const $$ = this; // for each target, find the closest point const candidates = targets .map(t => $$.findClosest( t.values, pos, $$.config.tooltip_horizontal ? $$.horizontalDistance.bind($$) : $$.dist.bind($$), $$.config.point_sensitivity ) ) .filter(v => v); // returns the closest of candidates if (candidates.length === 0) { return undefined } else if (candidates.length === 1) { return candidates[0] } else { return $$.findClosest(candidates, pos, $$.dist.bind($$)) } }; /** * Find the closest point from the x value or undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} x A value on X axis * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargetsByX = function(targets, x) { let closest; let diff; targets.forEach(t => { t.values.forEach(d => { let newDiff = Math.abs(x - d.x); if (diff === undefined || newDiff < diff) { closest = d; diff = newDiff; } }); }); return closest }; /** * Using given compute distance method, returns the closest data point from the * given position. * * Giving optionally a minimum distance to satisfy. * * @param {Array} dataPoints List of DataPoints * @param {Array} pos An [x,y] coordinate * @param {Function} computeDist Function to compute distance between 2 points * @param {Number} minDist Minimal distance to satisfy * @return {Object|undefined} Closest data point */ ChartInternal.prototype.findClosest = function( dataPoints, pos, computeDist, minDist = Infinity ) { const $$ = this; let closest; // find closest bar dataPoints .filter(v => v && $$.isBarType(v.id)) .forEach(function(v) { if (!closest) { const shape = $$.main .select( '.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index ) .node(); if ($$.isWithinBar(pos, shape)) { closest = v; } } }); // find closest point from non-bar dataPoints .filter(v => v && !$$.isBarType(v.id)) .forEach(v => { let d = computeDist(v, pos); if (d < minDist) { minDist = d; closest = v; } }); return closest }; ChartInternal.prototype.dist = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, yIndex = config.axis_rotated ? 0 : 1, y = $$.circleY(data, data.index), x = $$.x(data.x); return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)) }; ChartInternal.prototype.horizontalDistance = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, x = $$.x(data.x); return Math.abs(x - pos[xIndex]) }; ChartInternal.prototype.convertValuesToStep = function(values) { var converted = [].concat(values), i; if (!this.isCategorized()) { return values } for (i = values.length + 1; 0 < i; i--) { converted[i] = converted[i - 1]; } converted[0] = { x: converted[0].x - 1, value: converted[0].value, id: converted[0].id }; converted[values.length + 1] = { x: converted[values.length].x + 1, value: converted[values.length].value, id: converted[values.length].id }; return converted }; /** * Get ratio value * * @param {String} type Ratio for given type * @param {Object} d Data value object * @param {Boolean} asPercent Convert the return as percent or not * @return {Number} Ratio value * @private */ ChartInternal.prototype.getRatio = function(type, d, asPercent = false) { const $$ = this; const api = $$.api; let ratio = 0; if (d && api.data.shown.call(api).length) { ratio = d.ratio || d.value; if (type === 'arc') { if ($$.hasType('gauge')) { ratio = (d.endAngle - d.startAngle) / (Math.PI * ($$.config.gauge_fullCircle ? 2 : 1)); } else { const total = $$.getTotalDataSum(); ratio = d.value / total; } } else if (type === 'index') { const total = $$.getTotalPerIndex($$.axis.getId(d.id)); d.ratio = isNumber(d.value) && total && total[d.index] > 0 ? d.value / total[d.index] : 0; ratio = d.ratio; } } return asPercent && ratio ? ratio * 100 : ratio }; ChartInternal.prototype.updateDataAttributes = function(name, attrs) { var $$ = this, config = $$.config, current = config['data_' + name]; if (typeof attrs === 'undefined') { return current } Object.keys(attrs).forEach(function(id) { current[id] = attrs[id]; }); $$.redraw({ withLegend: true }); return current }; ChartInternal.prototype.load = function(targets, args) { var $$ = this; if (targets) { // filter loading targets if needed if (args.filter) { targets = targets.filter(args.filter); } // set type if args.types || args.type specified if (args.type || args.types) { targets.forEach(function(t) { var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; $$.setTargetType(t.id, type); }); } // Update/Add data $$.data.targets.forEach(function(d) { for (var i = 0; i < targets.length; i++) { if (d.id === targets[i].id) { d.values = targets[i].values; targets.splice(i, 1); break } } }); $$.data.targets = $$.data.targets.concat(targets); // add remained } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }; ChartInternal.prototype.loadFromArgs = function(args) { var $$ = this; $$.resetCache(); if (args.data) { $$.load($$.convertDataToTargets(args.data), args); } else if (args.url) { $$.convertUrlToData( args.url, args.mimeType, args.headers, args.keys, function(data) { $$.load($$.convertDataToTargets(data), args); } ); } else if (args.json) { $$.load( $$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args ); } else if (args.rows) { $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); } else if (args.columns) { $$.load( $$.convertDataToTargets($$.convertColumnsToData(args.columns)), args ); } else { $$.load(null, args); } }; ChartInternal.prototype.unload = function(targetIds, done) { var $$ = this; $$.resetCache(); if (!done) { done = function() {}; } // filter existing target targetIds = targetIds.filter(function(id) { return $$.hasTarget($$.data.targets, id) }); // If no target, call done and return if (!targetIds || targetIds.length === 0) { done(); return } $$.svg .selectAll( targetIds.map(function(id) { return $$.selectorTarget(id) }) ) .transition() .style('opacity', 0) .remove() .call($$.endall, done); targetIds.forEach(function(id) { // Reset fadein for future load $$.withoutFadeIn[id] = false; // Remove target's elements if ($$.legend) { $$.legend .selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)) .remove(); } // Remove target $$.data.targets = $$.data.targets.filter(function(t) { return t.id !== id }); }); }; ChartInternal.prototype.getYDomainMin = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasNegativeValue; if (config.data_groups.length > 0) { hasNegativeValue = $$.hasNegativeValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }); if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0]; // Consider negative values if (hasNegativeValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v < 0 ? v : 0; }); } // Compute min for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0) ) { ys[baseId][i] += +v; } }); } } } return $$.d3.min( Object.keys(ys).map(function(key) { return $$.d3.min(ys[key]) }) ) }; ChartInternal.prototype.getYDomainMax = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasPositiveValue; if (config.data_groups.length > 0) { hasPositiveValue = $$.hasPositiveValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }); if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0]; // Consider positive values if (hasPositiveValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v > 0 ? v : 0; }); } // Compute max for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0) ) { ys[baseId][i] += +v; } }); } } } return $$.d3.max( Object.keys(ys).map(function(key) { return $$.d3.max(ys[key]) }) ) }; ChartInternal.prototype.getYDomain = function(targets, axisId, xDomain) { var $$ = this, config = $$.config; if ($$.isAxisNormalized(axisId)) { return [0, 100] } var targetsByAxisId = targets.filter(function(t) { return $$.axis.getId(t.id) === axisId }), yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yDomainMin = $$.getYDomainMin(yTargets), yDomainMax = $$.getYDomainMax(yTargets), domain, domainLength, padding_top, padding_bottom, center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; // MEMO: avoid inverting domain unexpectedly yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin; yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax; if (yTargets.length === 0) { // use current domain if target of axisId is none return axisId === 'y2' ? $$.y2.domain() : $$.y.domain() } if (isNaN(yDomainMin)) { // set minimum to zero when not number yDomainMin = 0; } if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin yDomainMax = yDomainMin; } if (yDomainMin === yDomainMax) { yDomainMin < 0 ? (yDomainMax = 0) : (yDomainMin = 0); } isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; // Cancel zerobased if axis_*_min / axis_*_max specified if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { isZeroBased = false; } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { yDomainMin = 0; } if (isAllNegative) { yDomainMax = 0; } } domainLength = Math.abs(yDomainMax - yDomainMin); padding_top = padding_bottom = domainLength * 0.1; if (typeof center !== 'undefined') { yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); yDomainMax = center + yDomainAbs; yDomainMin = center - yDomainAbs; } // add padding for data label if (showHorizontalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); diff = diffDomain($$.y.range()); ratio = [lengths[0] / diff, lengths[1] / diff]; padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); } else if (showVerticalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); const pixelsToAxisPadding = $$.getY( config[`axis_${axisId}_type`], // input domain as pixels [0, config.axis_rotated ? $$.width : $$.height], // output range as axis padding [0, domainLength] ); padding_top += pixelsToAxisPadding(lengths[1]); padding_bottom += pixelsToAxisPadding(lengths[0]); } if (axisId === 'y' && notEmpty(config.axis_y_padding)) { padding_top = $$.axis.getPadding( config.axis_y_padding, 'top', padding_top, domainLength ); padding_bottom = $$.axis.getPadding( config.axis_y_padding, 'bottom', padding_bottom, domainLength ); } if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { padding_top = $$.axis.getPadding( config.axis_y2_padding, 'top', padding_top, domainLength ); padding_bottom = $$.axis.getPadding( config.axis_y2_padding, 'bottom', padding_bottom, domainLength ); } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { padding_bottom = yDomainMin; } if (isAllNegative) { padding_top = -yDomainMax; } } domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; return isInverted ? domain.reverse() : domain }; ChartInternal.prototype.getXDomainMin = function(targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function(t) { return $$.d3.min(t.values, function(v) { return v.x }) }) }; ChartInternal.prototype.getXDomainMax = function(targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function(t) { return $$.d3.max(t.values, function(v) { return v.x }) }) }; ChartInternal.prototype.getXDomainPadding = function(domain) { var $$ = this, config = $$.config, diff = domain[1] - domain[0], maxDataCount, padding, paddingLeft, paddingRight; if ($$.isCategorized()) { padding = 0; } else if ($$.hasType('bar')) { maxDataCount = $$.getMaxDataCount(); padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5; } else { padding = diff * 0.01; } if ( typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding) ) { paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; } else if (typeof config.axis_x_padding === 'number') { paddingLeft = paddingRight = config.axis_x_padding; } else { paddingLeft = paddingRight = padding; } return { left: paddingLeft, right: paddingRight } }; ChartInternal.prototype.getXDomain = function(targets) { var $$ = this, xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], firstX = xDomain[0], lastX = xDomain[1], padding = $$.getXDomainPadding(xDomain), min = 0, max = 0; // show center of x domain if min and max are the same if (firstX - lastX === 0 && !$$.isCategorized()) { if ($$.isTimeSeries()) { firstX = new Date(firstX.getTime() * 0.5); lastX = new Date(lastX.getTime() * 1.5); } else { firstX = firstX === 0 ? 1 : firstX * 0.5; lastX = lastX === 0 ? -1 : lastX * 1.5; } } if (firstX || firstX === 0) { min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; } if (lastX || lastX === 0) { max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; } return [min, max] }; ChartInternal.prototype.updateXDomain = function( targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain ) { var $$ = this, config = $$.config; if (withUpdateOrgXDomain) { $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); $$.orgXDomain = $$.x.domain(); if (config.zoom_enabled) { $$.zoom.update(); } $$.subX.domain($$.x.domain()); if ($$.brush) { $$.brush.updateScale($$.subX); } } if (withUpdateXDomain) { $$.x.domain( domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue() ); } // Trim domain when too big by zoom mousemove event if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); } return $$.x.domain() }; ChartInternal.prototype.trimXDomain = function(domain) { var zoomDomain = this.getZoomDomain(), min = zoomDomain[0], max = zoomDomain[1]; if (domain[0] <= min) { domain[1] = +domain[1] + (min - domain[0]); domain[0] = min; } if (max <= domain[1]) { domain[0] = +domain[0] - (domain[1] - max); domain[1] = max; } return domain }; ChartInternal.prototype.drag = function(mouse) { var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3; var sx, sy, mx, my, minX, maxX, minY, maxY; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable if (!config.data_selection_multiple) { return } // skip when single selection because drag is used for multiple selection sx = $$.dragStart[0]; sy = $$.dragStart[1]; mx = mouse[0]; my = mouse[1]; minX = Math.min(sx, mx); maxX = Math.max(sx, mx); minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my); maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my); main .select('.' + CLASS.dragarea) .attr('x', minX) .attr('y', minY) .attr('width', maxX - minX) .attr('height', maxY - minY); // TODO: binary search when multiple xs main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { if (!config.data_selection_isselectable(d)) { return } var shape = d3.select(this), isSelected = shape.classed(CLASS.SELECTED), isIncluded = shape.classed(CLASS.INCLUDED), _x, _y, _w, _h, toggle, isWithin = false, box; if (shape.classed(CLASS.circle)) { _x = shape.attr('cx') * 1; _y = shape.attr('cy') * 1; toggle = $$.togglePoint; isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; } else if (shape.classed(CLASS.bar)) { box = getPathBox(this); _x = box.x; _y = box.y; _w = box.width; _h = box.height; toggle = $$.togglePath; isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); } else { // line/area selection not supported yet return } if (isWithin ^ isIncluded) { shape.classed(CLASS.INCLUDED, !isIncluded); // TODO: included/unincluded callback here shape.classed(CLASS.SELECTED, !isSelected); toggle.call($$, !isSelected, shape, d, i); } }); }; ChartInternal.prototype.dragstart = function(mouse) { var $$ = this, config = $$.config; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.dragStart = mouse; $$.main .select('.' + CLASS.chart) .append('rect') .attr('class', CLASS.dragarea) .style('opacity', 0.1); $$.dragging = true; }; ChartInternal.prototype.dragend = function() { var $$ = this, config = $$.config; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.main .select('.' + CLASS.dragarea) .transition() .duration(100) .style('opacity', 0) .remove(); $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false); $$.dragging = false; }; ChartInternal.prototype.getYFormat = function(forArc) { var $$ = this, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; return function(v, ratio, id) { var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; return format.call($$, v, ratio) } }; ChartInternal.prototype.yFormat = function(v) { var $$ = this, config = $$.config, format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; return format(v) }; ChartInternal.prototype.y2Format = function(v) { var $$ = this, config = $$.config, format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; return format(v) }; ChartInternal.prototype.defaultValueFormat = function(v) { return isValue(v) ? +v : '' }; ChartInternal.prototype.defaultArcValueFormat = function(v, ratio) { return (ratio * 100).toFixed(1) + '%' }; ChartInternal.prototype.dataLabelFormat = function(targetId) { var $$ = this, data_labels = $$.config.data_labels, format, defaultFormat = function(v) { return isValue(v) ? +v : '' }; // find format according to axis id if (typeof data_labels.format === 'function') { format = data_labels.format; } else if (typeof data_labels.format === 'object') { if (data_labels.format[targetId]) { format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; } else { format = function() { return '' }; } } else { format = defaultFormat; } return format }; ChartInternal.prototype.initGrid = function() { var $$ = this, config = $$.config, d3 = $$.d3; $$.grid = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid); if (config.grid_x_show) { $$.grid.append('g').attr('class', CLASS.xgrids); } if (config.grid_y_show) { $$.grid.append('g').attr('class', CLASS.ygrids); } if (config.grid_focus_show) { $$.grid .append('g') .attr('class', CLASS.xgridFocus) .append('line') .attr('class', CLASS.xgridFocus); } $$.xgrid = d3.selectAll([]); if (!config.grid_lines_front) { $$.initGridLines(); } }; ChartInternal.prototype.initGridLines = function() { var $$ = this, d3 = $$.d3; $$.gridLines = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid + ' ' + CLASS.gridLines); $$.gridLines.append('g').attr('class', CLASS.xgridLines); $$.gridLines.append('g').attr('class', CLASS.ygridLines); $$.xgridLines = d3.selectAll([]); }; ChartInternal.prototype.updateXGrid = function(withoutUpdate) { var $$ = this, config = $$.config, d3 = $$.d3, xgridData = $$.generateGridData(config.grid_x_type, $$.x), tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; $$.xgridAttr = config.axis_rotated ? { x1: 0, x2: $$.width, y1: function(d) { return $$.x(d) - tickOffset }, y2: function(d) { return $$.x(d) - tickOffset } } : { x1: function(d) { return $$.x(d) + tickOffset }, x2: function(d) { return $$.x(d) + tickOffset }, y1: 0, y2: $$.height }; $$.xgridAttr.opacity = function() { var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1'); return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1 }; var xgrid = $$.main .select('.' + CLASS.xgrids) .selectAll('.' + CLASS.xgrid) .data(xgridData); var xgridEnter = xgrid .enter() .append('line') .attr('class', CLASS.xgrid) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', 0); $$.xgrid = xgridEnter.merge(xgrid); if (!withoutUpdate) { $$.xgrid .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); } xgrid.exit().remove(); }; ChartInternal.prototype.updateYGrid = function() { var $$ = this, config = $$.config, gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); var ygrid = $$.main .select('.' + CLASS.ygrids) .selectAll('.' + CLASS.ygrid) .data(gridValues); var ygridEnter = ygrid .enter() .append('line') // TODO: x1, x2, y1, y2, opacity need to be set here maybe .attr('class', CLASS.ygrid); $$.ygrid = ygridEnter.merge(ygrid); $$.ygrid .attr('x1', config.axis_rotated ? $$.y : 0) .attr('x2', config.axis_rotated ? $$.y : $$.width) .attr('y1', config.axis_rotated ? 0 : $$.y) .attr('y2', config.axis_rotated ? $$.height : $$.y); ygrid.exit().remove(); $$.smoothLines($$.ygrid, 'grid'); }; ChartInternal.prototype.gridTextAnchor = function(d) { return d.position ? d.position : 'end' }; ChartInternal.prototype.gridTextDx = function(d) { return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4 }; ChartInternal.prototype.xGridTextX = function(d) { return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0 }; ChartInternal.prototype.yGridTextX = function(d) { return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width }; ChartInternal.prototype.updateGrid = function(duration) { var $$ = this, main = $$.main, config = $$.config, xgridLine, xgridLineEnter, ygridLine, ygridLineEnter, xv = $$.xv.bind($$), yv = $$.yv.bind($$), xGridTextX = $$.xGridTextX.bind($$), yGridTextX = $$.yGridTextX.bind($$); // hide if arc type $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); if (config.grid_x_show) { $$.updateXGrid(); } xgridLine = main .select('.' + CLASS.xgridLines) .selectAll('.' + CLASS.xgridLine) .data(config.grid_x_lines); // enter xgridLineEnter = xgridLine .enter() .append('g') .attr('class', function(d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '') }); xgridLineEnter .append('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 0); xgridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .attr('x', config.axis_rotated ? yGridTextX : xGridTextX) .attr('y', xv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // udpate $$.xgridLines = xgridLineEnter.merge(xgridLine); // done in d3.transition() of the end of this function // exit xgridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Y-Grid if (config.grid_y_show) { $$.updateYGrid(); } ygridLine = main .select('.' + CLASS.ygridLines) .selectAll('.' + CLASS.ygridLine) .data(config.grid_y_lines); // enter ygridLineEnter = ygridLine .enter() .append('g') .attr('class', function(d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '') }); ygridLineEnter .append('line') .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 0); ygridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .attr('x', config.axis_rotated ? xGridTextX : yGridTextX) .attr('y', yv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // update $$.ygridLines = ygridLineEnter.merge(ygridLine); $$.ygridLines .select('line') .transition() .duration(duration) .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 1); $$.ygridLines .select('text') .transition() .duration(duration) .attr( 'x', config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$) ) .attr('y', yv) .text(function(d) { return d.text }) .style('opacity', 1); // exit ygridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawGrid = function(withTransition, transition) { var $$ = this, config = $$.config, xv = $$.xv.bind($$), lines = $$.xgridLines.select('line'), texts = $$.xgridLines.select('text'); return [ (withTransition ? lines.transition(transition) : lines) .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 1), (withTransition ? texts.transition(transition) : texts) .attr( 'x', config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$) ) .attr('y', xv) .text(function(d) { return d.text }) .style('opacity', 1) ] }; ChartInternal.prototype.showXGridFocus = function(selectedData) { var $$ = this, config = $$.config, dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), xx = $$.xx.bind($$); if (!config.tooltip_show) { return } // Hide when stanford plot exists if ($$.hasType('stanford') || $$.hasArcType()) { return } focusEl .style('visibility', 'visible') .data([dataToShow[0]]) .attr(config.axis_rotated ? 'y1' : 'x1', xx) .attr(config.axis_rotated ? 'y2' : 'x2', xx); $$.smoothLines(focusEl, 'grid'); }; ChartInternal.prototype.hideXGridFocus = function() { this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); }; ChartInternal.prototype.updateXgridFocus = function() { var $$ = this, config = $$.config; $$.main .select('line.' + CLASS.xgridFocus) .attr('x1', config.axis_rotated ? 0 : -10) .attr('x2', config.axis_rotated ? $$.width : -10) .attr('y1', config.axis_rotated ? -10 : 0) .attr('y2', config.axis_rotated ? -10 : $$.height); }; ChartInternal.prototype.generateGridData = function(type, scale) { var $$ = this, gridData = [], xDomain, firstYear, lastYear, i, tickNum = $$.main .select('.' + CLASS.axisX) .selectAll('.tick') .size(); if (type === 'year') { xDomain = $$.getXDomain(); firstYear = xDomain[0].getFullYear(); lastYear = xDomain[1].getFullYear(); for (i = firstYear; i <= lastYear; i++) { gridData.push(new Date(i + '-01-01 00:00:00')); } } else { gridData = scale.ticks(10); if (gridData.length > tickNum) { // use only int gridData = gridData.filter(function(d) { return ('' + d).indexOf('.') < 0 }); } } return gridData }; ChartInternal.prototype.getGridFilterToRemove = function(params) { return params ? function(line) { var found = false ;[].concat(params).forEach(function(param) { if ( ('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class']) ) { found = true; } }); return found } : function() { return true } }; ChartInternal.prototype.removeGridLines = function(params, forX) { var $$ = this, config = $$.config, toRemove = $$.getGridFilterToRemove(params), toShow = function(line) { return !toRemove(line) }, classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; $$.main .select('.' + classLines) .selectAll('.' + classLine) .filter(toRemove) .transition() .duration(config.transition_duration) .style('opacity', 0) .remove(); if (forX) { config.grid_x_lines = config.grid_x_lines.filter(toShow); } else { config.grid_y_lines = config.grid_y_lines.filter(toShow); } }; ChartInternal.prototype.initEventRect = function() { var $$ = this, config = $$.config; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0); $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect); // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null); if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)); } } }; ChartInternal.prototype.redrawEventRect = function() { const $$ = this, d3 = $$.d3, config = $$.config; function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null); $$.hideXGridFocus(); $$.hideTooltip(); $$.unexpandCircles(); $$.unexpandBars(); } const isHoveringDataPoint = (mouse, closest) => closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity); const withName = d => (d ? $$.addName(Object.assign({}, d)) : null); // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style( 'cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null ); $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on( 'mouseout', config.interaction_enabled ? function() { if (!config) { return } // chart is destroyed if ($$.hasArcType()) { return } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } mouseout(); } : null ) .on( 'mousemove', config.interaction_enabled ? function() { // do nothing when dragging if ($$.dragging) { return } const targetsToShow = $$.getTargetsToShow(); // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this); const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ); const isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest); // ensure onmouseout is always called if mousemove switch between 2 targets if ( $$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index) ) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest); $$.mouseover = closest; } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null); // if tooltip not grouped, we want to display only data from closest data point const showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow); // find data to highlight let selectedData; if (showSingleDataPoint) { if (closest) { selectedData = [closest]; } } else { let closestByX; if (closest) { // reuse closest value closestByX = closest; } else { // try to find the closest value by X values from the mouse position const mouseX = config.axis_rotated ? mouse[1] : mouse[0]; closestByX = $$.findClosestFromTargetsByX( targetsToShow, $$.x.invert(mouseX) ); } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x); } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout() } // inject names for each point selectedData = selectedData.map(withName); // show tooltip $$.showTooltip(selectedData, this); // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles(); selectedData.forEach(function(d) { $$.expandCircles(d.index, d.id, false); }); } // expand bars $$.unexpandBars(); selectedData.forEach(function(d) { $$.expandBars(d.index, d.id, false); }); // Show xgrid focus line $$.showXGridFocus(selectedData); } : null ) .on( 'click', config.interaction_enabled ? function() { const targetsToShow = $$.getTargetsToShow(); if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this); const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ); if (!isHoveringDataPoint(mouse, closest)) { return } // select if selection enabled let sameXData; if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest]; } else { sameXData = $$.filterByX(targetsToShow, closest.x); } // toggle selected state sameXData.forEach(function(d) { $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id) ) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function() { if ( config.data_selection_grouped || $$.isWithinShape(this, d) ) { $$.toggleShape(this, d, d.index); } }); }); // call data_onclick on the closest data point if (closest) { const shape = $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id) ) .select('.' + CLASS.shape + '-' + closest.index); config.data_onclick.call($$.api, closest, shape.node()); } } : null ) .call( config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function() { $$.drag(d3.mouse(this)); }) .on('start', function() { $$.dragstart(d3.mouse(this)); }) .on('end', function() { $$.dragend(); }) : function() {} ); }; ChartInternal.prototype.getMousePosition = function(data) { var $$ = this; return [$$.x(data.x), $$.getYScale(data.id)(data.value)] }; ChartInternal.prototype.dispatchEvent = function(type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents'); event.initMouseEvent( type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null ); eventRect.dispatchEvent(event); }; ChartInternal.prototype.initLegend = function() { var $$ = this; $$.legendItemTextBox = {}; $$.legendHasRendered = false; $$.legend = $$.svg.append('g').attr('transform', $$.getTranslate('legend')); if (!$$.config.legend_show) { $$.legend.style('visibility', 'hidden'); $$.hiddenLegendIds = $$.mapToIds($$.data.targets); return } // MEMO: call here to update legend box and tranlate for all // MEMO: translate will be updated by this, so transform not needed in updateLegend() $$.updateLegendWithDefaults(); }; ChartInternal.prototype.updateLegendWithDefaults = function() { var $$ = this; $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }); }; ChartInternal.prototype.updateSizeForLegend = function( legendHeight, legendWidth ) { var $$ = this, config = $$.config, insetLegendPosition = { top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 }; $$.margin3 = { top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, right: NaN, bottom: 0, left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 }; }; ChartInternal.prototype.transformLegend = function(withTransition) { var $$ = this ;(withTransition ? $$.legend.transition() : $$.legend).attr( 'transform', $$.getTranslate('legend') ); }; ChartInternal.prototype.updateLegendStep = function(step) { this.legendStep = step; }; ChartInternal.prototype.updateLegendItemWidth = function(w) { this.legendItemWidth = w; }; ChartInternal.prototype.updateLegendItemHeight = function(h) { this.legendItemHeight = h; }; ChartInternal.prototype.getLegendWidth = function() { var $$ = this; return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0 }; ChartInternal.prototype.getLegendHeight = function() { var $$ = this, h = 0; if ($$.config.legend_show) { if ($$.isLegendRight) { h = $$.currentHeight; } else { h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); } } return h }; ChartInternal.prototype.opacityForLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 1 }; ChartInternal.prototype.opacityForUnfocusedLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3 }; ChartInternal.prototype.toggleFocusLegend = function(targetIds, focus) { var $$ = this; targetIds = $$.mapToTargetIds(targetIds); $$.legend .selectAll('.' + CLASS.legendItem) .filter(function(id) { return targetIds.indexOf(id) >= 0 }) .classed(CLASS.legendItemFocused, focus) .transition() .duration(100) .style('opacity', function() { var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; return opacity.call($$, $$.d3.select(this)) }); }; ChartInternal.prototype.revertLegend = function() { var $$ = this, d3 = $$.d3; $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemFocused, false) .transition() .duration(100) .style('opacity', function() { return $$.opacityForLegend(d3.select(this)) }); }; ChartInternal.prototype.showLegend = function(targetIds) { var $$ = this, config = $$.config; if (!config.legend_show) { config.legend_show = true; $$.legend.style('visibility', 'visible'); if (!$$.legendHasRendered) { $$.updateLegendWithDefaults(); } } $$.removeHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('visibility', 'visible') .transition() .style('opacity', function() { return $$.opacityForLegend($$.d3.select(this)) }); }; ChartInternal.prototype.hideLegend = function(targetIds) { var $$ = this, config = $$.config; if (config.legend_show && isEmpty(targetIds)) { config.legend_show = false; $$.legend.style('visibility', 'hidden'); } $$.addHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('opacity', 0) .style('visibility', 'hidden'); }; ChartInternal.prototype.clearLegendItemTextBoxCache = function() { this.legendItemTextBox = {}; }; ChartInternal.prototype.updateLegend = function( targetIds, options, transitions ) { var $$ = this, config = $$.config; var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; // Skip elements when their name is set to null targetIds = targetIds.filter(function(id) { return !isDefined(config.data_names[id]) || config.data_names[id] !== null }); options = options || {}; withTransition = getOption(options, 'withTransition', true); withTransitionForTransform = getOption( options, 'withTransitionForTransform', true ); function getTextBox(textElement, id) { if (!$$.legendItemTextBox[id]) { $$.legendItemTextBox[id] = $$.getTextRect( textElement.textContent, CLASS.legendItem, textElement ); } return $$.legendItemTextBox[id] } function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), margin, maxLength; // MEMO: care about condifion of step, totalLength function updateValues(id, withoutStep) { if (!withoutStep) { margin = (areaLength - totalLength - itemLength) / 2; if (margin < posMin) { margin = (areaLength - itemLength) / 2; totalLength = 0; step++; } } steps[id] = step; margins[step] = $$.isLegendInset ? 10 : margin; offsets[id] = totalLength; totalLength += itemLength; } if (reset) { totalLength = 0; step = 0; maxWidth = 0; maxHeight = 0; } if (config.legend_show && !$$.isLegendToShow(id)) { widths[id] = heights[id] = steps[id] = offsets[id] = 0; return } widths[id] = itemWidth; heights[id] = itemHeight; if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; } if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; } maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; if (config.legend_equally) { Object.keys(widths).forEach(function(id) { widths[id] = maxWidth; }); Object.keys(heights).forEach(function(id) { heights[id] = maxHeight; }); margin = (areaLength - maxLength * targetIds.length) / 2; if (margin < posMin) { totalLength = 0; step = 0; targetIds.forEach(function(id) { updateValues(id); }); } else { updateValues(id, true); } } else { updateValues(id); } } if ($$.isLegendInset) { step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; $$.updateLegendStep(step); } if ($$.isLegendRight) { xForLegend = function(id) { return maxWidth * steps[id] }; yForLegend = function(id) { return margins[steps[id]] + offsets[id] }; } else if ($$.isLegendInset) { xForLegend = function(id) { return maxWidth * steps[id] + 10 }; yForLegend = function(id) { return margins[steps[id]] + offsets[id] }; } else { xForLegend = function(id) { return margins[steps[id]] + offsets[id] }; yForLegend = function(id) { return maxHeight * steps[id] }; } xForLegendText = function(id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width }; yForLegendText = function(id, i) { return yForLegend(id, i) + 9 }; xForLegendRect = function(id, i) { return xForLegend(id, i) }; yForLegendRect = function(id, i) { return yForLegend(id, i) - 5 }; x1ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 }; x2ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width }; yForLegendTile = function(id, i) { return yForLegend(id, i) + 4 }; // Define g for legend area l = $$.legend .selectAll('.' + CLASS.legendItem) .data(targetIds) .enter() .append('g') .attr('class', function(id) { return $$.generateClass(CLASS.legendItem, id) }) .style('visibility', function(id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden' }) .style('cursor', function() { return config.interaction_enabled ? 'pointer' : 'auto' }) .on( 'click', config.interaction_enabled ? function(id) { if (config.legend_item_onclick) { config.legend_item_onclick.call($$, id); } else { if ($$.d3.event.altKey) { $$.api.hide(); $$.api.show(id); } else { $$.api.toggle(id); $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); } } } : null ) .on( 'mouseover', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseover) { config.legend_item_onmouseover.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, true); if (!$$.transiting && $$.isTargetToShow(id)) { $$.api.focus(id); } } } : null ) .on( 'mouseout', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseout) { config.legend_item_onmouseout.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, false); $$.api.revert(); } } : null ); l.append('text') .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) .each(function(id, i) { updatePositions(this, id, i); }) .style('pointer-events', 'none') .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); l.append('rect') .attr('class', CLASS.legendItemEvent) .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); l.append('line') .attr('class', CLASS.legendItemTile) .style('stroke', $$.color) .style('pointer-events', 'none') .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { background = $$.legend .insert('g', '.' + CLASS.legendItem) .attr('class', CLASS.legendBackground) .append('rect'); } texts = $$.legend .selectAll('text') .data(targetIds) .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) // MEMO: needed for update .each(function(id, i) { updatePositions(this, id, i); }) ;(withTransition ? texts.transition() : texts) .attr('x', xForLegendText) .attr('y', yForLegendText); rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds) ;(withTransition ? rects.transition() : rects) .attr('width', function(id) { return widths[id] }) .attr('height', function(id) { return heights[id] }) .attr('x', xForLegendRect) .attr('y', yForLegendRect); tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds) ;(withTransition ? tiles.transition() : tiles) .style( 'stroke', $$.levelColor ? function(id) { return $$.levelColor( $$.cache[id].values.reduce(function(total, item) { return total + item.value }, 0) ) } : $$.color ) .attr('x1', x1ForLegendTile) .attr('y1', yForLegendTile) .attr('x2', x2ForLegendTile) .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background) .attr('height', $$.getLegendHeight() - 12) .attr('width', maxWidth * (step + 1) + 10); } // toggle legend state $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemHidden, function(id) { return !$$.isTargetToShow(id) }); // Update all to reflect change of legend $$.updateLegendItemWidth(maxWidth); $$.updateLegendItemHeight(maxHeight); $$.updateLegendStep(step); // Update size and scale $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(withTransitionForTransform, transitions); $$.legendHasRendered = true; }; ChartInternal.prototype.initRegion = function() { var $$ = this; $$.region = $$.main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.regions); }; ChartInternal.prototype.updateRegion = function(duration) { var $$ = this, config = $$.config; // hide if arc type $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); var mainRegion = $$.main .select('.' + CLASS.regions) .selectAll('.' + CLASS.region) .data(config.regions); var g = mainRegion.enter().append('g'); g.append('rect') .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }); g.append('text').text($$.labelRegion.bind($$)); $$.mainRegion = g.merge(mainRegion).attr('class', $$.classRegion.bind($$)); mainRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawRegion = function(withTransition, transition) { var $$ = this, regions = $$.mainRegion, regionLabels = $$.mainRegion.selectAll('text'); return [ (withTransition ? regions.transition(transition) : regions) .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }), (withTransition ? regionLabels.transition(transition) : regionLabels) .attr('x', $$.labelOffsetX.bind($$)) .attr('y', $$.labelOffsetY.bind($$)) .attr('transform', $$.labelTransform.bind($$)) .attr('style', 'text-anchor: left;') ] }; ChartInternal.prototype.regionX = function(d) { var $$ = this, config = $$.config, xPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0; } else { xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0; } return xPos }; ChartInternal.prototype.regionY = function(d) { var $$ = this, config = $$.config, yPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0; } else { yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0; } return yPos }; ChartInternal.prototype.regionWidth = function(d) { var $$ = this, config = $$.config, start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width; } else { end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width; } return end < start ? 0 : end - start }; ChartInternal.prototype.regionHeight = function(d) { var $$ = this, config = $$.config, start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height; } else { end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height; } return end < start ? 0 : end - start }; ChartInternal.prototype.isRegionOnX = function(d) { return !d.axis || d.axis === 'x' }; ChartInternal.prototype.labelRegion = function(d) { return 'label' in d ? d.label : '' }; ChartInternal.prototype.labelTransform = function(d) { return 'vertical' in d && d.vertical ? 'rotate(90)' : '' }; ChartInternal.prototype.labelOffsetX = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? this.regionY(d) + paddingY : this.regionX(d) + paddingX }; ChartInternal.prototype.labelOffsetY = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? -(this.regionX(d) + paddingX) : this.regionY(d) + 10 + paddingY }; function c3LogScale(d3, linearScale, logScale) { var PROJECTION = [0.01, 10]; if (!linearScale) { linearScale = d3.scaleLinear(); linearScale.range(PROJECTION); } if (!logScale) { logScale = d3.scaleLog(); logScale.domain(PROJECTION); logScale.nice(); } // copied from https://github.com/compute-io/logspace function logspace(a, b, len) { var arr, end, tmp, d; if (arguments.length < 3) { len = 10; } else { if (len === 0) { return [] } } // Calculate the increment: end = len - 1; d = (b - a) / end; // Build the output array... arr = new Array(len); tmp = a; arr[0] = Math.pow(10, tmp); for (var i = 1; i < end; i++) { tmp += d; arr[i] = Math.pow(10, tmp); } arr[end] = Math.pow(10, b); return arr } function scale(x) { return logScale(linearScale(x)) } scale.domain = function(x) { if (!arguments.length) { return linearScale.domain() } linearScale.domain(x); return scale }; scale.range = function(x) { if (!arguments.length) { return logScale.range() } logScale.range(x); return scale }; scale.ticks = function(m) { return logspace(-2, 1, m || 10).map(function(v) { return linearScale.invert(v) }) }; scale.copy = function() { return c3LogScale(d3, linearScale.copy(), logScale.copy()) }; return scale } ChartInternal.prototype.getScale = function(min, max, forTimeseries) { return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([ min, max ]) }; ChartInternal.prototype.getX = function(min, max, domain, offset) { var $$ = this, scale = $$.getScale(min, max, $$.isTimeSeries()), _scale = domain ? scale.domain(domain) : scale, key; // Define customized scale if categorized axis if ($$.isCategorized()) { offset = offset || function() { return 0 }; scale = function(d, raw) { var v = _scale(d) + offset(d); return raw ? v : Math.ceil(v) }; } else { scale = function(d, raw) { var v = _scale(d); return raw ? v : Math.ceil(v) }; } // define functions for (key in _scale) { scale[key] = _scale[key]; } scale.orgDomain = function() { return _scale.domain() }; // define custom domain() for categorized axis if ($$.isCategorized()) { scale.domain = function(domain) { if (!arguments.length) { domain = this.orgDomain(); return [domain[0], domain[1] + 1] } _scale.domain(domain); return scale }; } return scale }; /** * Creates and configures a D3 scale instance for the given type. * * By defaults it returns a Linear scale. * * @param {String} type Type of d3-scale to create. Type can be 'linear', 'time', 'timeseries' or 'log'. * @param {Array} domain The scale domain such as [from, to] * @param {Array} range The scale's range such as [from, to] * * @return A d3-scale instance */ ChartInternal.prototype.getY = function(type, domain, range) { let scale; if (type === 'timeseries' || type === 'time') { scale = this.d3.scaleTime(); } else if (type === 'log') { scale = c3LogScale(this.d3); } else if (type === 'linear' || type === undefined) { scale = this.d3.scaleLinear(); } else { throw new Error(`Invalid Y axis type: "${type}"`) } if (domain) { scale.domain(domain); } if (range) { scale.range(range); } return scale }; ChartInternal.prototype.getYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.y2 : this.y }; ChartInternal.prototype.getSubYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY }; ChartInternal.prototype.updateScales = function() { var $$ = this, config = $$.config, forInit = !$$.x; // update edges $$.xMin = config.axis_rotated ? 1 : 0; $$.xMax = config.axis_rotated ? $$.height : $$.width; $$.yMin = config.axis_rotated ? 0 : $$.height; $$.yMax = config.axis_rotated ? $$.width : 1; $$.subXMin = $$.xMin; $$.subXMax = $$.xMax; $$.subYMin = config.axis_rotated ? 0 : $$.height2; $$.subYMax = config.axis_rotated ? $$.width2 : 1; // update scales $$.x = $$.getX( $$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function() { return $$.xAxis.tickOffset() } ); $$.y = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.y.domain(), [$$.yMin, $$.yMax] ); $$.y2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.y2.domain(), [$$.yMin, $$.yMax] ); $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function(d) { return d % 1 ? 0 : $$.subXAxis.tickOffset() }); $$.subY = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.subY.domain(), [$$.subYMin, $$.subYMax] ); $$.subY2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.subY2.domain(), [$$.subYMin, $$.subYMax] ); // update axes $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); $$.xAxisTickValues = $$.axis.getXAxisTickValues(); $$.yAxisTickValues = $$.axis.getYAxisTickValues(); $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); $$.xAxis = $$.axis.getXAxis( $$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ); $$.subXAxis = $$.axis.getXAxis( $$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ); $$.yAxis = $$.axis.getYAxis( 'y', $$.y, $$.yOrient, $$.yAxisTickValues, config.axis_y_tick_outer ); $$.y2Axis = $$.axis.getYAxis( 'y2', $$.y2, $$.y2Orient, $$.y2AxisTickValues, config.axis_y2_tick_outer ); // Set initialized scales to brush and zoom if (!forInit) { if ($$.brush) { $$.brush.updateScale($$.subX); } } // update for arc if ($$.updateArc) { $$.updateArc(); } }; ChartInternal.prototype.selectPoint = function(target, d, i) { var $$ = this, config = $$.config, cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), r = $$.pointSelectR.bind($$); config.data_onselected.call($$.api, d, target.node()); // add selected-circle on low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .data([d]) .enter() .append('circle') .attr('class', function() { return $$.generateClass(CLASS.selectedCircle, i) }) .attr('cx', cx) .attr('cy', cy) .attr('stroke', function() { return $$.color(d) }) .attr('r', function(d) { return $$.pointSelectR(d) * 1.4 }) .transition() .duration(100) .attr('r', r); }; ChartInternal.prototype.unselectPoint = function(target, d, i) { var $$ = this; $$.config.data_onunselected.call($$.api, d, target.node()); // remove selected-circle from low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .transition() .duration(100) .attr('r', 0) .remove(); }; ChartInternal.prototype.togglePoint = function(selected, target, d, i) { selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); }; ChartInternal.prototype.selectPath = function(target, d) { var $$ = this; $$.config.data_onselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.d3.rgb($$.color(d)).brighter(0.75) }); } }; ChartInternal.prototype.unselectPath = function(target, d) { var $$ = this; $$.config.data_onunselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.color(d) }); } }; ChartInternal.prototype.togglePath = function(selected, target, d, i) { selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); }; ChartInternal.prototype.getToggle = function(that, d) { var $$ = this, toggle; if (that.nodeName === 'circle') { if ($$.isStepType(d)) { // circle is hidden in step chart, so treat as within the click area toggle = function() {}; // TODO: how to select step chart? } else { toggle = $$.togglePoint; } } else if (that.nodeName === 'path') { toggle = $$.togglePath; } return toggle }; ChartInternal.prototype.toggleShape = function(that, d, i) { var $$ = this, d3 = $$.d3, config = $$.config, shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), toggle = $$.getToggle(that, d).bind($$); if (config.data_selection_enabled && config.data_selection_isselectable(d)) { if (!config.data_selection_multiple) { $$.main .selectAll( '.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : '') ) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this); if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } }); } shape.classed(CLASS.SELECTED, !isSelected); toggle(!isSelected, shape, d, i); } }; ChartInternal.prototype.initBar = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); }; ChartInternal.prototype.updateTargetsForBar = function(targets) { var $$ = this, config = $$.config, mainBars, mainBarEnter, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classFocus = $$.classFocus.bind($$); mainBars = $$.main .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) .attr('class', function(d) { return classChartBar(d) + classFocus(d) }); mainBarEnter = mainBars .enter() .append('g') .attr('class', classChartBar) .style('pointer-events', 'none'); // Bars for each data mainBarEnter .append('g') .attr('class', classBars) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }); }; ChartInternal.prototype.updateBar = function(durationForExit) { var $$ = this, barData = $$.barData.bind($$), classBar = $$.classBar.bind($$), initialOpacity = $$.initialOpacity.bind($$), color = function(d) { return $$.color(d.id) }; var mainBar = $$.main .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data(barData); var mainBarEnter = mainBar .enter() .append('path') .attr('class', classBar) .style('stroke', color) .style('fill', color); $$.mainBar = mainBarEnter.merge(mainBar).style('opacity', initialOpacity); mainBar .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawBar = function( drawBar, withTransition, transition ) { const $$ = this; return [ (withTransition ? this.mainBar.transition(transition) : this.mainBar) .attr('d', drawBar) .style('stroke', this.color) .style('fill', this.color) .style('opacity', d => ($$.isTargetToShow(d.id) ? 1 : 0)) ] }; ChartInternal.prototype.getBarW = function(axis, barTargetsNum) { var $$ = this, config = $$.config, w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0; return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w }; ChartInternal.prototype.getBars = function(i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')) }; ChartInternal.prototype.expandBars = function(i, id, reset) { var $$ = this; if (reset) { $$.unexpandBars(); } $$.getBars(i, id).classed(CLASS.EXPANDED, true); }; ChartInternal.prototype.unexpandBars = function(i) { var $$ = this; $$.getBars(i).classed(CLASS.EXPANDED, false); }; ChartInternal.prototype.generateDrawBar = function(barIndices, isSub) { var $$ = this, config = $$.config, getPoints = $$.generateGetBarPoints(barIndices, isSub); return function(d, i) { // 4 points that make a bar var points = getPoints(d, i); // switch points if axis is rotated, not applicable for sub chart var indexX = config.axis_rotated ? 1 : 0; var indexY = config.axis_rotated ? 0 : 1; var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z'; return path } }; ChartInternal.prototype.generateGetBarPoints = function(barIndices, isSub) { var $$ = this, axis = isSub ? $$.subXAxis : $$.xAxis, barTargetsNum = barIndices.__max__ + 1, barW = $$.getBarW(axis, barTargetsNum), barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), barY = $$.getShapeY(!!isSub), barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), barSpaceOffset = barW * ($$.config.bar_space / 2), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = barOffset(d, i) || y0, // offset is for stacked bar chart posX = barX(d), posY = barY(d); // fix posY not to overflow opposite quadrant if ($$.config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } posY -= y0 - offset; // 4 points that make a bar return [ [posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY], [posX + barW - barSpaceOffset, posY], [posX + barW - barSpaceOffset, offset] ] } }; /** * Returns whether the data point is within the given bar shape. * * @param mouse * @param barShape * @return {boolean} */ ChartInternal.prototype.isWithinBar = function(mouse, barShape) { return isWithinBox(mouse, getBBox(barShape), 2) }; ChartInternal.prototype.getShapeIndices = function(typeFilter) { var $$ = this, config = $$.config, indices = {}, i = 0, j, k; $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach( function(d) { for (j = 0; j < config.data_groups.length; j++) { if (config.data_groups[j].indexOf(d.id) < 0) { continue } for (k = 0; k < config.data_groups[j].length; k++) { if (config.data_groups[j][k] in indices) { indices[d.id] = indices[config.data_groups[j][k]]; break } } } if (isUndefined(indices[d.id])) { indices[d.id] = i++; } } ); indices.__max__ = i - 1; return indices }; ChartInternal.prototype.getShapeX = function( offset, targetsNum, indices, isSub ) { var $$ = this, scale = isSub ? $$.subX : $$.x; return function(d) { var index = d.id in indices ? indices[d.id] : 0; return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0 } }; ChartInternal.prototype.getShapeY = function(isSub) { const $$ = this; return function(d) { const scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); return scale( $$.isTargetNormalized(d.id) ? $$.getRatio('index', d, true) : d.value ) } }; ChartInternal.prototype.getShapeOffset = function(typeFilter, indices, isSub) { var $$ = this, targets = $$.orderTargets( $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)) ), targetIds = targets.map(function(t) { return t.id }); return function(d, i) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), y0 = scale(0), offset = y0; targets.forEach(function(t) { const rowValues = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; const isTargetNormalized = $$.isTargetNormalized(d.id); const values = rowValues.map(v => isTargetNormalized ? $$.getRatio('index', v, true) : v.value ); if (t.id === d.id || indices[t.id] !== indices[d.id]) { return } if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { // check if the x values line up if (isUndefined(rowValues[i]) || +rowValues[i].x !== +d.x) { // "+" for timeseries // if not, try to find the value that does line up i = -1; rowValues.forEach(function(v, j) { const x1 = v.x.constructor === Date ? +v.x : v.x; const x2 = d.x.constructor === Date ? +d.x : d.x; if (x1 === x2) { i = j; } }); } if (i in rowValues && rowValues[i].value * d.value >= 0) { offset += scale(values[i]) - y0; } } }); return offset } }; ChartInternal.prototype.isWithinShape = function(that, d) { var $$ = this, shape = $$.d3.select(that), isWithin; if (!$$.isTargetToShow(d.id)) { isWithin = false; } else if (that.nodeName === 'circle') { isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); } else if (that.nodeName === 'path') { isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true; } return isWithin }; ChartInternal.prototype.getInterpolate = function(d) { var $$ = this, d3 = $$.d3, types = { linear: d3.curveLinear, 'linear-closed': d3.curveLinearClosed, basis: d3.curveBasis, 'basis-open': d3.curveBasisOpen, 'basis-closed': d3.curveBasisClosed, bundle: d3.curveBundle, cardinal: d3.curveCardinal, 'cardinal-open': d3.curveCardinalOpen, 'cardinal-closed': d3.curveCardinalClosed, monotone: d3.curveMonotoneX, step: d3.curveStep, 'step-before': d3.curveStepBefore, 'step-after': d3.curveStepAfter }, type; if ($$.isSplineType(d)) { type = types[$$.config.spline_interpolation_type] || types.cardinal; } else if ($$.isStepType(d)) { type = types[$$.config.line_step_type]; } else { type = types.linear; } return type }; ChartInternal.prototype.initLine = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); }; ChartInternal.prototype.updateTargetsForLine = function(targets) { var $$ = this, config = $$.config, mainLines, mainLineEnter, classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$), classCircles = $$.classCircles.bind($$), classFocus = $$.classFocus.bind($$); mainLines = $$.main .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) .attr('class', function(d) { return classChartLine(d) + classFocus(d) }); mainLineEnter = mainLines .enter() .append('g') .attr('class', classChartLine) .style('opacity', 0) .style('pointer-events', 'none'); // Lines for each data mainLineEnter.append('g').attr('class', classLines); // Areas mainLineEnter.append('g').attr('class', classAreas); // Circles for each data point on lines mainLineEnter.append('g').attr('class', function(d) { return $$.generateClass(CLASS.selectedCircles, d.id) }); mainLineEnter .append('g') .attr('class', classCircles) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }); // Update date for selected circles targets.forEach(function(t) { $$.main .selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)) .selectAll('.' + CLASS.selectedCircle) .each(function(d) { d.value = t.values[d.index].value; }); }); // MEMO: can not keep same color... //mainLineUpdate.exit().remove(); }; ChartInternal.prototype.updateLine = function(durationForExit) { var $$ = this; var mainLine = $$.main .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var mainLineEnter = mainLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); $$.mainLine = mainLineEnter .merge(mainLine) .style('opacity', $$.initialOpacity.bind($$)) .style('shape-rendering', function(d) { return $$.isStepType(d) ? 'crispEdges' : '' }) .attr('transform', null); mainLine .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawLine = function( drawLine, withTransition, transition ) { return [ (withTransition ? this.mainLine.transition(transition) : this.mainLine) .attr('d', drawLine) .style('stroke', this.color) .style('opacity', 1) ] }; ChartInternal.prototype.generateDrawLine = function(lineIndices, isSub) { var $$ = this, config = $$.config, line = $$.d3.line(), getPoints = $$.generateGetLinePoints(lineIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, yValue = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value) }; line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); if (!config.line_connectNull) { line = line.defined(function(d) { return d.value != null }); } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x = isSub ? $$.subX : $$.x, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path; if ($$.isLineType(d)) { if (config.data_regions[d.id]) { path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); } else { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = line.curve($$.getInterpolate(d))(values); } } else { if (values[0]) { x0 = x(values[0].x); y0 = y(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0' } }; ChartInternal.prototype.generateGetLinePoints = function(lineIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, lineTargetsNum = lineIndices.__max__ + 1, x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), y = $$.getShapeY(!!isSub), lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = lineOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the line position return [ [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)] // needed for compatibility ] } }; ChartInternal.prototype.lineWithRegions = function(d, x, y, _regions) { var $$ = this, config = $$.config, prev = -1, i, j, s = 'M', sWithRegion, xp, yp, dx, dy, dd, diff, diffx2, xOffset = $$.isCategorized() ? 0.5 : 0, xValue, yValue, regions = []; function isWithinRegions(x, regions) { var i; for (i = 0; i < regions.length; i++) { if (regions[i].start < x && x <= regions[i].end) { return true } } return false } // Check start/end of regions if (isDefined(_regions)) { for (i = 0; i < _regions.length; i++) { regions[i] = {}; if (isUndefined(_regions[i].start)) { regions[i].start = d[0].x; } else { regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; } if (isUndefined(_regions[i].end)) { regions[i].end = d[d.length - 1].x; } else { regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; } } } // Set scales xValue = config.axis_rotated ? function(d) { return y(d.value) } : function(d) { return x(d.x) }; yValue = config.axis_rotated ? function(d) { return x(d.x) } : function(d) { return y(d.value) }; // Define svg generator function for region function generateM(points) { return ( 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1] ) } if ($$.isTimeSeries()) { sWithRegion = function(d0, d1, j, diff) { var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, xv0 = new Date(x0 + x_diff * j), xv1 = new Date(x0 + x_diff * (j + diff)), points; if (config.axis_rotated) { points = [ [y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)] ]; } else { points = [ [x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))] ]; } return generateM(points) }; } else { sWithRegion = function(d0, d1, j, diff) { var points; if (config.axis_rotated) { points = [ [y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))] ]; } else { points = [ [x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))] ]; } return generateM(points) }; } // Generate for (i = 0; i < d.length; i++) { // Draw as normal if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { s += ' ' + xValue(d[i]) + ' ' + yValue(d[i]); } // Draw with region // TODO: Fix for horizotal charts else { xp = $$.getScale( d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries() ); yp = $$.getScale(d[i - 1].value, d[i].value); dx = x(d[i].x) - x(d[i - 1].x); dy = y(d[i].value) - y(d[i - 1].value); dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); diff = 2 / dd; diffx2 = diff * 2; for (j = diff; j <= 1; j += diffx2) { s += sWithRegion(d[i - 1], d[i], j, diff); } } prev = d[i].x; } return s }; ChartInternal.prototype.updateArea = function(durationForExit) { var $$ = this, d3 = $$.d3; var mainArea = $$.main .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var mainAreaEnter = mainArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0 }); $$.mainArea = mainAreaEnter .merge(mainArea) .style('opacity', $$.orgAreaOpacity); mainArea .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawArea = function( drawArea, withTransition, transition ) { return [ (withTransition ? this.mainArea.transition(transition) : this.mainArea) .attr('d', drawArea) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) ] }; ChartInternal.prototype.generateDrawArea = function(areaIndices, isSub) { var $$ = this, config = $$.config, area = $$.d3.area(), getPoints = $$.generateGetAreaPoints(areaIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, value0 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)) }, value1 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value) }; area = config.axis_rotated ? area .x0(value0) .x1(value1) .y(xValue) : area .x(xValue) .y0(config.area_above ? 0 : value0) .y1(value1); if (!config.line_connectNull) { area = area.defined(function(d) { return d.value !== null }); } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path; if ($$.isAreaType(d)) { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = area.curve($$.getInterpolate(d))(values); } else { if (values[0]) { x0 = $$.x(values[0].x); y0 = $$.getYScale(d.id)(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0' } }; ChartInternal.prototype.getAreaBaseValue = function() { return 0 }; ChartInternal.prototype.generateGetAreaPoints = function(areaIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, areaTargetsNum = areaIndices.__max__ + 1, x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), y = $$.getShapeY(!!isSub), areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = areaOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the area position return [ [posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, offset] // needed for compatibility ] } }; ChartInternal.prototype.updateCircle = function(cx, cy) { var $$ = this; var mainCircle = $$.main .selectAll('.' + CLASS.circles) .selectAll('.' + CLASS.circle) .data($$.lineOrScatterOrStanfordData.bind($$)); var mainCircleEnter = mainCircle .enter() .append('circle') .attr('shape-rendering', $$.isStanfordGraphType() ? 'crispEdges' : '') .attr('class', $$.classCircle.bind($$)) .attr('cx', cx) .attr('cy', cy) .attr('r', $$.pointR.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ); $$.mainCircle = mainCircleEnter .merge(mainCircle) .style( 'opacity', $$.isStanfordGraphType() ? 1 : $$.initialOpacityForCircle.bind($$) ); mainCircle.exit().style('opacity', 0); }; ChartInternal.prototype.redrawCircle = function( cx, cy, withTransition, transition ) { var $$ = this, selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle); return [ (withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle) .style('opacity', this.opacityForCircle.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ) .attr('cx', cx) .attr('cy', cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles) .attr('cx', cx) .attr('cy', cy) ] }; ChartInternal.prototype.circleX = function(d) { return d.x || d.x === 0 ? this.x(d.x) : null }; ChartInternal.prototype.updateCircleY = function() { var $$ = this, lineIndices, getPoints; if ($$.config.data_groups.length > 0) { (lineIndices = $$.getShapeIndices($$.isLineType)), (getPoints = $$.generateGetLinePoints(lineIndices)); $$.circleY = function(d, i) { return getPoints(d, i)[0][1] }; } else { $$.circleY = function(d) { return $$.getYScale(d.id)(d.value) }; } }; ChartInternal.prototype.getCircles = function(i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')) }; ChartInternal.prototype.expandCircles = function(i, id, reset) { var $$ = this, r = $$.pointExpandedR.bind($$); if (reset) { $$.unexpandCircles(); } $$.getCircles(i, id) .classed(CLASS.EXPANDED, true) .attr('r', r); }; ChartInternal.prototype.unexpandCircles = function(i) { var $$ = this, r = $$.pointR.bind($$); $$.getCircles(i) .filter(function() { return $$.d3.select(this).classed(CLASS.EXPANDED) }) .classed(CLASS.EXPANDED, false) .attr('r', r); }; ChartInternal.prototype.pointR = function(d) { var $$ = this, config = $$.config; return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r }; ChartInternal.prototype.pointExpandedR = function(d) { var $$ = this, config = $$.config; if (config.point_focus_expand_enabled) { return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75 } else { return $$.pointR(d) } }; ChartInternal.prototype.pointSelectR = function(d) { var $$ = this, config = $$.config; return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4 }; ChartInternal.prototype.isWithinCircle = function(that, r) { var d3 = this.d3, mouse = d3.mouse(that), d3_this = d3.select(that), cx = +d3_this.attr('cx'), cy = +d3_this.attr('cy'); return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r }; ChartInternal.prototype.isWithinStep = function(that, y) { return Math.abs(y - this.d3.mouse(that)[1]) < 30 }; ChartInternal.prototype.getCurrentWidth = function() { var $$ = this, config = $$.config; return config.size_width ? config.size_width : $$.getParentWidth() }; ChartInternal.prototype.getCurrentHeight = function() { var $$ = this, config = $$.config, h = config.size_height ? config.size_height : $$.getParentHeight(); return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1) }; ChartInternal.prototype.getCurrentPaddingTop = function() { var $$ = this, config = $$.config, padding = isValue(config.padding_top) ? config.padding_top : 0; if ($$.title && $$.title.node()) { padding += $$.getTitlePadding(); } return padding }; ChartInternal.prototype.getCurrentPaddingBottom = function() { var config = this.config; return isValue(config.padding_bottom) ? config.padding_bottom : 0 }; ChartInternal.prototype.getCurrentPaddingLeft = function(withoutRecompute) { var $$ = this, config = $$.config; if (isValue(config.padding_left)) { return config.padding_left } else if (config.axis_rotated) { return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40) } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1 } else { return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)) } }; ChartInternal.prototype.getCurrentPaddingRight = function() { var $$ = this, config = $$.config, padding = 0, defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; if (isValue(config.padding_right)) { padding = config.padding_right + 1; // 1 is needed not to hide tick line } else if (config.axis_rotated) { padding = defaultPadding + legendWidthOnRight; } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated padding = 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); } else { padding = ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; } if ($$.colorScale && $$.colorScale.node()) { padding += $$.getColorScalePadding(); } return padding }; ChartInternal.prototype.getParentRectValue = function(key) { var parent = this.selectChart.node(), v; while (parent && parent.tagName !== 'BODY') { try { v = parent.getBoundingClientRect()[key]; } catch (e) { if (key === 'width') { // In IE in certain cases getBoundingClientRect // will cause an "unspecified error" v = parent.offsetWidth; } } if (v) { break } parent = parent.parentNode; } return v }; ChartInternal.prototype.getParentWidth = function() { return this.getParentRectValue('width') }; ChartInternal.prototype.getParentHeight = function() { var h = this.selectChart.style('height'); return h.indexOf('px') > 0 ? +h.replace('px', '') : 0 }; ChartInternal.prototype.getSvgLeft = function(withoutRecompute) { var $$ = this, config = $$.config, hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, leftAxis = $$.main.select('.' + leftAxisClass).node(), svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, chartRect = $$.selectChart.node().getBoundingClientRect(), hasArc = $$.hasArcType(), svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); return svgLeft > 0 ? svgLeft : 0 }; ChartInternal.prototype.getAxisWidthByAxisId = function(id, withoutRecompute) { var $$ = this, position = $$.axis.getLabelPositionById(id); return ( $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40) ) }; ChartInternal.prototype.getHorizontalAxisHeight = function(axisId) { var $$ = this, config = $$.config, h = 30; if (axisId === 'x' && !config.axis_x_show) { return 8 } if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height } if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1 } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top } // Calculate x axis height when tick rotated if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_x_tick_rotate))) / 180); } // Calculate y axis height when tick rotated if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_y_tick_rotate))) / 180); } return ( h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0) ) }; ChartInternal.prototype.initBrush = function(scale) { var $$ = this, d3 = $$.d3; // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function() { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return } $$.redrawForBrush(); }) .on('end', function() { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); } }); $$.brush.updateExtent = function() { var range = this.scale.range(), extent; if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ]; } else { extent = [ [range[0], 0], [range[1], $$.height2] ]; } this.extent(extent); return this }; $$.brush.updateScale = function(scale) { this.scale = scale; return this }; $$.brush.update = function(scale) { this.updateScale(scale || $$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call(this); }; $$.brush.clear = function() { $$.context.select('.' + CLASS.brush).call($$.brush.move, null); }; $$.brush.selection = function() { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()) }; $$.brush.selectionAsValue = function(selectionAsValue, withTransition) { var selection, brush; if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ]; brush = $$.context.select('.' + CLASS.brush); if (withTransition) { brush = brush.transition(); } $$.brush.move(brush, selection); } return [] } selection = $$.brush.selection() || [0, 0]; return [this.scale.invert(selection[0]), this.scale.invert(selection[1])] }; $$.brush.empty = function() { var selection = $$.brush.selection(); return !selection || selection[0] === selection[1] }; return $$.brush.updateScale(scale) }; ChartInternal.prototype.initSubchart = function() { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))); // set style context.style('visibility', 'visible'); // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart); // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush); // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis); }; ChartInternal.prototype.initSubchartBrush = function() { var $$ = this; // Add extent rect for Brush $$.initBrush($$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call($$.brush); }; ChartInternal.prototype.updateTargetsForSubchart = function(targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$); //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets); contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0); contextBarEnter.merge(contextBar).attr('class', classChartBar); // Bars for each data contextBarEnter.append('g').attr('class', classBars); //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets); contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0); contextLineEnter.merge(contextLine).attr('class', classChartLine); // Lines for each data contextLineEnter.append('g').attr('class', classLines); // Area contextLineEnter.append('g').attr('class', classAreas); //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr( config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2 ); }; ChartInternal.prototype.updateBarForSubchart = function(durationForExit) { var $$ = this; var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)); var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color); contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawBarForSubchart = function( drawBarOnSub, withTransition, duration ) { (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar ) .attr('d', drawBarOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateLineForSubchart = function(durationForExit) { var $$ = this; var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawLineForSubchart = function( drawLineOnSub, withTransition, duration ) { (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine ) .attr('d', drawLineOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateAreaForSubchart = function(durationForExit) { var $$ = this, d3 = $$.d3; var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0 }); contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0); }; ChartInternal.prototype.redrawAreaForSubchart = function( drawAreaOnSub, withTransition, duration ) { (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea ) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity); }; ChartInternal.prototype.redrawSubchart = function( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub; // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()); } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()); } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true); drawBarOnSub = $$.generateDrawBar(barIndices, true); drawLineOnSub = $$.generateDrawLine(lineIndices, true); $$.updateBarForSubchart(duration); $$.updateLineForSubchart(duration); $$.updateAreaForSubchart(duration); $$.redrawBarForSubchart(drawBarOnSub, duration, duration); $$.redrawLineForSubchart(drawLineOnSub, duration, duration); $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); } }; ChartInternal.prototype.redrawForBrush = function() { var $$ = this, x = $$.x, d3 = $$.d3, s; $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }); // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range(); $$.main .select('.' + CLASS.eventRect) .call( $$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) ); $$.config.subchart_onbrush.call($$.api, x.orgDomain()); }; ChartInternal.prototype.transformContext = function( withTransition, transitions ) { var $$ = this, subXAxis; if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX; } else { subXAxis = $$.context.select('.' + CLASS.axisX); if (withTransition) { subXAxis = subXAxis.transition(); } } $$.context.attr('transform', $$.getTranslate('context')); subXAxis.attr('transform', $$.getTranslate('subx')); }; ChartInternal.prototype.getDefaultSelection = function() { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection; if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])]; } return selection }; ChartInternal.prototype.removeSubchart = function() { const $$ = this; $$.brush = null; $$.context.remove(); $$.context = null; }; ChartInternal.prototype.initText = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartTexts); $$.mainText = $$.d3.selectAll([]); }; ChartInternal.prototype.updateTargetsForText = function(targets) { var $$ = this, classChartText = $$.classChartText.bind($$), classTexts = $$.classTexts.bind($$), classFocus = $$.classFocus.bind($$); var mainText = $$.main .select('.' + CLASS.chartTexts) .selectAll('.' + CLASS.chartText) .data(targets); var mainTextEnter = mainText .enter() .append('g') .attr('class', classChartText) .style('opacity', 0) .style('pointer-events', 'none'); mainTextEnter.append('g').attr('class', classTexts); mainTextEnter.merge(mainText).attr('class', function(d) { return classChartText(d) + classFocus(d) }); }; ChartInternal.prototype.updateText = function( xForText, yForText, durationForExit ) { var $$ = this, config = $$.config, barOrLineData = $$.barOrLineData.bind($$), classText = $$.classText.bind($$); var mainText = $$.main .selectAll('.' + CLASS.texts) .selectAll('.' + CLASS.text) .data(barOrLineData); var mainTextEnter = mainText .enter() .append('text') .attr('class', classText) .attr('text-anchor', function(d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle' }) .style('stroke', 'none') .attr('x', xForText) .attr('y', yForText) .style('fill', function(d) { return $$.color(d) }) .style('fill-opacity', 0); $$.mainText = mainTextEnter.merge(mainText).text(function(d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j) }); mainText .exit() .transition() .duration(durationForExit) .style('fill-opacity', 0) .remove(); }; ChartInternal.prototype.redrawText = function( xForText, yForText, forFlow, withTransition, transition ) { return [ (withTransition ? this.mainText.transition(transition) : this.mainText) .attr('x', xForText) .attr('y', yForText) .style('fill', this.color) .style('fill-opacity', forFlow ? 0 : this.opacityForText.bind(this)) ] }; ChartInternal.prototype.getTextRect = function(text, cls, element) { var dummy = this.d3 .select('body') .append('div') .classed('c3', true), svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0), font = this.d3.select(element).style('font'), rect; svg .selectAll('.dummy') .data([text]) .enter() .append('text') .classed(cls ? cls : '', true) .style('font', font) .text(text) .each(function() { rect = getBBox(this); }); dummy.remove(); return rect }; ChartInternal.prototype.generateXYForText = function( areaIndices, barIndices, lineIndices, forX ) { var $$ = this, getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false), getter = forX ? $$.getXForText : $$.getYForText; return function(d, i) { var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; return getter.call($$, getPoints(d, i), d, this) } }; ChartInternal.prototype.getXForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding; if ($$.config.axis_rotated) { padding = $$.isBarType(d) ? 4 : 6; xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); } else { xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; } // show labels regardless of the domain if value is null if (d.value === null) { if (xPos > $$.width) { xPos = $$.width - box.width; } else if (xPos < 0) { xPos = 4; } } return xPos }; ChartInternal.prototype.getYForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), yPos; if ($$.config.axis_rotated) { yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; } else { yPos = points[2][1]; if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { yPos += box.height; if ($$.isBarType(d) && $$.isSafari()) { yPos -= 3; } else if (!$$.isBarType(d) && $$.isChrome()) { yPos += 3; } } else { yPos += $$.isBarType(d) ? -3 : -6; } } // show labels regardless of the domain if value is null if (d.value === null && !$$.config.axis_rotated) { if (yPos < box.height) { yPos = box.height; } else if (yPos > this.height) { yPos = this.height - 4; } } return yPos }; ChartInternal.prototype.initTitle = function() { var $$ = this; $$.title = $$.svg .append('text') .text($$.config.title_text) .attr('class', $$.CLASS.title); }; ChartInternal.prototype.redrawTitle = function() { var $$ = this; $$.title.attr('x', $$.xForTitle.bind($$)).attr('y', $$.yForTitle.bind($$)); }; ChartInternal.prototype.xForTitle = function() { var $$ = this, config = $$.config, position = config.title_position || 'left', x; if (position.indexOf('right') >= 0) { x = $$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width - config.title_padding.right; } else if (position.indexOf('center') >= 0) { x = Math.max( ($$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width) / 2, 0 ); } else { // left x = config.title_padding.left; } return x }; ChartInternal.prototype.yForTitle = function() { var $$ = this; return ( $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()) .height ) }; ChartInternal.prototype.getTitlePadding = function() { var $$ = this; return $$.yForTitle() + $$.config.title_padding.bottom }; function powerOfTen(d) { return d / Math.pow(10, Math.ceil(Math.log(d) / Math.LN10 - 1e-12)) === 1 } ChartInternal.prototype.drawColorScale = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], barWidth, barHeight, axis, points, legendAxis, axisScale, inverseScale, height; barWidth = !isNaN(config.stanford_scaleWidth) ? config.stanford_scaleWidth : 20; barHeight = 5; if (barHeight < 0 || barWidth < 0) { throw Error("Colorscale's barheight and barwidth must be greater than 0.") } height = $$.height - config.stanford_padding.bottom - config.stanford_padding.top; points = d3.range(config.stanford_padding.bottom, height, barHeight); inverseScale = d3 .scaleSequential(target.colors) .domain([points[points.length - 1], points[0]]); if ($$.colorScale) { $$.colorScale.remove(); } $$.colorScale = $$.svg .append('g') .attr('width', 50) .attr('height', height) .attr('class', CLASS.colorScale); $$.colorScale .append('g') .attr('transform', `translate(0, ${config.stanford_padding.top})`) .selectAll('bars') .data(points) .enter() .append('rect') .attr('y', (d, i) => i * barHeight) .attr('x', 0) .attr('width', barWidth) .attr('height', barHeight) .attr('fill', function(d) { return inverseScale(d) }); // Legend Axis axisScale = d3 .scaleLog() .domain([target.minEpochs, target.maxEpochs]) .range([ points[0] + config.stanford_padding.top + points[points.length - 1] + barHeight - 1, points[0] + config.stanford_padding.top ]); legendAxis = d3.axisRight(axisScale); if (config.stanford_scaleFormat === 'pow10') { legendAxis.tickValues([1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]); } else if (isFunction(config.stanford_scaleFormat)) { legendAxis.tickFormat(config.stanford_scaleFormat); } else { legendAxis.tickFormat(d3.format('d')); } if (isFunction(config.stanford_scaleValues)) { legendAxis.tickValues( config.stanford_scaleValues(target.minEpochs, target.maxEpochs) ); } // Draw Axis axis = $$.colorScale .append('g') .attr('class', 'legend axis') .attr('transform', `translate(${barWidth},0)`) .call(legendAxis); if (config.stanford_scaleFormat === 'pow10') { axis .selectAll('.tick text') .text(null) .filter(powerOfTen) .text(10) .append('tspan') .attr('dy', '-.7em') // https://bl.ocks.org/mbostock/6738229 .text(function(d) { return Math.round(Math.log(d) / Math.LN10) }); } $$.colorScale.attr( 'transform', `translate(${$$.currentWidth - $$.xForColorScale()}, 0)` ); }; ChartInternal.prototype.xForColorScale = function() { var $$ = this; return $$.config.stanford_padding.right + getBBox($$.colorScale.node()).width }; ChartInternal.prototype.getColorScalePadding = function() { var $$ = this; return $$.xForColorScale() + $$.config.stanford_padding.left + 20 }; ChartInternal.prototype.isStanfordGraphType = function() { var $$ = this; return $$.config.data_type === 'stanford' }; ChartInternal.prototype.initStanfordData = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], epochs, maxEpochs, minEpochs; // Make larger values appear on top target.values.sort(compareEpochs); // Get array of epochs epochs = target.values.map(a => a.epochs); minEpochs = !isNaN(config.stanford_scaleMin) ? config.stanford_scaleMin : d3.min(epochs); maxEpochs = !isNaN(config.stanford_scaleMax) ? config.stanford_scaleMax : d3.max(epochs); if (minEpochs > maxEpochs) { throw Error('Number of minEpochs has to be smaller than maxEpochs') } target.colors = isFunction(config.stanford_colors) ? config.stanford_colors : d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)); target.colorscale = d3 .scaleSequentialLog(target.colors) .domain([minEpochs, maxEpochs]); target.minEpochs = minEpochs; target.maxEpochs = maxEpochs; }; ChartInternal.prototype.getStanfordPointColor = function(d) { var $$ = this, target = $$.data.targets[0]; return target.colorscale(d.epochs) }; // http://jsfiddle.net/Xotic750/KtzLq/ ChartInternal.prototype.getCentroid = function(points) { var area = getRegionArea(points); var x = 0, y = 0, i, j, f, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; f = point1.x * point2.y - point2.x * point1.y; x += (point1.x + point2.x) * f; y += (point1.y + point2.y) * f; } f = area * 6; return { x: x / f, y: y / f } }; ChartInternal.prototype.getStanfordTooltipTitle = function(d) { var $$ = this, labelX = $$.axis.getLabelText('x'), labelY = $$.axis.getLabelText('y'); return ` ${labelX ? sanitise(labelX) : 'x'}${ d.x } ${labelY ? sanitise(labelY) : 'y'}${ d.value } ` }; ChartInternal.prototype.countEpochsInRegion = function(region) { var $$ = this, target = $$.data.targets[0], total, count; total = target.values.reduce( (accumulator, currentValue) => accumulator + Number(currentValue.epochs), 0 ); count = target.values.reduce((accumulator, currentValue) => { if (pointInRegion(currentValue, region)) { return accumulator + Number(currentValue.epochs) } return accumulator }, 0); return { value: count, percentage: count !== 0 ? ((count / total) * 100).toFixed(1) : 0 } }; var getRegionArea = function(points) { // thanks to: https://stackoverflow.com/questions/16282330/find-centerpoint-of-polygon-in-javascript var area = 0, i, j, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; area += point1.x * point2.y; area -= point1.y * point2.x; } area /= 2; return area }; var pointInRegion = function(point, region) { // thanks to: http://bl.ocks.org/bycoffe/5575904 // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html let xi, yi, yj, xj, intersect, x = point.x, y = point.value, inside = false; for (let i = 0, j = region.length - 1; i < region.length; j = i++) { xi = region[i].x; yi = region[i].y; xj = region[j].x; yj = region[j].y; intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside }; var compareEpochs = function(a, b) { if (a.epochs < b.epochs) { return -1 } if (a.epochs > b.epochs) { return 1 } return 0 }; ChartInternal.prototype.initStanfordElements = function() { var $$ = this; // Avoid blocking eventRect $$.stanfordElements = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.stanfordElements); $$.stanfordElements.append('g').attr('class', CLASS.stanfordLines); $$.stanfordElements.append('g').attr('class', CLASS.stanfordTexts); $$.stanfordElements.append('g').attr('class', CLASS.stanfordRegions); }; ChartInternal.prototype.updateStanfordElements = function(duration) { var $$ = this, main = $$.main, config = $$.config, stanfordLine, stanfordLineEnter, stanfordRegion, stanfordRegionEnter, stanfordText, stanfordTextEnter, xvCustom = $$.xvCustom.bind($$), yvCustom = $$.yvCustom.bind($$), countPointsInRegion = $$.countEpochsInRegion.bind($$); // Stanford-Lines stanfordLine = main .select('.' + CLASS.stanfordLines) .style('shape-rendering', 'geometricprecision') .selectAll('.' + CLASS.stanfordLine) .data(config.stanford_lines); // enter stanfordLineEnter = stanfordLine .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordLine + (d['class'] ? ' ' + d['class'] : '') }); stanfordLineEnter .append('line') .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 0); // update $$.stanfordLines = stanfordLineEnter.merge(stanfordLine); $$.stanfordLines .select('line') .transition() .duration(duration) .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 1); // exit stanfordLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Text stanfordText = main .select('.' + CLASS.stanfordTexts) .selectAll('.' + CLASS.stanfordText) .data(config.stanford_texts); // enter stanfordTextEnter = stanfordText .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordText + (d['class'] ? ' ' + d['class'] : '') }); stanfordTextEnter .append('text') .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .style('opacity', 0); // update $$.stanfordTexts = stanfordTextEnter.merge(stanfordText); $$.stanfordTexts .select('text') .transition() .duration(duration) .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .text(function(d) { return d.content }) .style('opacity', 1); // exit stanfordText .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Regions stanfordRegion = main .select('.' + CLASS.stanfordRegions) .selectAll('.' + CLASS.stanfordRegion) .data(config.stanford_regions); // enter stanfordRegionEnter = stanfordRegion .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordRegion + (d['class'] ? ' ' + d['class'] : '') }); stanfordRegionEnter .append('polygon') .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', 0); stanfordRegionEnter .append('text') .attr('x', d => $$.getCentroid(d.points).x) .attr('y', d => $$.getCentroid(d.points).y) .style('opacity', 0); // update $$.stanfordRegions = stanfordRegionEnter.merge(stanfordRegion); $$.stanfordRegions .select('polygon') .transition() .duration(duration) .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', d => { return d.opacity ? d.opacity : 0.2 }); $$.stanfordRegions .select('text') .transition() .duration(duration) .attr('x', d => config.axis_rotated ? yvCustom($$.getCentroid(d.points), 'y') : xvCustom($$.getCentroid(d.points), 'x') ) .attr('y', d => config.axis_rotated ? xvCustom($$.getCentroid(d.points), 'x') : yvCustom($$.getCentroid(d.points), 'y') ) .text(function(d) { if (d.text) { var value, percentage, temp; if ($$.isStanfordGraphType()) { temp = countPointsInRegion(d.points); value = temp.value; percentage = temp.percentage; } return d.text(value, percentage) } return '' }) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('opacity', 1); // exit stanfordRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.initTooltip = function() { var $$ = this, config = $$.config, i; $$.tooltip = $$.selectChart .style('position', 'relative') .append('div') .attr('class', CLASS.tooltipContainer) .style('position', 'absolute') .style('pointer-events', 'none') .style('display', 'none'); // Show tooltip if needed if (config.tooltip_init_show) { if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); for (i = 0; i < $$.data.targets[0].values.length; i++) { if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { break } } config.tooltip_init_x = i; } $$.tooltip.html( config.tooltip_contents.call( $$, $$.data.targets.map(function(d) { return $$.addName(d.values[config.tooltip_init_x]) }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color ) ); $$.tooltip .style('top', config.tooltip_init_position.top) .style('left', config.tooltip_init_position.left) .style('display', 'block'); } }; ChartInternal.prototype.getTooltipSortFunction = function() { var $$ = this, config = $$.config; if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { // if data are not grouped or if an order is specified // for the tooltip values we sort them by their values var order = config.tooltip_order; if (order === undefined) { order = config.data_order; } var valueOf = function(obj) { return obj ? obj.value : null }; // if data are not grouped, we sort them by their value if (isString(order) && order.toLowerCase() === 'asc') { return function(a, b) { return valueOf(a) - valueOf(b) } } else if (isString(order) && order.toLowerCase() === 'desc') { return function(a, b) { return valueOf(b) - valueOf(a) } } else if (isFunction(order)) { // if the function is from data_order we need // to wrap the returned function in order to format // the sorted value to the expected format var sortFunction = order; if (config.tooltip_order === undefined) { sortFunction = function(a, b) { return order( a ? { id: a.id, values: [a] } : null, b ? { id: b.id, values: [b] } : null ) }; } return sortFunction } else if (isArray(order)) { return function(a, b) { return order.indexOf(a.id) - order.indexOf(b.id) } } } else { // if data are grouped, we follow the order of grouped targets var ids = $$.orderTargets($$.data.targets).map(function(i) { return i.id }); // if it was either asc or desc we need to invert the order // returned by orderTargets if ($$.isOrderAsc() || $$.isOrderDesc()) { ids = ids.reverse(); } return function(a, b) { return ids.indexOf(a.id) - ids.indexOf(b.id) } } }; ChartInternal.prototype.getTooltipContent = function( d, defaultTitleFormat, defaultValueFormat, color ) { var $$ = this, config = $$.config, titleFormat = config.tooltip_format_title || defaultTitleFormat, nameFormat = config.tooltip_format_name || function(name) { return name }, text, i, title, value, name, bgcolor; var valueFormat = config.tooltip_format_value; if (!valueFormat) { valueFormat = $$.isTargetNormalized(d.id) ? (v, ratio) => `${(ratio * 100).toFixed(2)}%` : defaultValueFormat; } var tooltipSortFunction = this.getTooltipSortFunction(); if (tooltipSortFunction) { d.sort(tooltipSortFunction); } for (i = 0; i < d.length; i++) { if (!(d[i] && (d[i].value || d[i].value === 0))) { continue } if ($$.isStanfordGraphType()) { // Custom tooltip for stanford plots if (!text) { title = $$.getStanfordTooltipTitle(d[i]); text = "" + title; } bgcolor = $$.getStanfordPointColor(d[i]); name = sanitise(config.data_epochs); // Epochs key name value = d[i].epochs; } else { // Regular tooltip if (!text) { title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x); text = "
" + (title || title === 0 ? "' : ''); } value = sanitise( valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d) ); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue } name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); } } if (value !== undefined) { text += ""; text += "'; text += "'; text += ''; } } return text + '
" + title + '
" + name + '" + value + '
' }; ChartInternal.prototype.tooltipPosition = function( dataToShow, tWidth, tHeight, element ) { var $$ = this, config = $$.config, d3 = $$.d3; var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; var forArc = $$.hasArcType(), mouse = d3.mouse(element); // Determin tooltip position if (forArc) { tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]; tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20; } else { svgLeft = $$.getSvgLeft(true); if (config.axis_rotated) { tooltipLeft = svgLeft + mouse[0] + 100; tooltipRight = tooltipLeft + tWidth; chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = $$.x(dataToShow[0].x) + 20; } else { tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; tooltipRight = tooltipLeft + tWidth; chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = mouse[1] + 15; } if (tooltipRight > chartRight) { // 20 is needed for Firefox to keep tooltip width tooltipLeft -= tooltipRight - chartRight + 20; } if (tooltipTop + tHeight > $$.currentHeight) { tooltipTop -= tHeight + 30; } } if (tooltipTop < 0) { tooltipTop = 0; } return { top: tooltipTop, left: tooltipLeft } }; ChartInternal.prototype.showTooltip = function(selectedData, element) { var $$ = this, config = $$.config; var tWidth, tHeight, position; var forArc = $$.hasArcType(), dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition; if (dataToShow.length === 0 || !config.tooltip_show) { $$.hideTooltip(); return } $$.tooltip .html( config.tooltip_contents.call( $$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color ) ) .style('display', 'block'); // Get tooltip dimensions tWidth = $$.tooltip.property('offsetWidth'); tHeight = $$.tooltip.property('offsetHeight'); position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); // Set tooltip $$.tooltip .style('top', position.top + 'px') .style('left', position.left + 'px'); }; ChartInternal.prototype.hideTooltip = function() { this.tooltip.style('display', 'none'); }; ChartInternal.prototype.setTargetType = function(targetIds, type) { var $$ = this, config = $$.config; $$.mapToTargetIds(targetIds).forEach(function(id) { $$.withoutFadeIn[id] = type === config.data_types[id]; config.data_types[id] = type; }); if (!targetIds) { config.data_type = type; } }; ChartInternal.prototype.hasType = function(type, targets) { var $$ = this, types = $$.config.data_types, has = false; targets = targets || $$.data.targets; if (targets && targets.length) { targets.forEach(function(target) { var t = types[target.id]; if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { has = true; } }); } else if (Object.keys(types).length) { Object.keys(types).forEach(function(id) { if (types[id] === type) { has = true; } }); } else { has = $$.config.data_type === type; } return has }; ChartInternal.prototype.hasArcType = function(targets) { return ( this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets) ) }; ChartInternal.prototype.isLineType = function(d) { var config = this.config, id = isString(d) ? d : d.id; return ( !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf( config.data_types[id] ) >= 0 ) }; ChartInternal.prototype.isStepType = function(d) { var id = isString(d) ? d : d.id; return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0 }; ChartInternal.prototype.isSplineType = function(d) { var id = isString(d) ? d : d.id; return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0 }; ChartInternal.prototype.isAreaType = function(d) { var id = isString(d) ? d : d.id; return ( ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0 ) }; ChartInternal.prototype.isBarType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'bar' }; ChartInternal.prototype.isScatterType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'scatter' }; ChartInternal.prototype.isStanfordType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'stanford' }; ChartInternal.prototype.isPieType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'pie' }; ChartInternal.prototype.isGaugeType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'gauge' }; ChartInternal.prototype.isDonutType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'donut' }; ChartInternal.prototype.isArcType = function(d) { return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d) }; ChartInternal.prototype.lineData = function(d) { return this.isLineType(d) ? [d] : [] }; ChartInternal.prototype.arcData = function(d) { return this.isArcType(d.data) ? [d] : [] }; /* not used function scatterData(d) { return isScatterType(d) ? d.values : []; } */ ChartInternal.prototype.barData = function(d) { return this.isBarType(d) ? d.values : [] }; ChartInternal.prototype.lineOrScatterOrStanfordData = function(d) { return this.isLineType(d) || this.isScatterType(d) || this.isStanfordType(d) ? d.values : [] }; ChartInternal.prototype.barOrLineData = function(d) { return this.isBarType(d) || this.isLineType(d) ? d.values : [] }; ChartInternal.prototype.isSafari = function() { var ua = window.navigator.userAgent; return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0 }; ChartInternal.prototype.isChrome = function() { var ua = window.navigator.userAgent; return ua.indexOf('Chrome') >= 0 }; ChartInternal.prototype.initZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, startEvent; $$.zoom = d3 .zoom() .on('start', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } startEvent = e; config.zoom_onzoomstart.call($$.api, e); }) .on('zoom', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } $$.redrawForZoom(); config.zoom_onzoom.call($$.api, $$.x.orgDomain()); }) .on('end', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } // if click, do nothing. otherwise, click interaction will be canceled. if ( e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY ) { return } config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); }); $$.zoom.updateDomain = function() { if (d3.event && d3.event.transform) { $$.x.domain(d3.event.transform.rescaleX($$.subX).domain()); } return this }; $$.zoom.updateExtent = function() { this.scaleExtent([1, Infinity]) .translateExtent([ [0, 0], [$$.width, $$.height] ]) .extent([ [0, 0], [$$.width, $$.height] ]); return this }; $$.zoom.update = function() { return this.updateExtent().updateDomain() }; return $$.zoom.updateExtent() }; ChartInternal.prototype.zoomTransform = function(range) { var $$ = this, s = [$$.x(range[0]), $$.x(range[1])]; return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) }; ChartInternal.prototype.initDragZoom = function() { const $$ = this; const d3 = $$.d3; const config = $$.config; const context = ($$.context = $$.svg); const brushXPos = $$.margin.left + 20.5; const brushYPos = $$.margin.top + 0.5; if (!(config.zoom_type === 'drag' && config.zoom_enabled)) { return } const getZoomedDomain = selection => selection && selection.map(x => $$.x.invert(x)); const brush = ($$.dragZoomBrush = d3 .brushX() .on('start', () => { $$.api.unzoom(); $$.svg.select('.' + CLASS.dragZoom).classed('disabled', false); config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); }) .on('brush', () => { config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection)); }) .on('end', () => { if (d3.event.selection == null) { return } const zoomedDomain = getZoomedDomain(d3.event.selection); if (!config.zoom_disableDefaultBehavior) { $$.api.zoom(zoomedDomain); } $$.svg.select('.' + CLASS.dragZoom).classed('disabled', true); config.zoom_onzoomend.call($$.api, zoomedDomain); })); context .append('g') .classed(CLASS.dragZoom, true) .attr('clip-path', $$.clipPath) .attr('transform', 'translate(' + brushXPos + ',' + brushYPos + ')') .call(brush); }; ChartInternal.prototype.getZoomDomain = function() { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); return [min, max] }; ChartInternal.prototype.redrawForZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x; if (!config.zoom_enabled) { return } if ($$.filterTargetsToShow($$.data.targets).length === 0) { return } zoom.update(); if (config.zoom_disableDefaultBehavior) { return } if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); } $$.redraw({ withTransition: false, withY: config.zoom_rescale, withSubchart: false, withEventRect: false, withDimension: false }); if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') { $$.cancelClick = true; } }; export default c3; ================================================ FILE: c3.js ================================================ /* @license C3.js v0.7.20 | (c) C3 Team and other contributors | http://c3js.org/ */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.c3 = factory()); }(this, (function () { 'use strict'; function ChartInternal(api) { var $$ = this; // Note: This part will be replaced by rollup-plugin-modify // When bundling esm output. Beware of changing this line. // TODO: Maybe we should check that the modification by rollup-plugin-modify // is valid during unit tests. $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require('d3') : undefined; $$.api = api; $$.config = $$.getDefaultConfig(); $$.data = {}; $$.cache = {}; $$.axes = {}; } /** * The Chart class * * The methods of this class is the public APIs of the chart object. */ function Chart(config) { this.internal = new ChartInternal(this); this.internal.loadConfig(config); this.internal.beforeInit(config); this.internal.init(); this.internal.afterInit(config); (function bindThis(fn, target, argThis) { Object.keys(fn).forEach(function (key) { target[key] = fn[key].bind(argThis); if (Object.keys(fn[key]).length > 0) { bindThis(fn[key], target[key], argThis); } }); })(Chart.prototype, this, this); } var asHalfPixel = function (n) { return Math.ceil(n) + 0.5; }; var ceil10 = function (v) { return Math.ceil(v / 10) * 10; }; var diffDomain = function (d) { return d[1] - d[0]; }; var getOption = function (options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue; }; var getPathBox = function (path) { var box = getBBox(path), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], minX = items[0].x, minY = Math.min(items[0].y, items[1].y); return { x: minX, y: minY, width: box.width, height: box.height }; }; var getBBox = function (element) { try { return element.getBBox(); } catch (ignore) { // Firefox will throw an exception if getBBox() is called whereas the // element is rendered with display:none // See https://github.com/c3js/c3/issues/2692 // The previous code was using `getBoundingClientRect` which was returning // everything at 0 in this case so let's reproduce this behavior here. return { x: 0, y: 0, width: 0, height: 0 }; } }; var hasValue = function (dict, value) { var found = false; Object.keys(dict).forEach(function (key) { if (dict[key] === value) { found = true; } }); return found; }; var isArray = function (o) { return Array.isArray(o); }; var isDefined = function (v) { return typeof v !== 'undefined'; }; var isEmpty = function (o) { return (typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0)); }; var isFunction = function (o) { return typeof o === 'function'; }; var isNumber = function (o) { return typeof o === 'number'; }; var isString = function (o) { return typeof o === 'string'; }; var isUndefined = function (v) { return typeof v === 'undefined'; }; var isValue = function (v) { return v || v === 0; }; var notEmpty = function (o) { return !isEmpty(o); }; var sanitise = function (str) { return typeof str === 'string' ? str.replace(//g, '>') : str; }; var flattenArray = function (arr) { return Array.isArray(arr) ? [].concat.apply([], arr) : []; }; /** * Returns whether the point is within the given box. * * @param {Array} point An [x,y] coordinate * @param {Object} box An object with {x, y, width, height} keys * @param {Number} sensitivity An offset to ease check on very small boxes */ var isWithinBox = function (point, box, sensitivity) { if (sensitivity === void 0) { sensitivity = 0; } var xStart = box.x - sensitivity; var xEnd = box.x + box.width + sensitivity; var yStart = box.y + box.height + sensitivity; var yEnd = box.y - sensitivity; return (xStart < point[0] && point[0] < xEnd && yEnd < point[1] && point[1] < yStart); }; /** * Returns Internet Explorer version number (or false if no Internet Explorer used). * * @param string agent Optional parameter to specify user agent */ var getIEVersion = function (agent) { // https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie if (typeof agent === 'undefined') { agent = window.navigator.userAgent; } var pos = agent.indexOf('MSIE '); // up to IE10 if (pos > 0) { return parseInt(agent.substring(pos + 5, agent.indexOf('.', pos)), 10); } pos = agent.indexOf('Trident/'); // IE11 if (pos > 0) { pos = agent.indexOf('rv:'); return parseInt(agent.substring(pos + 3, agent.indexOf('.', pos)), 10); } return false; }; /** * Returns whether the used browser is Internet Explorer. * * @param version Optional parameter to specify IE version */ var isIE = function (version) { var ver = getIEVersion(); if (typeof version === 'undefined') { return !!ver; } return version === ver; }; function AxisInternal(component, params) { var internal = this; internal.component = component; internal.params = params || {}; internal.d3 = component.d3; internal.scale = internal.d3.scaleLinear(); internal.range; internal.orient = 'bottom'; internal.innerTickSize = 6; internal.outerTickSize = this.params.withOuterTick ? 6 : 0; internal.tickPadding = 3; internal.tickValues = null; internal.tickFormat; internal.tickArguments; internal.tickOffset = 0; internal.tickCulling = true; internal.tickCentered; internal.tickTextCharSize; internal.tickTextRotate = internal.params.tickTextRotate; internal.tickLength; internal.axis = internal.generateAxis(); } AxisInternal.prototype.axisX = function (selection, x, tickOffset) { selection.attr('transform', function (d) { return 'translate(' + Math.ceil(x(d) + tickOffset) + ', 0)'; }); }; AxisInternal.prototype.axisY = function (selection, y) { selection.attr('transform', function (d) { return 'translate(0,' + Math.ceil(y(d)) + ')'; }); }; AxisInternal.prototype.scaleExtent = function (domain) { var start = domain[0], stop = domain[domain.length - 1]; return start < stop ? [start, stop] : [stop, start]; }; AxisInternal.prototype.generateTicks = function (scale) { var internal = this; var i, domain, ticks = []; if (scale.ticks) { return scale.ticks.apply(scale, internal.tickArguments); } domain = scale.domain(); for (i = Math.ceil(domain[0]); i < domain[1]; i++) { ticks.push(i); } if (ticks.length > 0 && ticks[0] > 0) { ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); } return ticks; }; AxisInternal.prototype.copyScale = function () { var internal = this; var newScale = internal.scale.copy(), domain; if (internal.params.isCategory) { domain = internal.scale.domain(); newScale.domain([domain[0], domain[1] - 1]); } return newScale; }; AxisInternal.prototype.textFormatted = function (v) { var internal = this, formatted = internal.tickFormat ? internal.tickFormat(v) : v; return typeof formatted !== 'undefined' ? formatted : ''; }; AxisInternal.prototype.updateRange = function () { var internal = this; internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()); return internal.range; }; AxisInternal.prototype.updateTickTextCharSize = function (tick) { var internal = this; if (internal.tickTextCharSize) { return internal.tickTextCharSize; } var size = { h: 11.5, w: 5.5 }; tick .select('text') .text(function (d) { return internal.textFormatted(d); }) .each(function (d) { var box = getBBox(this), text = internal.textFormatted(d), h = box.height, w = text ? box.width / text.length : undefined; if (h && w) { size.h = h; size.w = w; } }) .text(''); internal.tickTextCharSize = size; return size; }; AxisInternal.prototype.isVertical = function () { return this.orient === 'left' || this.orient === 'right'; }; AxisInternal.prototype.tspanData = function (d, i, scale) { var internal = this; var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d)); if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) { splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax); } return splitted.map(function (s) { return { index: i, splitted: s, length: splitted.length }; }); }; AxisInternal.prototype.splitTickText = function (d, scale) { var internal = this, tickText = internal.textFormatted(d), maxWidth = internal.params.tickWidth, subtext, spaceIndex, textWidth, splitted = []; if (Object.prototype.toString.call(tickText) === '[object Array]') { return tickText; } if (!maxWidth || maxWidth <= 0) { maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110; } function split(splitted, text) { spaceIndex = undefined; for (var i = 1; i < text.length; i++) { if (text.charAt(i) === ' ') { spaceIndex = i; } subtext = text.substr(0, i + 1); textWidth = internal.tickTextCharSize.w * subtext.length; // if text width gets over tick width, split by space index or crrent index if (maxWidth < textWidth) { return split(splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i)); } } return splitted.concat(text); } return split(splitted, tickText + ''); }; AxisInternal.prototype.ellipsify = function (splitted, max) { if (splitted.length <= max) { return splitted; } var ellipsified = splitted.slice(0, max); var remaining = 3; for (var i = max - 1; i >= 0; i--) { var available = ellipsified[i].length; ellipsified[i] = ellipsified[i] .substr(0, available - remaining) .padEnd(available, '.'); remaining -= available; if (remaining <= 0) { break; } } return ellipsified; }; AxisInternal.prototype.updateTickLength = function () { var internal = this; internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding; }; AxisInternal.prototype.lineY2 = function (d) { var internal = this, tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset); return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0; }; AxisInternal.prototype.textY = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength; }; AxisInternal.prototype.textTransform = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 'rotate(' + rotate + ')' : ''; }; AxisInternal.prototype.textTextAnchor = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? (rotate > 0 ? 'start' : 'end') : 'middle'; }; AxisInternal.prototype.tspanDx = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0; }; AxisInternal.prototype.tspanDy = function (d, i) { var internal = this, dy = internal.tickTextCharSize.h; if (i === 0) { if (internal.isVertical()) { dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3); } else { dy = '.71em'; } } return dy; }; AxisInternal.prototype.generateAxis = function () { var internal = this, d3 = internal.d3, params = internal.params; function axis(g, transition) { var self; g.each(function () { var g = (axis.g = d3.select(this)); var scale0 = this.__chart__ || internal.scale, scale1 = (this.__chart__ = internal.copyScale()); var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), ticks = g.selectAll('.tick').data(ticksValues, scale1), tickEnter = ticks .enter() .insert('g', '.domain') .attr('class', 'tick') .style('opacity', 1e-6), // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. tickExit = ticks.exit().remove(), tickUpdate = ticks.merge(tickEnter), tickTransform, tickX, tickY; if (params.isCategory) { internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); tickX = internal.tickCentered ? 0 : internal.tickOffset; tickY = internal.tickCentered ? internal.tickOffset : 0; } else { internal.tickOffset = tickX = 0; } internal.updateRange(); internal.updateTickLength(); internal.updateTickTextCharSize(g.select('.tick')); var lineUpdate = tickUpdate .select('line') .merge(tickEnter.append('line')), textUpdate = tickUpdate.select('text').merge(tickEnter.append('text')); var tspans = tickUpdate .selectAll('text') .selectAll('tspan') .data(function (d, i) { return internal.tspanData(d, i, scale1); }), tspanEnter = tspans.enter().append('tspan'), tspanUpdate = tspanEnter.merge(tspans).text(function (d) { return d.splitted; }); tspans.exit().remove(); var path = g.selectAll('.domain').data([0]), pathUpdate = path .enter() .append('path') .merge(path) .attr('class', 'domain'); // TODO: each attr should be one function and change its behavior by internal.orient, probably switch (internal.orient) { case 'bottom': { tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function (d, i) { return internal.lineY2(d, i); }); textUpdate .attr('x', 0) .attr('y', function (d, i) { return internal.textY(d, i); }) .attr('transform', function (d, i) { return internal.textTransform(d, i); }) .style('text-anchor', function (d, i) { return internal.textTextAnchor(d, i); }); tspanUpdate .attr('x', 0) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }) .attr('dx', function (d, i) { return internal.tspanDx(d, i); }); pathUpdate.attr('d', 'M' + internal.range[0] + ',' + internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + internal.outerTickSize); break; } case 'top': { // TODO: rotated tick text tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function (d, i) { return -1 * internal.lineY2(d, i); }); textUpdate .attr('x', 0) .attr('y', function (d, i) { return (-1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2)); }) .attr('transform', function (d, i) { return internal.textTransform(d, i); }) .style('text-anchor', function (d, i) { return internal.textTextAnchor(d, i); }); tspanUpdate .attr('x', 0) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }) .attr('dx', function (d, i) { return internal.tspanDx(d, i); }); pathUpdate.attr('d', 'M' + internal.range[0] + ',' + -internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + -internal.outerTickSize); break; } case 'left': { tickTransform = internal.axisY; lineUpdate .attr('x2', -internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', -internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'end'); tspanUpdate .attr('x', -internal.tickLength) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }); pathUpdate.attr('d', 'M' + -internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + -internal.outerTickSize); break; } case 'right': { tickTransform = internal.axisY; lineUpdate .attr('x2', internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'start'); tspanUpdate.attr('x', internal.tickLength).attr('dy', function (d, i) { return internal.tspanDy(d, i); }); pathUpdate.attr('d', 'M' + internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + internal.outerTickSize); break; } } if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2; scale0 = scale1 = function (d) { return x(d) + dx; }; } else if (scale0.rangeBand) { scale0 = scale1; } else { tickExit.call(tickTransform, scale1, internal.tickOffset); } tickEnter.call(tickTransform, scale0, internal.tickOffset); self = (transition ? tickUpdate.transition(transition) : tickUpdate) .style('opacity', 1) .call(tickTransform, scale1, internal.tickOffset); }); return self; } axis.scale = function (x) { if (!arguments.length) { return internal.scale; } internal.scale = x; return axis; }; axis.orient = function (x) { if (!arguments.length) { return internal.orient; } internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + '' : 'bottom'; return axis; }; axis.tickFormat = function (format) { if (!arguments.length) { return internal.tickFormat; } internal.tickFormat = format; return axis; }; axis.tickCentered = function (isCentered) { if (!arguments.length) { return internal.tickCentered; } internal.tickCentered = isCentered; return axis; }; axis.tickOffset = function () { return internal.tickOffset; }; axis.tickInterval = function () { var interval, length; if (params.isCategory) { interval = internal.tickOffset * 2; } else { length = axis.g .select('path.domain') .node() .getTotalLength() - internal.outerTickSize * 2; interval = length / axis.g.selectAll('line').size(); } return interval === Infinity ? 0 : interval; }; axis.ticks = function () { if (!arguments.length) { return internal.tickArguments; } internal.tickArguments = arguments; return axis; }; axis.tickCulling = function (culling) { if (!arguments.length) { return internal.tickCulling; } internal.tickCulling = culling; return axis; }; axis.tickValues = function (x) { if (typeof x === 'function') { internal.tickValues = function () { return x(internal.scale.domain()); }; } else { if (!arguments.length) { return internal.tickValues; } internal.tickValues = x; } return axis; }; return axis; }; var CLASS = { target: 'c3-target', chart: 'c3-chart', chartLine: 'c3-chart-line', chartLines: 'c3-chart-lines', chartBar: 'c3-chart-bar', chartBars: 'c3-chart-bars', chartText: 'c3-chart-text', chartTexts: 'c3-chart-texts', chartArc: 'c3-chart-arc', chartArcs: 'c3-chart-arcs', chartArcsTitle: 'c3-chart-arcs-title', chartArcsBackground: 'c3-chart-arcs-background', chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', selectedCircle: 'c3-selected-circle', selectedCircles: 'c3-selected-circles', eventRect: 'c3-event-rect', eventRects: 'c3-event-rects', eventRectsSingle: 'c3-event-rects-single', eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', regions: 'c3-regions', title: 'c3-title', tooltipContainer: 'c3-tooltip-container', tooltip: 'c3-tooltip', tooltipName: 'c3-tooltip-name', shape: 'c3-shape', shapes: 'c3-shapes', line: 'c3-line', lines: 'c3-lines', bar: 'c3-bar', bars: 'c3-bars', circle: 'c3-circle', circles: 'c3-circles', arc: 'c3-arc', arcLabelLine: 'c3-arc-label-line', arcs: 'c3-arcs', area: 'c3-area', areas: 'c3-areas', empty: 'c3-empty', text: 'c3-text', texts: 'c3-texts', gaugeValue: 'c3-gauge-value', grid: 'c3-grid', gridLines: 'c3-grid-lines', xgrid: 'c3-xgrid', xgrids: 'c3-xgrids', xgridLine: 'c3-xgrid-line', xgridLines: 'c3-xgrid-lines', xgridFocus: 'c3-xgrid-focus', ygrid: 'c3-ygrid', ygrids: 'c3-ygrids', ygridLine: 'c3-ygrid-line', ygridLines: 'c3-ygrid-lines', colorScale: 'c3-colorscale', stanfordElements: 'c3-stanford-elements', stanfordLine: 'c3-stanford-line', stanfordLines: 'c3-stanford-lines', stanfordRegion: 'c3-stanford-region', stanfordRegions: 'c3-stanford-regions', stanfordText: 'c3-stanford-text', stanfordTexts: 'c3-stanford-texts', axis: 'c3-axis', axisX: 'c3-axis-x', axisXLabel: 'c3-axis-x-label', axisY: 'c3-axis-y', axisYLabel: 'c3-axis-y-label', axisY2: 'c3-axis-y2', axisY2Label: 'c3-axis-y2-label', legendBackground: 'c3-legend-background', legendItem: 'c3-legend-item', legendItemEvent: 'c3-legend-item-event', legendItemTile: 'c3-legend-item-tile', legendItemHidden: 'c3-legend-item-hidden', legendItemFocused: 'c3-legend-item-focused', dragarea: 'c3-dragarea', EXPANDED: '_expanded_', SELECTED: '_selected_', INCLUDED: '_included_' }; var AxisClass = /** @class */ (function () { function AxisClass(owner) { this.owner = owner; this.d3 = owner.d3; this.internal = AxisInternal; } return AxisClass; }()); var Axis = AxisClass; Axis.prototype.init = function init() { var $$ = this.owner, config = $$.config, main = $$.main; $$.axes.x = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisX) .attr('clip-path', config.axis_x_inner ? '' : $$.clipPathForXAxis) .attr('transform', $$.getTranslate('x')) .style('visibility', config.axis_x_show ? 'visible' : 'hidden'); $$.axes.x .append('text') .attr('class', CLASS.axisXLabel) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .style('text-anchor', this.textAnchorForXAxisLabel.bind(this)); $$.axes.y = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY) .attr('clip-path', config.axis_y_inner ? '' : $$.clipPathForYAxis) .attr('transform', $$.getTranslate('y')) .style('visibility', config.axis_y_show ? 'visible' : 'hidden'); $$.axes.y .append('text') .attr('class', CLASS.axisYLabel) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForYAxisLabel.bind(this)); $$.axes.y2 = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY2) // clip-path? .attr('transform', $$.getTranslate('y2')) .style('visibility', config.axis_y2_show ? 'visible' : 'hidden'); $$.axes.y2 .append('text') .attr('class', CLASS.axisY2Label) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForY2AxisLabel.bind(this)); }; Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { var $$ = this.owner, config = $$.config, axisParams = { isCategory: $$.isCategorized(), withOuterTick: withOuterTick, tickMultiline: config.axis_x_tick_multiline, tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0, tickWidth: config.axis_x_tick_width, tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, withoutTransition: withoutTransition }, axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient); if ($$.isTimeSeries() && tickValues && typeof tickValues !== 'function') { tickValues = tickValues.map(function (v) { return $$.parseDate(v); }); } // Set tick axis.tickFormat(tickFormat).tickValues(tickValues); if ($$.isCategorized()) { axis.tickCentered(config.axis_x_tick_centered); if (isEmpty(config.axis_x_tick_culling)) { config.axis_x_tick_culling = false; } } return axis; }; Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) { var $$ = this.owner, config = $$.config, tickValues; if (config.axis_x_tick_fit || config.axis_x_tick_count) { tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); } if (axis) { axis.tickValues(tickValues); } else { $$.xAxis.tickValues(tickValues); $$.subXAxis.tickValues(tickValues); } return tickValues; }; Axis.prototype.getYAxis = function getYAxis(axisId, scale, orient, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { var $$ = this.owner; var config = $$.config; var tickFormat = config["axis_" + axisId + "_tick_format"]; if (!tickFormat && $$.isAxisNormalized(axisId)) { tickFormat = function (x) { return x + "%"; }; } var axis = new this.internal(this, { withOuterTick: withOuterTick, withoutTransition: withoutTransition, tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate }).axis .scale(scale) .orient(orient); if (tickFormat) { axis.tickFormat(tickFormat); } if ($$.isTimeSeriesY()) { axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval); } else { axis.tickValues(tickValues); } return axis; }; Axis.prototype.getId = function getId(id) { var config = this.owner.config; return id in config.data_axes ? config.data_axes[id] : 'y'; }; Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { // #2251 previously set any negative values to a whole number, // however both should be truncated according to the users format specification var $$ = this.owner, config = $$.config; var format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v; }; if (config.axis_x_tick_format) { if (isFunction(config.axis_x_tick_format)) { format = config.axis_x_tick_format; } else if ($$.isTimeSeries()) { format = function (date) { return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : ''; }; } } return isFunction(format) ? function (v) { return format.call($$, v); } : format; }; Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { return tickValues ? tickValues : axis ? axis.tickValues() : undefined; }; Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis); }; Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis); }; Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis); }; Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) { var $$ = this.owner, config = $$.config, option; if (axisId === 'y') { option = config.axis_y_label; } else if (axisId === 'y2') { option = config.axis_y2_label; } else if (axisId === 'x') { option = config.axis_x_label; } return option; }; Axis.prototype.getLabelText = function getLabelText(axisId) { var option = this.getLabelOptionByAxisId(axisId); return isString(option) ? option : option ? option.text : null; }; Axis.prototype.setLabelText = function setLabelText(axisId, text) { var $$ = this.owner, config = $$.config, option = this.getLabelOptionByAxisId(axisId); if (isString(option)) { if (axisId === 'y') { config.axis_y_label = text; } else if (axisId === 'y2') { config.axis_y2_label = text; } else if (axisId === 'x') { config.axis_x_label = text; } } else if (option) { option.text = text; } }; Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) { var option = this.getLabelOptionByAxisId(axisId), position = option && typeof option === 'object' && option.position ? option.position : defaultPosition; return { isInner: position.indexOf('inner') >= 0, isOuter: position.indexOf('outer') >= 0, isLeft: position.indexOf('left') >= 0, isCenter: position.indexOf('center') >= 0, isRight: position.indexOf('right') >= 0, isTop: position.indexOf('top') >= 0, isMiddle: position.indexOf('middle') >= 0, isBottom: position.indexOf('bottom') >= 0 }; }; Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right'); }; Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); }; Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); }; Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); }; Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { return this.getLabelText('x'); }; Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { return this.getLabelText('y'); }; Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { return this.getLabelText('y2'); }; Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { var $$ = this.owner; if (forHorizontal) { return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; } else { return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; } }; Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) { if (forHorizontal) { return position.isLeft ? '0.5em' : position.isRight ? '-0.5em' : '0'; } else { return position.isTop ? '-0.5em' : position.isBottom ? '0.5em' : '0'; } }; Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) { if (forHorizontal) { return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; } else { return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; } }; Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { var $$ = this.owner, config = $$.config, position = this.getXAxisLabelPosition(); if (config.axis_rotated) { return position.isInner ? '1.2em' : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x')); } else { return position.isInner ? '-0.5em' : $$.getHorizontalAxisHeight('x') - 10; } }; Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { var $$ = this.owner, position = this.getYAxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '-0.5em' : '3em'; } else { return position.isInner ? '1.2em' : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10); } }; Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { var $$ = this.owner, position = this.getY2AxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '1.2em' : '-2.2em'; } else { return position.isInner ? '-0.5em' : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15); } }; Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) { var $$ = this.owner, maxWidth = 0, targetsToShow, scale, axis, dummy, svg; if (withoutRecompute && $$.currentMaxTickWidths[id]) { return $$.currentMaxTickWidths[id]; } if ($$.svg) { targetsToShow = $$.filterTargetsToShow($$.data.targets); if (id === 'y') { scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); axis = this.getYAxis(id, scale, $$.yOrient, $$.yAxisTickValues, false, true, true); } else if (id === 'y2') { scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); axis = this.getYAxis(id, scale, $$.y2Orient, $$.y2AxisTickValues, false, true, true); } else { scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true); this.updateXAxisTickValues(targetsToShow, axis); } dummy = $$.d3 .select('body') .append('div') .classed('c3', true); (svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0)), svg .append('g') .call(axis) .each(function () { $$.d3 .select(this) .selectAll('text') .each(function () { var box = getBBox(this); if (maxWidth < box.width) { maxWidth = box.width; } }); dummy.remove(); }); } $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; return $$.currentMaxTickWidths[id]; }; Axis.prototype.updateLabels = function updateLabels(withTransition) { var $$ = this.owner; var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); (withTransition ? axisXLabel.transition() : axisXLabel) .attr('x', this.xForXAxisLabel.bind(this)) .attr('dx', this.dxForXAxisLabel.bind(this)) .attr('dy', this.dyForXAxisLabel.bind(this)) .text(this.textForXAxisLabel.bind(this)); (withTransition ? axisYLabel.transition() : axisYLabel) .attr('x', this.xForYAxisLabel.bind(this)) .attr('dx', this.dxForYAxisLabel.bind(this)) .attr('dy', this.dyForYAxisLabel.bind(this)) .text(this.textForYAxisLabel.bind(this)); (withTransition ? axisY2Label.transition() : axisY2Label) .attr('x', this.xForY2AxisLabel.bind(this)) .attr('dx', this.dxForY2AxisLabel.bind(this)) .attr('dy', this.dyForY2AxisLabel.bind(this)) .text(this.textForY2AxisLabel.bind(this)); }; Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) { var p = typeof padding === 'number' ? padding : padding[key]; if (!isValue(p)) { return defaultValue; } if (padding.unit === 'ratio') { return padding[key] * domainLength; } // assume padding is pixels if unit is not specified return this.convertPixelsToAxisPadding(p, domainLength); }; Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) { var $$ = this.owner, length = $$.config.axis_rotated ? $$.width : $$.height; return domainLength * (pixels / length); }; Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) { var tickValues = values, targetCount, start, end, count, interval, i, tickValue; if (tickCount) { targetCount = isFunction(tickCount) ? tickCount() : tickCount; // compute ticks according to tickCount if (targetCount === 1) { tickValues = [values[0]]; } else if (targetCount === 2) { tickValues = [values[0], values[values.length - 1]]; } else if (targetCount > 2) { count = targetCount - 2; start = values[0]; end = values[values.length - 1]; interval = (end - start) / (count + 1); // re-construct unique values tickValues = [start]; for (i = 0; i < count; i++) { tickValue = +start + interval * (i + 1); tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); } tickValues.push(end); } } if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); } return tickValues; }; Axis.prototype.generateTransitions = function generateTransitions(duration) { var $$ = this.owner, axes = $$.axes; return { axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx }; }; Axis.prototype.redraw = function redraw(duration, isHidden) { var $$ = this.owner, transition = duration ? $$.d3.transition().duration(duration) : null; $$.axes.x.style('opacity', isHidden ? 0 : 1).call($$.xAxis, transition); $$.axes.y.style('opacity', isHidden ? 0 : 1).call($$.yAxis, transition); $$.axes.y2.style('opacity', isHidden ? 0 : 1).call($$.y2Axis, transition); $$.axes.subx.style('opacity', isHidden ? 0 : 1).call($$.subXAxis, transition); }; var c3 = { version: '0.7.20', chart: { fn: Chart.prototype, internal: { fn: ChartInternal.prototype, axis: { fn: AxisClass.prototype, internal: { fn: AxisInternal.prototype } } } }, generate: function (config) { return new Chart(config); } }; ChartInternal.prototype.beforeInit = function () { // can do something }; ChartInternal.prototype.afterInit = function () { // can do something }; ChartInternal.prototype.init = function () { var $$ = this, config = $$.config; $$.initParams(); if (config.data_url) { $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData); } else if (config.data_json) { $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); } else if (config.data_rows) { $$.initWithData($$.convertRowsToData(config.data_rows)); } else if (config.data_columns) { $$.initWithData($$.convertColumnsToData(config.data_columns)); } else { throw Error('url or json or rows or columns is required.'); } }; ChartInternal.prototype.initParams = function () { var $$ = this, d3 = $$.d3, config = $$.config; // MEMO: clipId needs to be unique because it conflicts when multiple charts exist $$.clipId = 'c3-' + new Date().valueOf() + '-clip'; $$.clipIdForXAxis = $$.clipId + '-xaxis'; $$.clipIdForYAxis = $$.clipId + '-yaxis'; $$.clipIdForGrid = $$.clipId + '-grid'; $$.clipIdForSubchart = $$.clipId + '-subchart'; $$.clipPath = $$.getClipPath($$.clipId); $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis); $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid); $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart); $$.dragStart = null; $$.dragging = false; $$.flowing = false; $$.cancelClick = false; $$.mouseover = undefined; $$.transiting = false; $$.color = $$.generateColor(); $$.levelColor = $$.generateLevelColor(); $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)($$.config.data_xFormat); $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat; $$.defaultAxisTimeFormat = function (date) { if (date.getMilliseconds()) { return d3.timeFormat('.%L')(date); } if (date.getSeconds()) { return d3.timeFormat(':%S')(date); } if (date.getMinutes()) { return d3.timeFormat('%I:%M')(date); } if (date.getHours()) { return d3.timeFormat('%I %p')(date); } if (date.getDay() && date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date); } if (date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date); } if (date.getMonth()) { return d3.timeFormat('%-m/%-d')(date); } return d3.timeFormat('%Y/%-m/%-d')(date); }; $$.hiddenTargetIds = []; $$.hiddenLegendIds = []; $$.focusedTargetIds = []; $$.defocusedTargetIds = []; $$.xOrient = config.axis_rotated ? config.axis_x_inner ? 'right' : 'left' : config.axis_x_inner ? 'top' : 'bottom'; $$.yOrient = config.axis_rotated ? config.axis_y_inner ? 'top' : 'bottom' : config.axis_y_inner ? 'right' : 'left'; $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? 'bottom' : 'top' : config.axis_y2_inner ? 'left' : 'right'; $$.subXOrient = config.axis_rotated ? 'left' : 'bottom'; $$.isLegendRight = config.legend_position === 'right'; $$.isLegendInset = config.legend_position === 'inset'; $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; $$.legendStep = 0; $$.legendItemWidth = 0; $$.legendItemHeight = 0; $$.currentMaxTickWidths = { x: 0, y: 0, y2: 0 }; $$.rotated_padding_left = 30; $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; $$.rotated_padding_top = 5; $$.withoutFadeIn = {}; $$.intervalForObserveInserted = undefined; $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js }; ChartInternal.prototype.initChartElements = function () { if (this.initBar) { this.initBar(); } if (this.initLine) { this.initLine(); } if (this.initArc) { this.initArc(); } if (this.initGauge) { this.initGauge(); } if (this.initText) { this.initText(); } }; ChartInternal.prototype.initWithData = function (data) { var $$ = this, d3 = $$.d3, config = $$.config; var defs, main, binding = true; $$.axis = new AxisClass($$); if (!config.bindto) { $$.selectChart = d3.selectAll([]); } else if (typeof config.bindto.node === 'function') { $$.selectChart = config.bindto; } else { $$.selectChart = d3.select(config.bindto); } if ($$.selectChart.empty()) { $$.selectChart = d3 .select(document.createElement('div')) .style('opacity', 0); $$.observeInserted($$.selectChart); binding = false; } $$.selectChart.html('').classed('c3', true); // Init data as targets $$.data.xs = {}; $$.data.targets = $$.convertDataToTargets(data); if (config.data_filter) { $$.data.targets = $$.data.targets.filter(config.data_filter); } // Set targets to hide if needed if (config.data_hide) { $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide); } if (config.legend_hide) { $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide); } if ($$.isStanfordGraphType()) { $$.initStanfordData(); } // Init sizes and scales $$.updateSizes(); $$.updateScales(); // Set domains for each scale $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); $$.y.domain($$.getYDomain($$.data.targets, 'y')); $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); $$.subX.domain($$.x.domain()); $$.subY.domain($$.y.domain()); $$.subY2.domain($$.y2.domain()); // Save original x domain for zoom update $$.orgXDomain = $$.x.domain(); /*-- Basic Elements --*/ // Define svgs $$.svg = $$.selectChart .append('svg') .style('overflow', 'hidden') .on('mouseenter', function () { return config.onmouseover.call($$); }) .on('mouseleave', function () { return config.onmouseout.call($$); }); if ($$.config.svg_classname) { $$.svg.attr('class', $$.config.svg_classname); } // Define defs defs = $$.svg.append('defs'); $$.clipChart = $$.appendClip(defs, $$.clipId); $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); $$.updateSvgSize(); // Define regions main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main')); if ($$.initPie) { $$.initPie(); } if ($$.initDragZoom) { $$.initDragZoom(); } if (config.subchart_show && $$.initSubchart) { $$.initSubchart(); } if ($$.initTooltip) { $$.initTooltip(); } if ($$.initLegend) { $$.initLegend(); } if ($$.initTitle) { $$.initTitle(); } if ($$.initZoom) { $$.initZoom(); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // Update selection based on size and scale // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart. if (config.subchart_show && $$.initSubchartBrush) { $$.initSubchartBrush(); } /*-- Main Region --*/ // text when empty main .append('text') .attr('class', CLASS.text + ' ' + CLASS.empty) .attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers. .attr('dominant-baseline', 'middle'); // vertical centering of text at y position in all browsers, except IE. // Regions $$.initRegion(); // Grids $$.initGrid(); // Define g for chart area main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.chart); // Grid lines if (config.grid_lines_front) { $$.initGridLines(); } $$.initStanfordElements(); // Cover whole with rects for events $$.initEventRect(); // Define g for chart $$.initChartElements(); // Add Axis $$.axis.init(); // Set targets $$.updateTargets($$.data.targets); // Set default extent if defined if (config.axis_x_selection) { $$.brush.selectionAsValue($$.getDefaultSelection()); } // Draw with targets if (binding) { $$.updateDimension(); $$.config.oninit.call($$); $$.redraw({ withTransition: false, withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransitionForAxis: false }); } // Bind to resize event $$.bindResize(); // Bind to window focus event $$.bindWindowFocus(); // export element of the chart $$.api.element = $$.selectChart.node(); }; ChartInternal.prototype.smoothLines = function (el, type) { var $$ = this; if (type === 'grid') { el.each(function () { var g = $$.d3.select(this), x1 = g.attr('x1'), x2 = g.attr('x2'), y1 = g.attr('y1'), y2 = g.attr('y2'); g.attr({ x1: Math.ceil(x1), x2: Math.ceil(x2), y1: Math.ceil(y1), y2: Math.ceil(y2) }); }); } }; ChartInternal.prototype.updateSizes = function () { var $$ = this, config = $$.config; var legendHeight = $$.legend ? $$.getLegendHeight() : 0, legendWidth = $$.legend ? $$.getLegendWidth() : 0, legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, hasArc = $$.hasArcType(), xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), subchartXAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x', true), subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + subchartXAxisHeight : 0; $$.currentWidth = $$.getCurrentWidth(); $$.currentHeight = $$.getCurrentHeight(); // for main $$.margin = config.axis_rotated ? { top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) } : { top: 4 + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: hasArc ? 0 : $$.getCurrentPaddingLeft() }; // for subchart $$.margin2 = config.axis_rotated ? { top: $$.margin.top, right: NaN, bottom: 20 + legendHeightForBottom, left: $$.rotated_padding_left } : { top: $$.currentHeight - subchartHeight - legendHeightForBottom, right: NaN, bottom: subchartXAxisHeight + legendHeightForBottom, left: $$.margin.left }; // for legend $$.margin3 = { top: 0, right: NaN, bottom: 0, left: 0 }; if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); } $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; if ($$.width < 0) { $$.width = 0; } if ($$.height < 0) { $$.height = 0; } $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; if ($$.width2 < 0) { $$.width2 = 0; } if ($$.height2 < 0) { $$.height2 = 0; } // for arc $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); if ($$.hasType('gauge') && !config.gauge_fullCircle) { $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); } if ($$.updateRadius) { $$.updateRadius(); } if ($$.isLegendRight && hasArc) { $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; } }; ChartInternal.prototype.updateTargets = function (targets) { var $$ = this, config = $$.config; /*-- Main --*/ //-- Text --// $$.updateTargetsForText(targets); //-- Bar --// $$.updateTargetsForBar(targets); //-- Line --// $$.updateTargetsForLine(targets); //-- Arc --// if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); } /*-- Sub --*/ if (config.subchart_show && $$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); } // Fade-in each chart $$.showTargets(); }; ChartInternal.prototype.showTargets = function () { var $$ = this; $$.svg .selectAll('.' + CLASS.target) .filter(function (d) { return $$.isTargetToShow(d.id); }) .transition() .duration($$.config.transition_duration) .style('opacity', 1); }; ChartInternal.prototype.redraw = function (options, transitions) { var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config; var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType); var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis; var hideAxis = $$.hasArcType(); var drawArea, drawBar, drawLine, xForText, yForText; var duration, durationForExit, durationForAxis; var transitionsToWait, waitForDraw, flow, transition; var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom; var xv = $$.xv.bind($$), cx, cy; options = options || {}; withY = getOption(options, 'withY', true); withSubchart = getOption(options, 'withSubchart', true); withTransition = getOption(options, 'withTransition', true); withTransform = getOption(options, 'withTransform', false); withUpdateXDomain = getOption(options, 'withUpdateXDomain', false); withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', false); withTrimXDomain = getOption(options, 'withTrimXDomain', true); withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain); withLegend = getOption(options, 'withLegend', false); withEventRect = getOption(options, 'withEventRect', true); withDimension = getOption(options, 'withDimension', true); withTransitionForExit = getOption(options, 'withTransitionForExit', withTransition); withTransitionForAxis = getOption(options, 'withTransitionForAxis', withTransition); duration = withTransition ? config.transition_duration : 0; durationForExit = withTransitionForExit ? duration : 0; durationForAxis = withTransitionForAxis ? duration : 0; transitions = transitions || $$.axis.generateTransitions(durationForAxis); // update legend and transform each g if (withLegend && config.legend_show) { $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); } else if (withDimension) { // need to update dimension (e.g. axis.y.tick.values) because y tick values should change // no need to update axis in it because they will be updated in redraw() $$.updateDimension(true); } // MEMO: needed for grids calculation if ($$.isCategorized() && targetsToShow.length === 0) { $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); } if (targetsToShow.length) { $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain); if (!config.axis_x_tick_values) { tickValues = $$.axis.updateXAxisTickValues(targetsToShow); } } else { $$.xAxis.tickValues([]); $$.subXAxis.tickValues([]); } if (config.zoom_rescale && !options.flow) { xDomainForZoom = $$.x.orgDomain(); } $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); if (!config.axis_y_tick_values && config.axis_y_tick_count) { $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count)); } if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)); } // axes $$.axis.redraw(durationForAxis, hideAxis); // Update axis label $$.axis.updateLabels(withTransition); // show/hide if manual culling needed if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if (config.axis_x_tick_culling && tickValues) { for (i = 1; i < tickValues.length; i++) { if (tickValues.length / i < config.axis_x_tick_culling_max) { intervalForCulling = i; break; } } $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) { var index = tickValues.indexOf(e); if (index >= 0) { d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block'); } }); } else { $$.svg .selectAll('.' + CLASS.axisX + ' .tick text') .style('display', 'block'); } } // setup drawer - MEMO: these must be called after axis updated drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); // update circleY based on updated parameters $$.updateCircleY(); // generate circle x/y functions depending on updated params cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); // Update sub domain if (withY) { $$.subY.domain($$.getYDomain(targetsToShow, 'y')); $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); } // xgrid focus $$.updateXgridFocus(); // Data empty label positioning and text. main .select('text.' + CLASS.text + '.' + CLASS.empty) .attr('x', $$.width / 2) .attr('y', $$.height / 2) .text(config.data_empty_label_text) .transition() .style('opacity', targetsToShow.length ? 0 : 1); // event rect if (withEventRect) { $$.redrawEventRect(); } // grid $$.updateGrid(duration); $$.updateStanfordElements(duration); // rect for regions $$.updateRegion(duration); // bars $$.updateBar(durationForExit); // lines, areas and circles $$.updateLine(durationForExit); $$.updateArea(durationForExit); $$.updateCircle(cx, cy); // text if ($$.hasDataLabel()) { $$.updateText(xForText, yForText, durationForExit); } // title if ($$.redrawTitle) { $$.redrawTitle(); } // arc if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } // subchart if (config.subchart_show && $$.redrawSubchart) { $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // circles for select main .selectAll('.' + CLASS.selectedCircles) .filter($$.isBarType.bind($$)) .selectAll('circle') .remove(); if (options.flow) { flow = $$.generateFlow({ targets: targetsToShow, flow: options.flow, duration: options.flow.duration, drawBar: drawBar, drawLine: drawLine, drawArea: drawArea, cx: cx, cy: cy, xv: xv, xForText: xForText, yForText: yForText }); } if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. // transition should be derived from one transition transition = d3.transition().duration(duration); transitionsToWait = []; [ $$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition) ].forEach(function (transitions) { transitions.forEach(function (transition) { transitionsToWait.push(transition); }); }); // Wait for end of transitions to call flow and onrendered callback waitForDraw = $$.generateWait(); transitionsToWait.forEach(function (t) { waitForDraw.add(t); }); waitForDraw(function () { if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } }); } else { $$.redrawBar(drawBar); $$.redrawLine(drawLine); $$.redrawArea(drawArea); $$.redrawCircle(cx, cy); $$.redrawText(xForText, yForText, options.flow); $$.redrawRegion(); $$.redrawGrid(); if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } } // update fadein condition $$.mapToIds($$.data.targets).forEach(function (id) { $$.withoutFadeIn[id] = true; }); }; ChartInternal.prototype.updateAndRedraw = function (options) { var $$ = this, config = $$.config, transitions; options = options || {}; // same with redraw options.withTransition = getOption(options, 'withTransition', true); options.withTransform = getOption(options, 'withTransform', false); options.withLegend = getOption(options, 'withLegend', false); // NOT same with redraw options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true); options.withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', true); options.withTransitionForExit = false; options.withTransitionForTransform = getOption(options, 'withTransitionForTransform', options.withTransition); // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) $$.updateSizes(); // MEMO: called in updateLegend in redraw if withLegend if (!(options.withLegend && config.legend_show)) { transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0); // Update scales $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(options.withTransitionForTransform, transitions); } // Draw with new sizes & scales $$.redraw(options, transitions); }; ChartInternal.prototype.redrawWithoutRescale = function () { this.redraw({ withY: false, withSubchart: false, withEventRect: false, withTransitionForAxis: false }); }; ChartInternal.prototype.isTimeSeries = function () { return this.config.axis_x_type === 'timeseries'; }; ChartInternal.prototype.isCategorized = function () { return this.config.axis_x_type.indexOf('categor') >= 0; }; ChartInternal.prototype.isCustomX = function () { var $$ = this, config = $$.config; return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)); }; ChartInternal.prototype.isTimeSeriesY = function () { return this.config.axis_y_type === 'timeseries'; }; ChartInternal.prototype.getTranslate = function (target) { var $$ = this, config = $$.config, x, y; if (target === 'main') { x = asHalfPixel($$.margin.left); y = asHalfPixel($$.margin.top); } else if (target === 'context') { x = asHalfPixel($$.margin2.left); y = asHalfPixel($$.margin2.top); } else if (target === 'legend') { x = $$.margin3.left; y = $$.margin3.top; } else if (target === 'x') { x = 0; y = config.axis_rotated ? 0 : $$.height; } else if (target === 'y') { x = 0; y = config.axis_rotated ? $$.height : 0; } else if (target === 'y2') { x = config.axis_rotated ? 0 : $$.width; y = config.axis_rotated ? 1 : 0; } else if (target === 'subx') { x = 0; y = config.axis_rotated ? 0 : $$.height2; } else if (target === 'arc') { x = $$.arcWidth / 2; y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label } return 'translate(' + x + ',' + y + ')'; }; ChartInternal.prototype.initialOpacity = function (d) { return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0; }; ChartInternal.prototype.initialOpacityForCircle = function (d) { return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0; }; ChartInternal.prototype.opacityForCircle = function (d) { var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show; var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0; return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0; }; ChartInternal.prototype.opacityForText = function () { return this.hasDataLabel() ? 1 : 0; }; ChartInternal.prototype.xx = function (d) { return d ? this.x(d.x) : null; }; ChartInternal.prototype.xvCustom = function (d, xyValue) { var $$ = this, value = xyValue ? d[xyValue] : d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)); }; ChartInternal.prototype.yvCustom = function (d, xyValue) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y, value = xyValue ? d[xyValue] : d.value; return Math.ceil(yScale(value)); }; ChartInternal.prototype.xv = function (d) { var $$ = this, value = d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)); }; ChartInternal.prototype.yv = function (d) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; return Math.ceil(yScale(d.value)); }; ChartInternal.prototype.subxx = function (d) { return d ? this.subX(d.x) : null; }; ChartInternal.prototype.transformMain = function (withTransition, transitions) { var $$ = this, xAxis, yAxis, y2Axis; if (transitions && transitions.axisX) { xAxis = transitions.axisX; } else { xAxis = $$.main.select('.' + CLASS.axisX); if (withTransition) { xAxis = xAxis.transition(); } } if (transitions && transitions.axisY) { yAxis = transitions.axisY; } else { yAxis = $$.main.select('.' + CLASS.axisY); if (withTransition) { yAxis = yAxis.transition(); } } if (transitions && transitions.axisY2) { y2Axis = transitions.axisY2; } else { y2Axis = $$.main.select('.' + CLASS.axisY2); if (withTransition) { y2Axis = y2Axis.transition(); } } (withTransition ? $$.main.transition() : $$.main).attr('transform', $$.getTranslate('main')); xAxis.attr('transform', $$.getTranslate('x')); yAxis.attr('transform', $$.getTranslate('y')); y2Axis.attr('transform', $$.getTranslate('y2')); $$.main .select('.' + CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); }; ChartInternal.prototype.transformAll = function (withTransition, transitions) { var $$ = this; $$.transformMain(withTransition, transitions); if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); } if ($$.legend) { $$.transformLegend(withTransition); } }; ChartInternal.prototype.updateSvgSize = function () { var $$ = this, brush = $$.svg.select("." + CLASS.brush + " .overlay"); $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); $$.svg .selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]) .select('rect') .attr('width', $$.width) .attr('height', $$.height); $$.svg .select('#' + $$.clipIdForXAxis) .select('rect') .attr('x', $$.getXAxisClipX.bind($$)) .attr('y', $$.getXAxisClipY.bind($$)) .attr('width', $$.getXAxisClipWidth.bind($$)) .attr('height', $$.getXAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForYAxis) .select('rect') .attr('x', $$.getYAxisClipX.bind($$)) .attr('y', $$.getYAxisClipY.bind($$)) .attr('width', $$.getYAxisClipWidth.bind($$)) .attr('height', $$.getYAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForSubchart) .select('rect') .attr('width', $$.width) .attr('height', (brush.size() && brush.attr('height')) || 0); // MEMO: parent div's height will be bigger than svg when $$.selectChart.style('max-height', $$.currentHeight + 'px'); }; ChartInternal.prototype.updateDimension = function (withoutAxis) { var $$ = this; if (!withoutAxis) { if ($$.config.axis_rotated) { $$.axes.x.call($$.xAxis); $$.axes.subx.call($$.subXAxis); } else { $$.axes.y.call($$.yAxis); $$.axes.y2.call($$.y2Axis); } } $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); $$.transformAll(false); }; ChartInternal.prototype.observeInserted = function (selection) { var $$ = this, observer; if (typeof MutationObserver === 'undefined') { window.console.error('MutationObserver not defined.'); return; } observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.type === 'childList' && mutation.previousSibling) { observer.disconnect(); // need to wait for completion of load because size calculation requires the actual sizes determined after that completion $$.intervalForObserveInserted = window.setInterval(function () { // parentNode will NOT be null when completed if (selection.node().parentNode) { window.clearInterval($$.intervalForObserveInserted); $$.updateDimension(); if ($$.brush) { $$.brush.update(); } $$.config.oninit.call($$); $$.redraw({ withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransition: false, withTransitionForTransform: false, withLegend: true }); selection.transition().style('opacity', 1); } }, 10); } }); }); observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }); }; /** * Binds handlers to the window resize event. */ ChartInternal.prototype.bindResize = function () { var $$ = this, config = $$.config; $$.resizeFunction = $$.generateResize(); // need to call .remove $$.resizeFunction.add(function () { config.onresize.call($$); }); if (config.resize_auto) { $$.resizeFunction.add(function () { if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } $$.resizeTimeout = window.setTimeout(function () { delete $$.resizeTimeout; $$.updateAndRedraw({ withUpdateXDomain: false, withUpdateOrgXDomain: false, withTransition: false, withTransitionForTransform: false, withLegend: true }); if ($$.brush) { $$.brush.update(); } }, 100); }); } $$.resizeFunction.add(function () { config.onresized.call($$); }); $$.resizeIfElementDisplayed = function () { // if element not displayed skip it if ($$.api == null || !$$.api.element.offsetParent) { return; } $$.resizeFunction(); }; window.addEventListener('resize', $$.resizeIfElementDisplayed, false); }; /** * Binds handlers to the window focus event. */ ChartInternal.prototype.bindWindowFocus = function () { var _this = this; if (this.windowFocusHandler) { // The handler is already set return; } this.windowFocusHandler = function () { _this.redraw(); }; window.addEventListener('focus', this.windowFocusHandler); }; /** * Unbinds from the window focus event. */ ChartInternal.prototype.unbindWindowFocus = function () { window.removeEventListener('focus', this.windowFocusHandler); delete this.windowFocusHandler; }; ChartInternal.prototype.generateResize = function () { var resizeFunctions = []; function callResizeFunctions() { resizeFunctions.forEach(function (f) { f(); }); } callResizeFunctions.add = function (f) { resizeFunctions.push(f); }; callResizeFunctions.remove = function (f) { for (var i = 0; i < resizeFunctions.length; i++) { if (resizeFunctions[i] === f) { resizeFunctions.splice(i, 1); break; } } }; return callResizeFunctions; }; ChartInternal.prototype.endall = function (transition, callback) { var n = 0; transition .each(function () { ++n; }) .on('end', function () { if (!--n) { callback.apply(this, arguments); } }); }; ChartInternal.prototype.generateWait = function () { var $$ = this; var transitionsToWait = [], f = function (callback) { var timer = setInterval(function () { if (!$$.isTabVisible()) { return; } var done = 0; transitionsToWait.forEach(function (t) { if (t.empty()) { done += 1; return; } try { t.transition(); } catch (e) { done += 1; } }); if (done === transitionsToWait.length) { clearInterval(timer); if (callback) { callback(); } } }, 50); }; f.add = function (transition) { transitionsToWait.push(transition); }; return f; }; ChartInternal.prototype.parseDate = function (date) { var $$ = this, parsedDate; if (date instanceof Date) { parsedDate = date; } else if (typeof date === 'string') { parsedDate = $$.dataTimeParse(date); } else if (typeof date === 'object') { parsedDate = new Date(+date); } else if (typeof date === 'number' && !isNaN(date)) { parsedDate = new Date(+date); } if (!parsedDate || isNaN(+parsedDate)) { window.console.error("Failed to parse x '" + date + "' to Date object"); } return parsedDate; }; ChartInternal.prototype.isTabVisible = function () { return !document.hidden; }; ChartInternal.prototype.getPathBox = getPathBox; ChartInternal.prototype.CLASS = CLASS; /* jshint ignore:start */ (function () { if (!('SVGPathSeg' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) { this.pathSegType = type; this.pathSegTypeAsLetter = typeAsLetter; this._owningPathSegList = owningPathSegList; }; window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; window.SVGPathSeg.PATHSEG_UNKNOWN = 0; window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; window.SVGPathSeg.PATHSEG_LINETO_REL = 5; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; window.SVGPathSeg.PATHSEG_ARC_ABS = 10; window.SVGPathSeg.PATHSEG_ARC_REL = 11; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; // Notify owning PathSegList on any changes so they can be synchronized back to the path element. window.SVGPathSeg.prototype._segmentChanged = function () { if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this); }; window.SVGPathSegClosePath = function (owningPathSegList) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList); }; window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegClosePath.prototype.toString = function () { return '[object SVGPathSegClosePath]'; }; window.SVGPathSegClosePath.prototype._asPathString = function () { return this.pathSegTypeAsLetter; }; window.SVGPathSegClosePath.prototype.clone = function () { return new window.SVGPathSegClosePath(undefined); }; window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegMovetoAbs.prototype.toString = function () { return '[object SVGPathSegMovetoAbs]'; }; window.SVGPathSegMovetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegMovetoAbs.prototype.clone = function () { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegMovetoRel.prototype.toString = function () { return '[object SVGPathSegMovetoRel]'; }; window.SVGPathSegMovetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegMovetoRel.prototype.clone = function () { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoAbs.prototype.toString = function () { return '[object SVGPathSegLinetoAbs]'; }; window.SVGPathSegLinetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegLinetoAbs.prototype.clone = function () { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoRel.prototype.toString = function () { return '[object SVGPathSegLinetoRel]'; }; window.SVGPathSegLinetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegLinetoRel.prototype.clone = function () { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicAbs]'; }; window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicRel]'; }; window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticAbs]'; }; window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticRel]'; }; window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegArcAbs.prototype.toString = function () { return '[object SVGPathSegArcAbs]'; }; window.SVGPathSegArcAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegArcAbs.prototype.clone = function () { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegArcRel.prototype.toString = function () { return '[object SVGPathSegArcRel]'; }; window.SVGPathSegArcRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegArcRel.prototype.clone = function () { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList); this._x = x; }; window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalAbs]'; }; window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }; Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList); this._x = x; }; window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalRel]'; }; window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }; Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList); this._y = y; }; window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalAbs]'; }; window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }; Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList); this._y = y; }; window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoVerticalRel.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalRel]'; }; window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; window.SVGPathSegLinetoVerticalRel.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }; Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothAbs]'; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothRel]'; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]'; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothRel]'; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); // Add createSVGPathSeg* functions to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. window.SVGPathElement.prototype.createSVGPathSegClosePath = function () { return new window.SVGPathSegClosePath(undefined); }; window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }; window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }; if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { // Add getPathSegAtLength to SVGPathElement. // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. window.SVGPathElement.prototype.getPathSegAtLength = function (distance) { if (distance === undefined || !isFinite(distance)) throw 'Invalid arguments.'; var measurementElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); measurementElement.setAttribute('d', this.getAttribute('d')); var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; // If the path is empty, return 0. if (lastPathSegment <= 0) return 0; do { measurementElement.pathSegList.removeItem(lastPathSegment); if (distance > measurementElement.getTotalLength()) break; lastPathSegment--; } while (lastPathSegment > 0); return lastPathSegment; }; } } if (!('SVGPathSegList' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList window.SVGPathSegList = function (pathElement) { this._pathElement = pathElement; this._list = this._parsePath(this._pathElement.getAttribute('d')); // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { attributes: true, attributeFilter: ['d'] }; this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); }; window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { get: function () { this._checkPathSynchronizedToList(); return this._list.length; }, enumerable: true }); // Add the pathSegList accessors to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { get: function () { if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this); return this._pathSegList; }, enumerable: true }); // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. Object.defineProperty(window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); Object.defineProperty(window.SVGPathElement.prototype, 'animatedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); Object.defineProperty(window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); // Process any pending mutations to the path element and update the list as needed. // This should be the first call of all public functions and is needed because // MutationObservers are not synchronous so we can have pending asynchronous mutations. window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () { this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); }; window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) { if (!this._pathElement) return; var hasPathMutations = false; mutationRecords.forEach(function (record) { if (record.attributeName == 'd') hasPathMutations = true; }); if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute('d')); }; // Serialize the list and update the path's 'd' attribute. window.SVGPathSegList.prototype._writeListToPath = function () { this._pathElementMutationObserver.disconnect(); this._pathElement.setAttribute('d', window.SVGPathSegList._pathSegArrayAsString(this._list)); this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); }; // When a path segment changes the list needs to be synchronized back to the path element. window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) { this._writeListToPath(); }; window.SVGPathSegList.prototype.clear = function () { this._checkPathSynchronizedToList(); this._list.forEach(function (pathSeg) { pathSeg._owningPathSegList = null; }); this._list = []; this._writeListToPath(); }; window.SVGPathSegList.prototype.initialize = function (newItem) { this._checkPathSynchronizedToList(); this._list = [newItem]; newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype._checkValidIndex = function (index) { if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw 'INDEX_SIZE_ERR'; }; window.SVGPathSegList.prototype.getItem = function (index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); return this._list[index]; }; window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) { this._checkPathSynchronizedToList(); // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. if (index > this.numberOfItems) index = this.numberOfItems; if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.splice(index, 0, newItem); newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype.replaceItem = function (newItem, index) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._checkValidIndex(index); this._list[index] = newItem; newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype.removeItem = function (index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); var item = this._list[index]; this._list.splice(index, 1); this._writeListToPath(); return item; }; window.SVGPathSegList.prototype.appendItem = function (newItem) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.push(newItem); newItem._owningPathSegList = this; // TODO: Optimize this to just append to the existing attribute. this._writeListToPath(); return newItem; }; window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) { var string = ''; var first = true; pathSegArray.forEach(function (pathSeg) { if (first) { first = false; string += pathSeg._asPathString(); } else { string += ' ' + pathSeg._asPathString(); } }); return string; }; // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. window.SVGPathSegList.prototype._parsePath = function (string) { if (!string || string.length == 0) return []; var owningPathSegList = this; var Builder = function () { this.pathSegList = []; }; Builder.prototype.appendSegment = function (pathSeg) { this.pathSegList.push(pathSeg); }; var Source = function (string) { this._string = string; this._currentIndex = 0; this._endIndex = this._string.length; this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; this._skipOptionalSpaces(); }; Source.prototype._isCurrentSpace = function () { var character = this._string[this._currentIndex]; return (character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f')); }; Source.prototype._skipOptionalSpaces = function () { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) this._currentIndex++; return this._currentIndex < this._endIndex; }; Source.prototype._skipOptionalSpacesOrDelimiter = function () { if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ',') return false; if (this._skipOptionalSpaces()) { if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ',') { this._currentIndex++; this._skipOptionalSpaces(); } } return this._currentIndex < this._endIndex; }; Source.prototype.hasMoreData = function () { return this._currentIndex < this._endIndex; }; Source.prototype.peekSegmentType = function () { var lookahead = this._string[this._currentIndex]; return this._pathSegTypeFromChar(lookahead); }; Source.prototype._pathSegTypeFromChar = function (lookahead) { switch (lookahead) { case 'Z': case 'z': return window.SVGPathSeg.PATHSEG_CLOSEPATH; case 'M': return window.SVGPathSeg.PATHSEG_MOVETO_ABS; case 'm': return window.SVGPathSeg.PATHSEG_MOVETO_REL; case 'L': return window.SVGPathSeg.PATHSEG_LINETO_ABS; case 'l': return window.SVGPathSeg.PATHSEG_LINETO_REL; case 'C': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; case 'c': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; case 'Q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; case 'q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; case 'A': return window.SVGPathSeg.PATHSEG_ARC_ABS; case 'a': return window.SVGPathSeg.PATHSEG_ARC_REL; case 'H': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; case 'h': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; case 'V': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; case 'v': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; case 'S': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; case 's': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; case 'T': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; case 't': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; default: return window.SVGPathSeg.PATHSEG_UNKNOWN; } }; Source.prototype._nextCommandHelper = function (lookahead, previousCommand) { // Check for remaining coordinates in the current command. if ((lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) { if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS; if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL; return previousCommand; } return window.SVGPathSeg.PATHSEG_UNKNOWN; }; Source.prototype.initialCommandIsMoveTo = function () { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) return true; var command = this.peekSegmentType(); // Path must start with moveTo. return (command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL); }; // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF Source.prototype._parseNumber = function () { var exponent = 0; var integer = 0; var frac = 1; var decimal = 0; var sign = 1; var expsign = 1; var startIndex = this._currentIndex; this._skipOptionalSpaces(); // Read the sign. if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '+') this._currentIndex++; else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; sign = -1; } if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) != '.')) // The first character of a number must be one of [0-9+-.]. return undefined; // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') this._currentIndex++; // Advance to first non-digit. if (this._currentIndex != startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1; var multiplier = 1; while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); multiplier *= 10; } } // Read the decimals. if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '.') { this._currentIndex++; // There must be a least one digit following the . if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') return undefined; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { frac *= 10; decimal += (this._string.charAt(this._currentIndex) - '0') / frac; this._currentIndex += 1; } } // Read the exponent part. if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == 'e' || this._string.charAt(this._currentIndex) == 'E') && this._string.charAt(this._currentIndex + 1) != 'x' && this._string.charAt(this._currentIndex + 1) != 'm') { this._currentIndex++; // Read the sign of the exponent. if (this._string.charAt(this._currentIndex) == '+') { this._currentIndex++; } else if (this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; expsign = -1; } // There must be an exponent. if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') return undefined; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { exponent *= 10; exponent += this._string.charAt(this._currentIndex) - '0'; this._currentIndex++; } } var number = integer + decimal; number *= sign; if (exponent) number *= Math.pow(10, expsign * exponent); if (startIndex == this._currentIndex) return undefined; this._skipOptionalSpacesOrDelimiter(); return number; }; Source.prototype._parseArcFlag = function () { if (this._currentIndex >= this._endIndex) return undefined; var flag = false; var flagChar = this._string.charAt(this._currentIndex++); if (flagChar == '0') flag = false; else if (flagChar == '1') flag = true; else return undefined; this._skipOptionalSpacesOrDelimiter(); return flag; }; Source.prototype.parseSegment = function () { var lookahead = this._string[this._currentIndex]; var command = this._pathSegTypeFromChar(lookahead); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { // Possibly an implicit command. Not allowed if this is the first command. if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; command = this._nextCommandHelper(lookahead, this._previousCommand); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; } else { this._currentIndex++; } this._previousCommand = command; switch (command) { case window.SVGPathSeg.PATHSEG_MOVETO_REL: return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_MOVETO_ABS: return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_REL: return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_ABS: return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_CLOSEPATH: this._skipOptionalSpaces(); return new window.SVGPathSegClosePath(owningPathSegList); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_ARC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); case window.SVGPathSeg.PATHSEG_ARC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); default: throw 'Unknown path seg type.'; } }; var builder = new Builder(); var source = new Source(string); if (!source.initialCommandIsMoveTo()) return []; while (source.hasMoreData()) { var pathSeg = source.parseSegment(); if (!pathSeg) return []; builder.appendSegment(pathSeg); } return builder.pathSegList; }; } })(); // String.padEnd polyfill for IE11 // // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== 'undefined' ? padString : ' '); if (this.length > targetLength) { return String(this); } else { targetLength = targetLength - this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed } return String(this) + padString.slice(0, targetLength); } }; } // Object.assign polyfill for IE11 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } /* jshint ignore:end */ Chart.prototype.axis = function () { }; Chart.prototype.axis.labels = function (labels) { var $$ = this.internal; if (arguments.length) { Object.keys(labels).forEach(function (axisId) { $$.axis.setLabelText(axisId, labels[axisId]); }); $$.axis.updateLabels(); } // TODO: return some values? }; Chart.prototype.axis.max = function (max) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof max === 'object') { if (isValue(max.x)) { config.axis_x_max = max.x; } if (isValue(max.y)) { config.axis_y_max = max.y; } if (isValue(max.y2)) { config.axis_y2_max = max.y2; } } else { config.axis_y_max = config.axis_y2_max = max; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_max, y: config.axis_y_max, y2: config.axis_y2_max }; } }; Chart.prototype.axis.min = function (min) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof min === 'object') { if (isValue(min.x)) { config.axis_x_min = min.x; } if (isValue(min.y)) { config.axis_y_min = min.y; } if (isValue(min.y2)) { config.axis_y2_min = min.y2; } } else { config.axis_y_min = config.axis_y2_min = min; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_min, y: config.axis_y_min, y2: config.axis_y2_min }; } }; Chart.prototype.axis.range = function (range) { if (arguments.length) { if (isDefined(range.max)) { this.axis.max(range.max); } if (isDefined(range.min)) { this.axis.min(range.min); } } else { return { max: this.axis.max(), min: this.axis.min() }; } }; Chart.prototype.axis.types = function (types) { var $$ = this.internal; if (types === undefined) { return { y: $$.config.axis_y_type, y2: $$.config.axis_y2_type }; } else { if (isDefined(types.y)) { $$.config.axis_y_type = types.y; } if (isDefined(types.y2)) { $$.config.axis_y2_type = types.y2; } $$.updateScales(); $$.redraw(); } }; Chart.prototype.category = function (i, category) { var $$ = this.internal, config = $$.config; if (arguments.length > 1) { config.axis_x_categories[i] = category; $$.redraw(); } return config.axis_x_categories[i]; }; Chart.prototype.categories = function (categories) { var $$ = this.internal, config = $$.config; if (!arguments.length) { return config.axis_x_categories; } config.axis_x_categories = categories; $$.redraw(); return config.axis_x_categories; }; Chart.prototype.resize = function (size) { var $$ = this.internal, config = $$.config; config.size_width = size ? size.width : null; config.size_height = size ? size.height : null; this.flush(); }; Chart.prototype.flush = function () { var $$ = this.internal; $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); }; Chart.prototype.destroy = function () { var $$ = this.internal; window.clearInterval($$.intervalForObserveInserted); if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } window.removeEventListener('resize', $$.resizeIfElementDisplayed); // Removes the inner resize functions $$.resizeFunction.remove(); // Unbinds from the window focus event $$.unbindWindowFocus(); $$.selectChart.classed('c3', false).html(''); // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. Object.keys($$).forEach(function (key) { $$[key] = null; }); return null; }; // TODO: fix Chart.prototype.color = function (id) { var $$ = this.internal; return $$.color(id); // more patterns }; Chart.prototype.data = function (targetIds) { var targets = this.internal.data.targets; return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) { return [].concat(targetIds).indexOf(t.id) >= 0; }); }; Chart.prototype.data.shown = function (targetIds) { return this.internal.filterTargetsToShow(this.data(targetIds)); }; /** * Get values of the data loaded in the chart. * * @param {String|Array} targetId This API returns the value of specified target. * @param flat * @return {Array} Data values */ Chart.prototype.data.values = function (targetId, flat) { if (flat === void 0) { flat = true; } var values = null; if (targetId) { var targets = this.data(targetId); if (targets && isArray(targets)) { values = targets.reduce(function (ret, v) { var dataValue = v.values.map(function (d) { return d.value; }); if (flat) { ret = ret.concat(dataValue); } else { ret.push(dataValue); } return ret; }, []); } } return values; }; Chart.prototype.data.names = function (names) { this.internal.clearLegendItemTextBoxCache(); return this.internal.updateDataAttributes('names', names); }; Chart.prototype.data.colors = function (colors) { return this.internal.updateDataAttributes('colors', colors); }; Chart.prototype.data.axes = function (axes) { return this.internal.updateDataAttributes('axes', axes); }; Chart.prototype.data.stackNormalized = function (normalized) { if (normalized === undefined) { return this.internal.isStackNormalized(); } this.internal.config.data_stack_normalize = !!normalized; this.internal.redraw(); }; Chart.prototype.donut = function () { }; Chart.prototype.donut.padAngle = function (padAngle) { if (padAngle === undefined) { return this.internal.config.donut_padAngle; } this.internal.config.donut_padAngle = padAngle; this.flush(); }; Chart.prototype.flow = function (args) { var $$ = this.internal, targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; if (args.json) { data = $$.convertJsonToData(args.json, args.keys); } else if (args.rows) { data = $$.convertRowsToData(args.rows); } else if (args.columns) { data = $$.convertColumnsToData(args.columns); } else { return; } targets = $$.convertDataToTargets(data, true); // Update/Add data $$.data.targets.forEach(function (t) { var found = false, i, j; for (i = 0; i < targets.length; i++) { if (t.id === targets[i].id) { found = true; if (t.values[t.values.length - 1]) { tail = t.values[t.values.length - 1].index + 1; } length = targets[i].values.length; for (j = 0; j < length; j++) { targets[i].values[j].index = tail + j; if (!$$.isTimeSeries()) { targets[i].values[j].x = tail + j; } } t.values = t.values.concat(targets[i].values); targets.splice(i, 1); break; } } if (!found) { notfoundIds.push(t.id); } }); // Append null for not found targets $$.data.targets.forEach(function (t) { var i, j; for (i = 0; i < notfoundIds.length; i++) { if (t.id === notfoundIds[i]) { tail = t.values[t.values.length - 1].index + 1; for (j = 0; j < length; j++) { t.values.push({ id: t.id, index: tail + j, x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, value: null }); } } } }); // Generate null values for new target if ($$.data.targets.length) { targets.forEach(function (t) { var i, missing = []; for (i = $$.data.targets[0].values[0].index; i < tail; i++) { missing.push({ id: t.id, index: i, x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, value: null }); } t.values.forEach(function (v) { v.index += tail; if (!$$.isTimeSeries()) { v.x += tail; } }); t.values = missing.concat(t.values); }); } $$.data.targets = $$.data.targets.concat(targets); // add remained // check data count because behavior needs to change when it's only one dataCount = $$.getMaxDataCount(); baseTarget = $$.data.targets[0]; baseValue = baseTarget.values[0]; // Update length to flow if needed if (isDefined(args.to)) { length = 0; to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; baseTarget.values.forEach(function (v) { if (v.x < to) { length++; } }); } else if (isDefined(args.length)) { length = args.length; } // If only one data, update the domain to flow from left edge of the chart if (!orgDataCount) { if ($$.isTimeSeries()) { if (baseTarget.values.length > 1) { diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; } else { diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; } } else { diff = 1; } domain = [baseValue.x - diff, baseValue.x]; $$.updateXDomain(null, true, true, false, domain); } else if (orgDataCount === 1) { if ($$.isTimeSeries()) { diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; $$.updateXDomain(null, true, true, false, domain); } } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ flow: { index: baseValue.index, length: length, duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, done: args.done, orgDataCount: orgDataCount }, withLegend: true, withTransition: orgDataCount > 1, withTrimXDomain: false, withUpdateXAxis: true }); }; ChartInternal.prototype.generateFlow = function (args) { var $$ = this, config = $$.config, d3 = $$.d3; return function () { var targets = args.targets, flow = args.flow, drawBar = args.drawBar, drawLine = args.drawLine, drawArea = args.drawArea, cx = args.cx, cy = args.cy, xv = args.xv, xForText = args.xForText, yForText = args.yForText, duration = args.duration; var translateX, scaleX = 1, transform, flowIndex = flow.index, flowLength = flow.length, flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), orgDomain = $$.x.domain(), domain, durationForFlow = flow.duration || duration, done = flow.done || function () { }, wait = $$.generateWait(); var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle; // set flag $$.flowing = true; // remove head data after rendered $$.data.targets.forEach(function (d) { d.values.splice(0, flowLength); }); // update x domain to generate axis elements for flow domain = $$.updateXDomain(targets, true, true); // update elements related to x scale if ($$.updateXGrid) { $$.updateXGrid(true); } xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid xgridLines = $$.xgridLines || d3.selectAll([]); mainRegion = $$.mainRegion || d3.selectAll([]); mainText = $$.mainText || d3.selectAll([]); mainBar = $$.mainBar || d3.selectAll([]); mainLine = $$.mainLine || d3.selectAll([]); mainArea = $$.mainArea || d3.selectAll([]); mainCircle = $$.mainCircle || d3.selectAll([]); // generate transform to flow if (!flow.orgDataCount) { // if empty if ($$.data.targets[0].values.length !== 1) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1); translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } else { translateX = diffDomain(domain) / 2; } } } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } } scaleX = diffDomain(orgDomain) / diffDomain(domain); transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; $$.hideXGridFocus(); var flowTransition = d3 .transition() .ease(d3.easeLinear) .duration(durationForFlow); wait.add($$.xAxis($$.axes.x, flowTransition)); wait.add(mainBar.transition(flowTransition).attr('transform', transform)); wait.add(mainLine.transition(flowTransition).attr('transform', transform)); wait.add(mainArea.transition(flowTransition).attr('transform', transform)); wait.add(mainCircle.transition(flowTransition).attr('transform', transform)); wait.add(mainText.transition(flowTransition).attr('transform', transform)); wait.add(mainRegion .filter($$.isRegionOnX) .transition(flowTransition) .attr('transform', transform)); wait.add(xgrid.transition(flowTransition).attr('transform', transform)); wait.add(xgridLines.transition(flowTransition).attr('transform', transform)); wait(function () { var i, shapes = [], texts = []; // remove flowed elements if (flowLength) { for (i = 0; i < flowLength; i++) { shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); texts.push('.' + CLASS.text + '-' + (flowIndex + i)); } $$.svg .selectAll('.' + CLASS.shapes) .selectAll(shapes) .remove(); $$.svg .selectAll('.' + CLASS.texts) .selectAll(texts) .remove(); $$.svg.select('.' + CLASS.xgrid).remove(); } // draw again for removing flowed elements and reverting attr xgrid .attr('transform', null) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); xgridLines.attr('transform', null); xgridLines .select('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv); xgridLines .select('text') .attr('x', config.axis_rotated ? $$.width : 0) .attr('y', xv); mainBar.attr('transform', null).attr('d', drawBar); mainLine.attr('transform', null).attr('d', drawLine); mainArea.attr('transform', null).attr('d', drawArea); mainCircle .attr('transform', null) .attr('cx', cx) .attr('cy', cy); mainText .attr('transform', null) .attr('x', xForText) .attr('y', yForText) .style('fill-opacity', $$.opacityForText.bind($$)); mainRegion.attr('transform', null); mainRegion .filter($$.isRegionOnX) .attr('x', $$.regionX.bind($$)) .attr('width', $$.regionWidth.bind($$)); // callback for end of flow done(); $$.flowing = false; }); }; }; Chart.prototype.focus = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); (candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)))), this.revert(); this.defocus(); candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.expandArc(targetIds); } $$.toggleFocusLegend(targetIds, true); $$.focusedTargetIds = targetIds; $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); }; Chart.prototype.defocus = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); (candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)))), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } $$.toggleFocusLegend(targetIds, false); $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); $$.defocusedTargetIds = targetIds; }; Chart.prototype.revert = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } if ($$.config.legend_show) { $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); $$.legend .selectAll($$.selectorLegends(targetIds)) .filter(function () { return $$.d3.select(this).classed(CLASS.legendItemFocused); }) .classed(CLASS.legendItemFocused, false); } $$.focusedTargetIds = []; $$.defocusedTargetIds = []; }; Chart.prototype.xgrids = function (grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_x_lines; } config.grid_x_lines = grids; $$.redrawWithoutRescale(); return config.grid_x_lines; }; Chart.prototype.xgrids.add = function (grids) { var $$ = this.internal; return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); }; Chart.prototype.xgrids.remove = function (params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, true); }; Chart.prototype.ygrids = function (grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_y_lines; } config.grid_y_lines = grids; $$.redrawWithoutRescale(); return config.grid_y_lines; }; Chart.prototype.ygrids.add = function (grids) { var $$ = this.internal; return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); }; Chart.prototype.ygrids.remove = function (params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, false); }; Chart.prototype.groups = function (groups) { var $$ = this.internal, config = $$.config; if (isUndefined(groups)) { return config.data_groups; } config.data_groups = groups; $$.redraw(); return config.data_groups; }; Chart.prototype.legend = function () { }; Chart.prototype.legend.show = function (targetIds) { var $$ = this.internal; $$.showLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: true }); }; Chart.prototype.legend.hide = function (targetIds) { var $$ = this.internal; $$.hideLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: false }); }; Chart.prototype.load = function (args) { var $$ = this.internal, config = $$.config; // update xs if specified if (args.xs) { $$.addXs(args.xs); } // update names if exists if ('names' in args) { Chart.prototype.data.names.bind(this)(args.names); } // update classes if exists if ('classes' in args) { Object.keys(args.classes).forEach(function (id) { config.data_classes[id] = args.classes[id]; }); } // update categories if exists if ('categories' in args && $$.isCategorized()) { config.axis_x_categories = args.categories; } // update axes if exists if ('axes' in args) { Object.keys(args.axes).forEach(function (id) { config.data_axes[id] = args.axes[id]; }); } // update colors if exists if ('colors' in args) { Object.keys(args.colors).forEach(function (id) { config.data_colors[id] = args.colors[id]; }); } // use cache if exists if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { $$.load($$.getCaches(args.cacheIds), args.done); return; } // unload if needed if (args.unload) { // TODO: do not unload if target will load (included in url/rows/columns) $$.unload($$.mapToTargetIds(args.unload === true ? null : args.unload), function () { $$.loadFromArgs(args); }); } else { $$.loadFromArgs(args); } }; Chart.prototype.unload = function (args) { var $$ = this.internal; args = args || {}; if (args instanceof Array) { args = { ids: args }; } else if (typeof args === 'string') { args = { ids: [args] }; } $$.unload($$.mapToTargetIds(args.ids), function () { $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }); }; Chart.prototype.pie = function () { }; Chart.prototype.pie.padAngle = function (padAngle) { if (padAngle === undefined) { return this.internal.config.pie_padAngle; } this.internal.config.pie_padAngle = padAngle; this.flush(); }; Chart.prototype.regions = function (regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions; } config.regions = regions; $$.redrawWithoutRescale(); return config.regions; }; Chart.prototype.regions.add = function (regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions; } config.regions = config.regions.concat(regions); $$.redrawWithoutRescale(); return config.regions; }; Chart.prototype.regions.remove = function (options) { var $$ = this.internal, config = $$.config, duration, classes, regions; options = options || {}; duration = getOption(options, 'duration', config.transition_duration); classes = getOption(options, 'classes', [CLASS.region]); regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; })); (duration ? regions.transition().duration(duration) : regions) .style('opacity', 0) .remove(); config.regions = config.regions.filter(function (region) { var found = false; if (!region['class']) { return true; } region['class'].split(' ').forEach(function (c) { if (classes.indexOf(c) >= 0) { found = true; } }); return !found; }); return config.regions; }; Chart.prototype.selected = function (targetId) { var $$ = this.internal, d3 = $$.d3; return $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)) .selectAll('.' + CLASS.shape) .filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) .nodes() .map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); }; Chart.prototype.select = function (ids, indices, resetOther) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return; } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return; } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d) && !isSelected) { toggle(true, shape.classed(CLASS.SELECTED, true), d, i); } } else if (isDefined(resetOther) && resetOther) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } }); }; Chart.prototype.unselect = function (ids, indices) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return; } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return; } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d)) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } } }); }; Chart.prototype.show = function (targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.removeHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('display', isIE() ? 'block' : 'initial', 'important') .style('opacity', 1, 'important') .call($$.endall, function () { targets.style('opacity', null).style('opacity', 1); }); if (options.withLegend) { $$.showLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.hide = function (targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.addHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('opacity', 0, 'important') .call($$.endall, function () { targets.style('opacity', null).style('opacity', 0); targets.style('display', 'none'); }); if (options.withLegend) { $$.hideLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.toggle = function (targetIds, options) { var that = this, $$ = this.internal; $$.mapToTargetIds(targetIds).forEach(function (targetId) { $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); }); }; Chart.prototype.subchart = function () { }; Chart.prototype.subchart.isShown = function () { var $$ = this.internal; return $$.config.subchart_show; }; Chart.prototype.subchart.show = function () { var $$ = this.internal; if ($$.config.subchart_show) { return; } $$.config.subchart_show = true; // insert DOM $$.initSubchart(); // update dimensions with sub chart now visible $$.updateDimension(); // insert brush (depends on sizes previously updated) $$.initSubchartBrush(); // attach data $$.updateTargetsForSubchart($$.getTargets()); // reset fade-in state $$.mapToIds($$.data.targets).forEach(function (id) { $$.withoutFadeIn[id] = false; }); // redraw chart ! $$.updateAndRedraw(); // update visible targets ! $$.showTargets(); }; Chart.prototype.subchart.hide = function () { var $$ = this.internal; if (!$$.config.subchart_show) { return; } $$.config.subchart_show = false; // remove DOM $$.removeSubchart(); // re-render chart $$.redraw(); }; Chart.prototype.tooltip = function () { }; Chart.prototype.tooltip.show = function (args) { var $$ = this.internal, targets, data, mouse = {}; // determine mouse position on the chart if (args.mouse) { mouse = args.mouse; } else { // determine focus data if (args.data) { data = args.data; } else if (typeof args.x !== 'undefined') { if (args.id) { targets = $$.data.targets.filter(function (t) { return t.id === args.id; }); } else { targets = $$.data.targets; } data = $$.filterByX(targets, args.x).slice(0, 1)[0]; } mouse = data ? $$.getMousePosition(data) : null; } // emulate mouse events to show $$.dispatchEvent('mousemove', mouse); $$.config.tooltip_onshow.call($$, data); }; Chart.prototype.tooltip.hide = function () { // TODO: get target data by checking the state of focus this.internal.dispatchEvent('mouseout', 0); this.internal.config.tooltip_onhide.call(this); }; Chart.prototype.transform = function (type, targetIds) { var $$ = this.internal, options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; $$.transformTo(targetIds, type, options); }; ChartInternal.prototype.transformTo = function (targetIds, type, optionsForRedraw) { var $$ = this, withTransitionForAxis = !$$.hasArcType(), options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis }; options.withTransitionForTransform = false; $$.transiting = false; $$.setTargetType(targetIds, type); $$.updateTargets($$.data.targets); // this is needed when transforming to arc $$.updateAndRedraw(options); }; Chart.prototype.x = function (x) { var $$ = this.internal; if (arguments.length) { $$.updateTargetX($$.data.targets, x); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs; }; Chart.prototype.xs = function (xs) { var $$ = this.internal; if (arguments.length) { $$.updateTargetXs($$.data.targets, xs); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs; }; Chart.prototype.zoom = function (domain) { var $$ = this.internal; if (domain) { if ($$.isTimeSeries()) { domain = domain.map(function (x) { return $$.parseDate(x); }); } if ($$.config.subchart_show) { $$.brush.selectionAsValue(domain, true); } else { $$.updateXDomain(null, true, false, false, domain); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); return domain; } else { return $$.x.domain(); } }; Chart.prototype.zoom.enable = function (enabled) { var $$ = this.internal; $$.config.zoom_enabled = enabled; $$.updateAndRedraw(); }; Chart.prototype.unzoom = function () { var $$ = this.internal; if ($$.config.subchart_show) { $$.brush.clear(); } else { $$.updateXDomain(null, true, false, false, $$.subX.domain()); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } }; Chart.prototype.zoom.max = function (max) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (max === 0 || max) { config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); } else { return config.zoom_x_max; } }; Chart.prototype.zoom.min = function (min) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (min === 0 || min) { config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); } else { return config.zoom_x_min; } }; Chart.prototype.zoom.range = function (range) { if (arguments.length) { if (isDefined(range.max)) { this.domain.max(range.max); } if (isDefined(range.min)) { this.domain.min(range.min); } } else { return { max: this.domain.max(), min: this.domain.min() }; } }; ChartInternal.prototype.initPie = function () { var $$ = this, d3 = $$.d3; $$.pie = d3 .pie() .padAngle(this.getPadAngle.bind(this)) .value(function (d) { return d.values.reduce(function (a, b) { return a + b.value; }, 0); }); var orderFct = $$.getOrderFunction(); // we need to reverse the returned order if asc or desc to have the slice in expected order. if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) { var defaultSort_1 = orderFct; orderFct = function (t1, t2) { return defaultSort_1(t1, t2) * -1; }; } $$.pie.sort(orderFct || null); }; ChartInternal.prototype.updateRadius = function () { var $$ = this, config = $$.config, w = config.gauge_width || config.donut_width, gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth; $$.radiusExpanded = (Math.min($$.arcWidth, $$.arcHeight) / 2) * ($$.hasType('gauge') ? 0.85 : 1); $$.radius = $$.radiusExpanded * 0.95; $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius; }; ChartInternal.prototype.getPadAngle = function () { if (this.hasType('pie')) { return this.config.pie_padAngle || 0; } else if (this.hasType('donut')) { return this.config.donut_padAngle || 0; } else { return 0; } }; ChartInternal.prototype.updateArc = function () { var $$ = this; $$.svgArc = $$.getSvgArc(); $$.svgArcExpanded = $$.getSvgArcExpanded(); $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); }; ChartInternal.prototype.updateAngle = function (d) { var $$ = this, config = $$.config, found = false, index = 0, gMin, gMax, gTic, gValue; if (!config) { return null; } $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) { if (!found && t.data.id === d.data.id) { found = true; d = t; d.index = index; } index++; }); if (isNaN(d.startAngle)) { d.startAngle = 0; } if (isNaN(d.endAngle)) { d.endAngle = d.startAngle; } if ($$.isGaugeType(d.data)) { gMin = config.gauge_min; gMax = config.gauge_max; gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin); gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin; d.startAngle = config.gauge_startingAngle; d.endAngle = d.startAngle + gTic * gValue; } return found ? d : null; }; ChartInternal.prototype.getSvgArc = function () { var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, arc = $$.d3 .arc() .outerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius; }) .innerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius; }), newArc = function (d, withoutUpdate) { var updated; if (withoutUpdate) { return arc(d); } // for interpolate updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0'; }; newArc.centroid = arc.centroid; return newArc; }; ChartInternal.prototype.getSvgArcExpanded = function (rate) { rate = rate || 1; var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, expandWidth = Math.min($$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100), arc = $$.d3 .arc() .outerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate; }) .innerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius; }); return function (d) { var updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0'; }; }; ChartInternal.prototype.getArc = function (d, withoutUpdate, force) { return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : 'M 0 0'; }; ChartInternal.prototype.transformForArcLabel = function (d) { var $$ = this, config = $$.config, updated = $$.updateAngle(d), c, x, y, h, ratio, translate = '', hasGauge = $$.hasType('gauge'); if (updated && !hasGauge) { c = this.svgArc.centroid(updated); x = isNaN(c[0]) ? 0 : c[0]; y = isNaN(c[1]) ? 0 : c[1]; h = Math.sqrt(x * x + y * y); if ($$.hasType('donut') && config.donut_label_ratio) { ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; } else if ($$.hasType('pie') && config.pie_label_ratio) { ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; } else { ratio = $$.radius && h ? ((36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius) / h : 0; } translate = 'translate(' + x * ratio + ',' + y * ratio + ')'; } else if (updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1) { var y1 = Math.sin(updated.endAngle - Math.PI / 2); x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25); y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3; translate = 'translate(' + x + ',' + y + ')'; } return translate; }; /** * @deprecated Use `getRatio('arc', d)` instead. */ ChartInternal.prototype.getArcRatio = function (d) { return this.getRatio('arc', d); }; ChartInternal.prototype.convertToArcData = function (d) { return this.addName({ id: d.data.id, value: d.value, ratio: this.getRatio('arc', d), index: d.index }); }; ChartInternal.prototype.textForArcLabel = function (d) { var $$ = this, updated, value, ratio, id, format; if (!$$.shouldShowArcLabel()) { return ''; } updated = $$.updateAngle(d); value = updated ? updated.value : null; ratio = $$.getRatio('arc', updated); id = d.data.id; if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { return ''; } format = $$.getArcLabelFormat(); return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio); }; ChartInternal.prototype.textForGaugeMinMax = function (value, isMax) { var $$ = this, format = $$.getGaugeLabelExtents(); return format ? format(value, isMax) : value; }; ChartInternal.prototype.expandArc = function (targetIds) { var $$ = this, interval; // MEMO: avoid to cancel transition if ($$.transiting) { interval = window.setInterval(function () { if (!$$.transiting) { window.clearInterval(interval); if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { $$.expandArc(targetIds); } } }, 10); return; } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .each(function (d) { if (!$$.shouldExpand(d.data.id)) { return; } $$.d3 .select(this) .selectAll('path') .transition() .duration($$.expandDuration(d.data.id)) .attr('d', $$.svgArcExpanded) .transition() .duration($$.expandDuration(d.data.id) * 2) .attr('d', $$.svgArcExpandedSub) .each(function (d) { if ($$.isDonutType(d.data)) ; }); }); }; ChartInternal.prototype.unexpandArc = function (targetIds) { var $$ = this; if ($$.transiting) { return; } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .selectAll('path') .transition() .duration(function (d) { return $$.expandDuration(d.data.id); }) .attr('d', $$.svgArc); $$.svg.selectAll('.' + CLASS.arc); }; ChartInternal.prototype.expandDuration = function (id) { var $$ = this, config = $$.config; if ($$.isDonutType(id)) { return config.donut_expand_duration; } else if ($$.isGaugeType(id)) { return config.gauge_expand_duration; } else if ($$.isPieType(id)) { return config.pie_expand_duration; } else { return 50; } }; ChartInternal.prototype.shouldExpand = function (id) { var $$ = this, config = $$.config; return (($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand)); }; ChartInternal.prototype.shouldShowArcLabel = function () { var $$ = this, config = $$.config, shouldShow = true; if ($$.hasType('donut')) { shouldShow = config.donut_label_show; } else if ($$.hasType('pie')) { shouldShow = config.pie_label_show; } // when gauge, always true return shouldShow; }; ChartInternal.prototype.meetsArcLabelThreshold = function (ratio) { var $$ = this, config = $$.config, threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; return ratio >= threshold; }; ChartInternal.prototype.getArcLabelFormat = function () { var $$ = this, config = $$.config, format = config.pie_label_format; if ($$.hasType('gauge')) { format = config.gauge_label_format; } else if ($$.hasType('donut')) { format = config.donut_label_format; } return format; }; ChartInternal.prototype.getGaugeLabelExtents = function () { var $$ = this, config = $$.config; return config.gauge_label_extents; }; ChartInternal.prototype.getArcTitle = function () { var $$ = this; return $$.hasType('donut') ? $$.config.donut_title : ''; }; ChartInternal.prototype.updateTargetsForArc = function (targets) { var $$ = this, main = $$.main, mainPies, mainPieEnter, classChartArc = $$.classChartArc.bind($$), classArcs = $$.classArcs.bind($$), classFocus = $$.classFocus.bind($$); mainPies = main .select('.' + CLASS.chartArcs) .selectAll('.' + CLASS.chartArc) .data($$.pie(targets)) .attr('class', function (d) { return classChartArc(d) + classFocus(d.data); }); mainPieEnter = mainPies .enter() .append('g') .attr('class', classChartArc); mainPieEnter.append('g').attr('class', classArcs); mainPieEnter .append('text') .attr('dy', $$.hasType('gauge') ? '-.1em' : '.35em') .style('opacity', 0) .style('text-anchor', 'middle') .style('pointer-events', 'none'); // MEMO: can not keep same color..., but not bad to update color in redraw //mainPieUpdate.exit().remove(); }; ChartInternal.prototype.initArc = function () { var $$ = this; $$.arcs = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); $$.arcs .append('text') .attr('class', CLASS.chartArcsTitle) .style('text-anchor', 'middle') .text($$.getArcTitle()); }; ChartInternal.prototype.redrawArc = function (duration, durationForExit, withTransform) { var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, arcs, mainArc, arcLabelLines, mainArcLabelLine, hasGaugeType = $$.hasType('gauge'); arcs = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arc) .data($$.arcData.bind($$)); mainArc = arcs .enter() .append('path') .attr('class', $$.classArc.bind($$)) .style('fill', function (d) { return $$.color(d.data); }) .style('cursor', function (d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? 'pointer' : null; }) .each(function (d) { if ($$.isGaugeType(d.data)) { d.startAngle = d.endAngle = config.gauge_startingAngle; } this._current = d; }) .merge(arcs); if (hasGaugeType) { arcLabelLines = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arcLabelLine) .data($$.arcData.bind($$)); mainArcLabelLine = arcLabelLines .enter() .append('rect') .attr('class', function (d) { return (CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id); }) .merge(arcLabelLines); if ($$.filterTargetsToShow($$.data.targets).length === 1) { mainArcLabelLine.style('display', 'none'); } else { mainArcLabelLine .style('fill', function (d) { return $$.levelColor ? $$.levelColor(d.data.values.reduce(function (total, item) { return total + item.value; }, 0)) : $$.color(d.data); }) .style('display', config.gauge_labelLine_show ? '' : 'none') .each(function (d) { var lineLength = 0, lineThickness = 2, x = 0, y = 0, transform = ''; if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) { var updated = $$.updateAngle(d), innerLineLength = ($$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length) * (updated.index + 1), lineAngle = updated.endAngle - Math.PI / 2, arcInnerRadius = $$.radius - innerLineLength, linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius); lineLength = $$.radiusExpanded - $$.radius + innerLineLength; x = Math.cos(linePositioningAngle) * arcInnerRadius; y = Math.sin(linePositioningAngle) * arcInnerRadius; transform = 'rotate(' + (lineAngle * 180) / Math.PI + ', ' + x + ', ' + y + ')'; } d3.select(this) .attr('x', x) .attr('y', y) .attr('width', lineLength) .attr('height', lineThickness) .attr('transform', transform) .style('stroke-dasharray', '0, ' + (lineLength + lineThickness) + ', 0'); }); } } mainArc .attr('transform', function (d) { return !$$.isGaugeType(d.data) && withTransform ? 'scale(0)' : ''; }) .on('mouseover', config.interaction_enabled ? function (d) { var updated, arcData; if ($$.transiting) { // skip while transiting return; } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.expandArc(updated.data.id); $$.api.focus(updated.data.id); $$.toggleFocusLegend(updated.data.id, true); $$.config.data_onmouseover(arcData, this); } } : null) .on('mousemove', config.interaction_enabled ? function (d) { var updated = $$.updateAngle(d), arcData, selectedData; if (updated) { (arcData = $$.convertToArcData(updated)), (selectedData = [arcData]); $$.showTooltip(selectedData, this); } } : null) .on('mouseout', config.interaction_enabled ? function (d) { var updated, arcData; if ($$.transiting) { // skip while transiting return; } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.unexpandArc(updated.data.id); $$.api.revert(); $$.revertLegend(); $$.hideTooltip(); $$.config.data_onmouseout(arcData, this); } } : null) .on('click', config.interaction_enabled ? function (d, i) { var updated = $$.updateAngle(d), arcData; if (updated) { arcData = $$.convertToArcData(updated); if ($$.toggleShape) { $$.toggleShape(this, arcData, i); } $$.config.data_onclick.call($$.api, arcData, this); } } : null) .each(function () { $$.transiting = true; }) .transition() .duration(duration) .attrTween('d', function (d) { var updated = $$.updateAngle(d), interpolate; if (!updated) { return function () { return 'M 0 0'; }; } // if (this._current === d) { // this._current = { // startAngle: Math.PI*2, // endAngle: Math.PI*2, // }; // } if (isNaN(this._current.startAngle)) { this._current.startAngle = 0; } if (isNaN(this._current.endAngle)) { this._current.endAngle = this._current.startAngle; } interpolate = d3.interpolate(this._current, updated); this._current = interpolate(0); return function (t) { // prevents crashing the charts once in transition and chart.destroy() has been called if ($$.config === null) { return 'M 0 0'; } var interpolated = interpolate(t); interpolated.data = d.data; // data.id will be updated by interporator return $$.getArc(interpolated, true); }; }) .attr('transform', withTransform ? 'scale(1)' : '') .style('fill', function (d) { return $$.levelColor ? $$.levelColor(d.data.values.reduce(function (total, item) { return total + item.value; }, 0)) : $$.color(d.data.id); }) // Where gauge reading color would receive customization. .call($$.endall, function () { $$.transiting = false; }); arcs .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); main .selectAll('.' + CLASS.chartArc) .select('text') .style('opacity', 0) .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; }) .text($$.textForArcLabel.bind($$)) .attr('transform', $$.transformForArcLabel.bind($$)) .style('font-size', function (d) { return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : ''; }) .transition() .duration(duration) .style('opacity', function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; }); main .select('.' + CLASS.chartArcsTitle) .style('opacity', $$.hasType('donut') || hasGaugeType ? 1 : 0); if (hasGaugeType) { var index_1 = 0; var backgroundArc = $$.arcs .select('g.' + CLASS.chartArcsBackground) .selectAll('path.' + CLASS.chartArcsBackground) .data($$.data.targets); backgroundArc .enter() .append('path') .attr('class', function (d, i) { return CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i; }) .merge(backgroundArc) .attr('d', function (d1) { if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) { return 'M 0 0'; } var d = { data: [{ value: config.gauge_max }], startAngle: config.gauge_startingAngle, endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1), index: index_1++ }; return $$.getArc(d, true, true); }); backgroundArc.exit().remove(); $$.arcs .select('.' + CLASS.chartArcsGaugeUnit) .attr('dy', '.75em') .text(config.gauge_label_show ? config.gauge_units : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMin) .attr('dx', -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + 'px') .attr('dy', '1.2em') .text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMax) .attr('dx', $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + 'px') .attr('dy', '1.2em') .text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : ''); } }; ChartInternal.prototype.initGauge = function () { var arcs = this.arcs; if (this.hasType('gauge')) { arcs.append('g').attr('class', CLASS.chartArcsBackground); arcs .append('text') .attr('class', CLASS.chartArcsGaugeUnit) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMin) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMax) .style('text-anchor', 'middle') .style('pointer-events', 'none'); } }; ChartInternal.prototype.getGaugeLabelHeight = function () { return this.config.gauge_label_show ? 20 : 0; }; /** * Store value into cache * * @param key * @param value */ ChartInternal.prototype.addToCache = function (key, value) { this.cache["$" + key] = value; }; /** * Returns a cached value or undefined * * @param key * @return {*} */ ChartInternal.prototype.getFromCache = function (key) { return this.cache["$" + key]; }; /** * Reset cached data */ ChartInternal.prototype.resetCache = function () { var _this = this; Object.keys(this.cache) .filter(function (key) { return /^\$/.test(key); }) .forEach(function (key) { delete _this.cache[key]; }); }; // Old API that stores Targets ChartInternal.prototype.hasCaches = function (ids) { for (var i = 0; i < ids.length; i++) { if (!(ids[i] in this.cache)) { return false; } } return true; }; ChartInternal.prototype.addCache = function (id, target) { this.cache[id] = this.cloneTarget(target); }; ChartInternal.prototype.getCaches = function (ids) { var targets = [], i; for (i = 0; i < ids.length; i++) { if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); } } return targets; }; ChartInternal.prototype.categoryName = function (i) { var config = this.config; return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i; }; ChartInternal.prototype.generateTargetClass = function (targetId) { return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : ''; }; ChartInternal.prototype.generateClass = function (prefix, targetId) { return ' ' + prefix + ' ' + prefix + this.generateTargetClass(targetId); }; ChartInternal.prototype.classText = function (d) { return this.generateClass(CLASS.text, d.index); }; ChartInternal.prototype.classTexts = function (d) { return this.generateClass(CLASS.texts, d.id); }; ChartInternal.prototype.classShape = function (d) { return this.generateClass(CLASS.shape, d.index); }; ChartInternal.prototype.classShapes = function (d) { return this.generateClass(CLASS.shapes, d.id); }; ChartInternal.prototype.classLine = function (d) { return this.classShape(d) + this.generateClass(CLASS.line, d.id); }; ChartInternal.prototype.classLines = function (d) { return this.classShapes(d) + this.generateClass(CLASS.lines, d.id); }; ChartInternal.prototype.classCircle = function (d) { return this.classShape(d) + this.generateClass(CLASS.circle, d.index); }; ChartInternal.prototype.classCircles = function (d) { return this.classShapes(d) + this.generateClass(CLASS.circles, d.id); }; ChartInternal.prototype.classBar = function (d) { return this.classShape(d) + this.generateClass(CLASS.bar, d.index); }; ChartInternal.prototype.classBars = function (d) { return this.classShapes(d) + this.generateClass(CLASS.bars, d.id); }; ChartInternal.prototype.classArc = function (d) { return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id); }; ChartInternal.prototype.classArcs = function (d) { return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id); }; ChartInternal.prototype.classArea = function (d) { return this.classShape(d) + this.generateClass(CLASS.area, d.id); }; ChartInternal.prototype.classAreas = function (d) { return this.classShapes(d) + this.generateClass(CLASS.areas, d.id); }; ChartInternal.prototype.classRegion = function (d, i) { return (this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '')); }; ChartInternal.prototype.classEvent = function (d) { return this.generateClass(CLASS.eventRect, d.index); }; ChartInternal.prototype.classTarget = function (id) { var $$ = this; var additionalClassSuffix = $$.config.data_classes[id], additionalClass = ''; if (additionalClassSuffix) { additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; } return $$.generateClass(CLASS.target, id) + additionalClass; }; ChartInternal.prototype.classFocus = function (d) { return this.classFocused(d) + this.classDefocused(d); }; ChartInternal.prototype.classFocused = function (d) { return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : ''); }; ChartInternal.prototype.classDefocused = function (d) { return (' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '')); }; ChartInternal.prototype.classChartText = function (d) { return CLASS.chartText + this.classTarget(d.id); }; ChartInternal.prototype.classChartLine = function (d) { return CLASS.chartLine + this.classTarget(d.id); }; ChartInternal.prototype.classChartBar = function (d) { return CLASS.chartBar + this.classTarget(d.id); }; ChartInternal.prototype.classChartArc = function (d) { return CLASS.chartArc + this.classTarget(d.data.id); }; ChartInternal.prototype.getTargetSelectorSuffix = function (targetId) { var targetClass = this.generateTargetClass(targetId); if (window.CSS && window.CSS.escape) { return window.CSS.escape(targetClass); } // fallback on imperfect method for old browsers (does not handles unicode) return targetClass.replace(/([?!@#$%^&*()=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1'); }; ChartInternal.prototype.selectorTarget = function (id, prefix) { return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id); }; ChartInternal.prototype.selectorTargets = function (ids, prefix) { var $$ = this; ids = ids || []; return ids.length ? ids.map(function (id) { return $$.selectorTarget(id, prefix); }) : null; }; ChartInternal.prototype.selectorLegend = function (id) { return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id); }; ChartInternal.prototype.selectorLegends = function (ids) { var $$ = this; return ids && ids.length ? ids.map(function (id) { return $$.selectorLegend(id); }) : null; }; ChartInternal.prototype.getClipPath = function (id) { return 'url(' + (isIE(9) ? '' : document.URL.split('#')[0]) + '#' + id + ')'; }; ChartInternal.prototype.appendClip = function (parent, id) { return parent .append('clipPath') .attr('id', id) .append('rect'); }; ChartInternal.prototype.getAxisClipX = function (forHorizontal) { // axis line width + padding for left var left = Math.max(30, this.margin.left); return forHorizontal ? -(1 + left) : -(left - 1); }; ChartInternal.prototype.getAxisClipY = function (forHorizontal) { return forHorizontal ? -20 : -this.margin.top; }; ChartInternal.prototype.getXAxisClipX = function () { var $$ = this; return $$.getAxisClipX(!$$.config.axis_rotated); }; ChartInternal.prototype.getXAxisClipY = function () { var $$ = this; return $$.getAxisClipY(!$$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipX = function () { var $$ = this; return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipY = function () { var $$ = this; return $$.getAxisClipY($$.config.axis_rotated); }; ChartInternal.prototype.getAxisClipWidth = function (forHorizontal) { var $$ = this, left = Math.max(30, $$.margin.left), right = Math.max(30, $$.margin.right); // width + axis line width + padding for left/right return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20; }; ChartInternal.prototype.getAxisClipHeight = function (forHorizontal) { // less than 20 is not enough to show the axis label 'outer' without legend return ((forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20); }; ChartInternal.prototype.getXAxisClipWidth = function () { var $$ = this; return $$.getAxisClipWidth(!$$.config.axis_rotated); }; ChartInternal.prototype.getXAxisClipHeight = function () { var $$ = this; return $$.getAxisClipHeight(!$$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipWidth = function () { var $$ = this; return ($$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0)); }; ChartInternal.prototype.getYAxisClipHeight = function () { var $$ = this; return $$.getAxisClipHeight($$.config.axis_rotated); }; ChartInternal.prototype.generateColor = function () { var $$ = this, config = $$.config, d3 = $$.d3, colors = config.data_colors, pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10, callback = config.data_color, ids = []; return function (d) { var id = d.id || (d.data && d.data.id) || d, color; // if callback function is provided if (colors[id] instanceof Function) { color = colors[id](d); } // if specified, choose that color else if (colors[id]) { color = colors[id]; } // if not specified, choose from pattern else { if (ids.indexOf(id) < 0) { ids.push(id); } color = pattern[ids.indexOf(id) % pattern.length]; colors[id] = color; } return callback instanceof Function ? callback(color, d) : color; }; }; ChartInternal.prototype.generateLevelColor = function () { var $$ = this, config = $$.config, colors = config.color_pattern, threshold = config.color_threshold, asValue = threshold.unit === 'value', values = threshold.values && threshold.values.length ? threshold.values : [], max = threshold.max || 100; return notEmpty(threshold) && notEmpty(colors) ? function (value) { var i, v, color = colors[colors.length - 1]; for (i = 0; i < values.length; i++) { v = asValue ? value : (value * 100) / max; if (v < values[i]) { color = colors[i]; break; } } return color; } : null; }; ChartInternal.prototype.getDefaultConfig = function () { var config = { bindto: '#chart', svg_classname: undefined, size_width: undefined, size_height: undefined, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, resize_auto: true, zoom_enabled: false, zoom_initialRange: undefined, zoom_type: 'scroll', zoom_disableDefaultBehavior: false, zoom_privileged: false, zoom_rescale: false, zoom_onzoom: function () { }, zoom_onzoomstart: function () { }, zoom_onzoomend: function () { }, zoom_x_min: undefined, zoom_x_max: undefined, interaction_brighten: true, interaction_enabled: true, onmouseover: function () { }, onmouseout: function () { }, onresize: function () { }, onresized: function () { }, oninit: function () { }, onrendered: function () { }, transition_duration: 350, data_epochs: 'epochs', data_x: undefined, data_xs: {}, data_xFormat: '%Y-%m-%d', data_xLocaltime: true, data_xSort: true, data_idConverter: function (id) { return id; }, data_names: {}, data_classes: {}, data_groups: [], data_axes: {}, data_type: undefined, data_types: {}, data_labels: {}, data_order: 'desc', data_regions: {}, data_color: undefined, data_colors: {}, data_hide: false, data_filter: undefined, data_selection_enabled: false, data_selection_grouped: false, data_selection_isselectable: function () { return true; }, data_selection_multiple: true, data_selection_draggable: false, data_stack_normalize: false, data_onclick: function () { }, data_onmouseover: function () { }, data_onmouseout: function () { }, data_onselected: function () { }, data_onunselected: function () { }, data_url: undefined, data_headers: undefined, data_json: undefined, data_rows: undefined, data_columns: undefined, data_mimeType: undefined, data_keys: undefined, // configuration for no plot-able data supplied. data_empty_label_text: '', // subchart subchart_show: false, subchart_size_height: 60, subchart_axis_x_show: true, subchart_onbrush: function () { }, // color color_pattern: [], color_threshold: {}, // legend legend_show: true, legend_hide: false, legend_position: 'bottom', legend_inset_anchor: 'top-left', legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, legend_item_onclick: undefined, legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, axis_x_type: 'indexed', axis_x_localtime: true, axis_x_categories: [], axis_x_tick_centered: false, axis_x_tick_format: undefined, axis_x_tick_culling: {}, axis_x_tick_culling_max: 10, axis_x_tick_count: undefined, axis_x_tick_fit: true, axis_x_tick_values: null, axis_x_tick_rotate: 0, axis_x_tick_outer: true, axis_x_tick_multiline: true, axis_x_tick_multilineMax: 0, axis_x_tick_width: null, axis_x_max: undefined, axis_x_min: undefined, axis_x_padding: {}, axis_x_height: undefined, axis_x_selection: undefined, axis_x_label: {}, axis_x_inner: undefined, axis_y_show: true, axis_y_type: 'linear', axis_y_max: undefined, axis_y_min: undefined, axis_y_inverted: false, axis_y_center: undefined, axis_y_inner: undefined, axis_y_label: {}, axis_y_tick_format: undefined, axis_y_tick_outer: true, axis_y_tick_values: null, axis_y_tick_rotate: 0, axis_y_tick_count: undefined, axis_y_tick_time_type: undefined, axis_y_tick_time_interval: undefined, axis_y_padding: {}, axis_y_default: undefined, axis_y2_show: false, axis_y2_type: 'linear', axis_y2_max: undefined, axis_y2_min: undefined, axis_y2_inverted: false, axis_y2_center: undefined, axis_y2_inner: undefined, axis_y2_label: {}, axis_y2_tick_format: undefined, axis_y2_tick_outer: true, axis_y2_tick_values: null, axis_y2_tick_count: undefined, axis_y2_padding: {}, axis_y2_default: undefined, // grid grid_x_show: false, grid_x_type: 'tick', grid_x_lines: [], grid_y_show: false, // not used // grid_y_type: 'tick', grid_y_lines: [], grid_y_ticks: 10, grid_focus_show: true, grid_lines_front: true, // point - point of each data point_show: true, point_r: 2.5, point_sensitivity: 10, point_focus_expand_enabled: true, point_focus_expand_r: undefined, point_select_r: undefined, // line line_connectNull: false, line_step_type: 'step', // bar bar_width: undefined, bar_width_ratio: 0.6, bar_width_max: undefined, bar_zerobased: true, bar_space: 0, // area area_zerobased: true, area_above: false, // pie pie_label_show: true, pie_label_format: undefined, pie_label_threshold: 0.05, pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, pie_padAngle: 0, // gauge gauge_fullCircle: false, gauge_label_show: true, gauge_labelLine_show: true, gauge_label_format: undefined, gauge_min: 0, gauge_max: 100, gauge_startingAngle: (-1 * Math.PI) / 2, gauge_label_extents: undefined, gauge_units: undefined, gauge_width: undefined, gauge_arcs_minWidth: 5, gauge_expand: {}, gauge_expand_duration: 50, // donut donut_label_show: true, donut_label_format: undefined, donut_label_threshold: 0.05, donut_label_ratio: undefined, donut_width: undefined, donut_title: '', donut_expand: {}, donut_expand_duration: 50, donut_padAngle: 0, // spline spline_interpolation_type: 'cardinal', // stanford stanford_lines: [], stanford_regions: [], stanford_texts: [], stanford_scaleMin: undefined, stanford_scaleMax: undefined, stanford_scaleWidth: undefined, stanford_scaleFormat: undefined, stanford_scaleValues: undefined, stanford_colors: undefined, stanford_padding: { top: 0, right: 0, bottom: 0, left: 0 }, // region - region to change style regions: [], // tooltip - show when mouseover on each data tooltip_show: true, tooltip_grouped: true, tooltip_order: undefined, tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, tooltip_horizontal: undefined, tooltip_position: undefined, tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) { return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : ''; }, tooltip_init_show: false, tooltip_init_x: 0, tooltip_init_position: { top: '0px', left: '50px' }, tooltip_onshow: function () { }, tooltip_onhide: function () { }, // title title_text: undefined, title_padding: { top: 0, right: 0, bottom: 0, left: 0 }, title_position: 'top-center' }; Object.keys(this.additionalConfig).forEach(function (key) { config[key] = this.additionalConfig[key]; }, this); return config; }; ChartInternal.prototype.additionalConfig = {}; ChartInternal.prototype.loadConfig = function (config) { var this_config = this.config, target, keys, read; function find() { var key = keys.shift(); // console.log("key =>", key, ", target =>", target); if (key && target && typeof target === 'object' && key in target) { target = target[key]; return find(); } else if (!key) { return target; } else { return undefined; } } Object.keys(this_config).forEach(function (key) { target = config; keys = key.split('_'); read = find(); // console.log("CONFIG : ", key, read); if (isDefined(read)) { this_config[key] = read; } }); }; ChartInternal.prototype.convertUrlToData = function (url, mimeType, headers, keys, done) { var $$ = this, type = mimeType ? mimeType : 'csv', f, converter; if (type === 'json') { f = $$.d3.json; converter = $$.convertJsonToData; } else if (type === 'tsv') { f = $$.d3.tsv; converter = $$.convertXsvToData; } else { f = $$.d3.csv; converter = $$.convertXsvToData; } f(url, headers) .then(function (data) { done.call($$, converter.call($$, data, keys)); }) .catch(function (error) { throw error; }); }; ChartInternal.prototype.convertXsvToData = function (xsv) { var keys = xsv.columns, rows = xsv; if (rows.length === 0) { return { keys: keys, rows: [keys.reduce(function (row, key) { var _a; return Object.assign(row, (_a = {}, _a[key] = null, _a)); }, {})] }; } else { // [].concat() is to convert result into a plain array otherwise // test is not happy because rows have properties. return { keys: keys, rows: [].concat(xsv) }; } }; ChartInternal.prototype.convertJsonToData = function (json, keys) { var $$ = this, new_rows = [], targetKeys, data; if (keys) { // when keys specified, json would be an array that includes objects if (keys.x) { targetKeys = keys.value.concat(keys.x); $$.config.data_x = keys.x; } else { targetKeys = keys.value; } new_rows.push(targetKeys); json.forEach(function (o) { var new_row = []; targetKeys.forEach(function (key) { // convert undefined to null because undefined data will be removed in convertDataToTargets() var v = $$.findValueInJson(o, key); if (isUndefined(v)) { v = null; } new_row.push(v); }); new_rows.push(new_row); }); data = $$.convertRowsToData(new_rows); } else { Object.keys(json).forEach(function (key) { new_rows.push([key].concat(json[key])); }); data = $$.convertColumnsToData(new_rows); } return data; }; /** * Finds value from the given nested object by the given path. * If it's not found, then this returns undefined. * @param {Object} object the object * @param {string} path the path */ ChartInternal.prototype.findValueInJson = function (object, path) { if (path in object) { // If object has a key that contains . or [], return the key's value // instead of searching for an inner object. // See https://github.com/c3js/c3/issues/1691 for details. return object[path]; } path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) path = path.replace(/^\./, ''); // strip a leading dot var pathArray = path.split('.'); for (var i = 0; i < pathArray.length; ++i) { var k = pathArray[i]; if (k in object) { object = object[k]; } else { return; } } return object; }; /** * Converts the rows to normalized data. * @param {any[][]} rows The row data * @return {Object} */ ChartInternal.prototype.convertRowsToData = function (rows) { var newRows = []; var keys = rows[0]; for (var i = 1; i < rows.length; i++) { var newRow = {}; for (var j = 0; j < rows[i].length; j++) { if (isUndefined(rows[i][j])) { throw new Error('Source data is missing a component at (' + i + ',' + j + ')!'); } newRow[keys[j]] = rows[i][j]; } newRows.push(newRow); } return { keys: keys, rows: newRows }; }; /** * Converts the columns to normalized data. * @param {any[][]} columns The column data * @return {Object} */ ChartInternal.prototype.convertColumnsToData = function (columns) { var newRows = []; var keys = []; for (var i = 0; i < columns.length; i++) { var key = columns[i][0]; for (var j = 1; j < columns[i].length; j++) { if (isUndefined(newRows[j - 1])) { newRows[j - 1] = {}; } if (isUndefined(columns[i][j])) { throw new Error('Source data is missing a component at (' + i + ',' + j + ')!'); } newRows[j - 1][key] = columns[i][j]; } keys.push(key); } return { keys: keys, rows: newRows }; }; /** * Converts the data format into the target format. * @param {!Object} data * @param {!Array} data.keys Ordered list of target IDs. * @param {!Array} data.rows Rows of data to convert. * @param {boolean} appendXs True to append to $$.data.xs, False to replace. * @return {!Array} */ ChartInternal.prototype.convertDataToTargets = function (data, appendXs) { var $$ = this, config = $$.config, targets, ids, xs, keys, epochs; // handles format where keys are not orderly provided if (isArray(data)) { keys = Object.keys(data[0]); } else { keys = data.keys; data = data.rows; } xs = keys.filter($$.isX, $$); if (!$$.isStanfordGraphType()) { ids = keys.filter($$.isNotX, $$); } else { epochs = keys.filter($$.isEpochs, $$); ids = keys.filter($$.isNotXAndNotEpochs, $$); if (xs.length !== 1 || epochs.length !== 1 || ids.length !== 1) { throw new Error("You must define the 'x' key name and the 'epochs' for Stanford Diagrams"); } } // save x for update data by load when custom x and c3.x API ids.forEach(function (id) { var xKey = $$.getXKey(id); if ($$.isCustomX() || $$.isTimeSeries()) { // if included in input data if (xs.indexOf(xKey) >= 0) { $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(data .map(function (d) { return d[xKey]; }) .filter(isValue) .map(function (rawX, i) { return $$.generateTargetX(rawX, id, i); })); } // if not included in input data, find from preloaded data of other id's x else if (config.data_x) { $$.data.xs[id] = $$.getOtherTargetXs(); } // if not included in input data, find from preloaded data else if (notEmpty(config.data_xs)) { $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); } // MEMO: if no x included, use same x of current will be used } else { $$.data.xs[id] = data.map(function (d, i) { return i; }); } }); // check x is defined ids.forEach(function (id) { if (!$$.data.xs[id]) { throw new Error('x is not defined for id = "' + id + '".'); } }); // convert to target targets = ids.map(function (id, index) { var convertedId = config.data_idConverter(id); return { id: convertedId, id_org: id, values: data .map(function (d, i) { var xKey = $$.getXKey(id), rawX = d[xKey], value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x, returnData; // use x as categories if custom x and categorized if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { if (index === 0 && i === 0) { config.axis_x_categories = []; } x = config.axis_x_categories.indexOf(rawX); if (x === -1) { x = config.axis_x_categories.length; config.axis_x_categories.push(rawX); } } else { x = $$.generateTargetX(rawX, id, i); } // mark as x = undefined if value is undefined and filter to remove after mapped if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { x = undefined; } returnData = { x: x, value: value, id: convertedId }; if ($$.isStanfordGraphType()) { returnData.epochs = d[epochs]; } return returnData; }) .filter(function (v) { return isDefined(v.x); }) }; }); // finish targets targets.forEach(function (t) { var i; // sort values by its x if (config.data_xSort) { t.values = t.values.sort(function (v1, v2) { var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, x2 = v2.x || v2.x === 0 ? v2.x : Infinity; return x1 - x2; }); } // indexing each value i = 0; t.values.forEach(function (v) { v.index = i++; }); // this needs to be sorted because its index and value.index is identical $$.data.xs[t.id].sort(function (v1, v2) { return v1 - v2; }); }); // cache information about values $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); // set target types if (config.data_type) { $$.setTargetType($$.mapToIds(targets).filter(function (id) { return !(id in config.data_types); }), config.data_type); } // cache as original id keyed targets.forEach(function (d) { $$.addCache(d.id_org, d); }); return targets; }; ChartInternal.prototype.isEpochs = function (key) { var $$ = this, config = $$.config; return config.data_epochs && key === config.data_epochs; }; ChartInternal.prototype.isX = function (key) { var $$ = this, config = $$.config; return ((config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key))); }; ChartInternal.prototype.isNotX = function (key) { return !this.isX(key); }; ChartInternal.prototype.isNotXAndNotEpochs = function (key) { return !this.isX(key) && !this.isEpochs(key); }; /** * Returns whether the normalized stack option is enabled or not. * * To be enabled it must also have data.groups defined. * * @return {boolean} */ ChartInternal.prototype.isStackNormalized = function () { return this.config.data_stack_normalize && this.config.data_groups.length > 0; }; /** * Returns whether the axis is normalized or not. * * An axis is normalized as long as one of its associated target * is normalized. * * @param axisId Axis ID (y or y2) * @return {Boolean} */ ChartInternal.prototype.isAxisNormalized = function (axisId) { var $$ = this; if (!$$.isStackNormalized()) { // shortcut return false; } return $$.data.targets .filter(function (target) { return $$.axis.getId(target.id) === axisId; }) .some(function (target) { return $$.isTargetNormalized(target.id); }); }; /** * Returns whether the values for this target ID is normalized or not. * * To be normalized the option needs to be enabled and target needs * to be defined in `data.groups`. * * @param targetId ID of the target * @return {Boolean} True if the target is normalized, false otherwise. */ ChartInternal.prototype.isTargetNormalized = function (targetId) { var $$ = this; return ($$.isStackNormalized() && $$.config.data_groups.some(function (group) { return group.includes(targetId); })); }; ChartInternal.prototype.getXKey = function (id) { var $$ = this, config = $$.config; return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null; }; /** * Get sum of visible data per index for given axis. * * Expect axisId to be either 'y' or 'y2'. * * @private * @param axisId Compute sum for data associated to given axis. * @return {Array} */ ChartInternal.prototype.getTotalPerIndex = function (axisId) { var $$ = this; if (!$$.isStackNormalized()) { return null; } var cached = $$.getFromCache('getTotalPerIndex'); if (cached !== undefined) { return cached[axisId]; } var sum = { y: [], y2: [] }; $$.data.targets // keep only target that are normalized .filter(function (target) { return $$.isTargetNormalized(target.id); }) // keep only target that are visible .filter(function (target) { return $$.isTargetToShow(target.id); }) // compute sum per axis .forEach(function (target) { var sumByAxis = sum[$$.axis.getId(target.id)]; target.values.forEach(function (v, i) { if (!sumByAxis[i]) { sumByAxis[i] = 0; } sumByAxis[i] += isNumber(v.value) ? v.value : 0; }); }); $$.addToCache('getTotalPerIndex', sum); return sum[axisId]; }; /** * Get sum of visible data. * * Should be used for normalised data only since all values * are expected to be positive. * * @private * @return {Number} */ ChartInternal.prototype.getTotalDataSum = function () { var $$ = this; var cached = $$.getFromCache('getTotalDataSum'); if (cached !== undefined) { return cached; } var totalDataSum = flattenArray($$.data.targets .filter(function (target) { return $$.isTargetToShow(target.id); }) .map(function (target) { return target.values; })) .map(function (d) { return d.value; }) .reduce(function (p, c) { return p + c; }, 0); $$.addToCache('getTotalDataSum', totalDataSum); return totalDataSum; }; ChartInternal.prototype.getXValuesOfXKey = function (key, targets) { var $$ = this, xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; ids.forEach(function (id) { if ($$.getXKey(id) === key) { xValues = $$.data.xs[id]; } }); return xValues; }; ChartInternal.prototype.getXValue = function (id, i) { var $$ = this; return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i; }; ChartInternal.prototype.getOtherTargetXs = function () { var $$ = this, idsForX = Object.keys($$.data.xs); return idsForX.length ? $$.data.xs[idsForX[0]] : null; }; ChartInternal.prototype.getOtherTargetX = function (index) { var xs = this.getOtherTargetXs(); return xs && index < xs.length ? xs[index] : null; }; ChartInternal.prototype.addXs = function (xs) { var $$ = this; Object.keys(xs).forEach(function (id) { $$.config.data_xs[id] = xs[id]; }); }; ChartInternal.prototype.addName = function (data) { var $$ = this, name; if (data) { name = $$.config.data_names[data.id]; data.name = name !== undefined ? name : data.id; } return data; }; ChartInternal.prototype.getValueOnIndex = function (values, index) { var valueOnIndex = values.filter(function (v) { return v.index === index; }); return valueOnIndex.length ? valueOnIndex[0] : null; }; ChartInternal.prototype.updateTargetX = function (targets, x) { var $$ = this; targets.forEach(function (t) { t.values.forEach(function (v, i) { v.x = $$.generateTargetX(x[i], t.id, i); }); $$.data.xs[t.id] = x; }); }; ChartInternal.prototype.updateTargetXs = function (targets, xs) { var $$ = this; targets.forEach(function (t) { if (xs[t.id]) { $$.updateTargetX([t], xs[t.id]); } }); }; ChartInternal.prototype.generateTargetX = function (rawX, id, index) { var $$ = this, x; if ($$.isTimeSeries()) { x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); } else if ($$.isCustomX() && !$$.isCategorized()) { x = isValue(rawX) ? +rawX : $$.getXValue(id, index); } else { x = index; } return x; }; ChartInternal.prototype.cloneTarget = function (target) { return { id: target.id, id_org: target.id_org, values: target.values.map(function (d) { return { x: d.x, value: d.value, id: d.id }; }) }; }; ChartInternal.prototype.getMaxDataCount = function () { var $$ = this; return $$.d3.max($$.data.targets, function (t) { return t.values.length; }); }; ChartInternal.prototype.mapToIds = function (targets) { return targets.map(function (d) { return d.id; }); }; ChartInternal.prototype.mapToTargetIds = function (ids) { var $$ = this; return ids ? [].concat(ids) : $$.mapToIds($$.data.targets); }; ChartInternal.prototype.hasTarget = function (targets, id) { var ids = this.mapToIds(targets), i; for (i = 0; i < ids.length; i++) { if (ids[i] === id) { return true; } } return false; }; ChartInternal.prototype.isTargetToShow = function (targetId) { return this.hiddenTargetIds.indexOf(targetId) < 0; }; ChartInternal.prototype.isLegendToShow = function (targetId) { return this.hiddenLegendIds.indexOf(targetId) < 0; }; /** * Returns only visible targets. * * This is the same as calling {@link filterTargetsToShow} on $$.data.targets. * * @return {Array} */ ChartInternal.prototype.getTargetsToShow = function () { var $$ = this; return $$.filterTargetsToShow($$.data.targets); }; ChartInternal.prototype.filterTargetsToShow = function (targets) { var $$ = this; return targets.filter(function (t) { return $$.isTargetToShow(t.id); }); }; /** * @return {Array} Returns all the targets attached to the chart, visible or not */ ChartInternal.prototype.getTargets = function () { var $$ = this; return $$.data.targets; }; ChartInternal.prototype.mapTargetsToUniqueXs = function (targets) { var $$ = this; var xs = $$.d3 .set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))) .values(); xs = $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; }); return xs.sort(function (a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; }); }; ChartInternal.prototype.addHiddenTargetIds = function (targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]); } } this.resetCache(); }; ChartInternal.prototype.removeHiddenTargetIds = function (targetIds) { this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); this.resetCache(); }; ChartInternal.prototype.addHiddenLegendIds = function (targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]); } } }; ChartInternal.prototype.removeHiddenLegendIds = function (targetIds) { this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); }; ChartInternal.prototype.getValuesAsIdKeyed = function (targets) { var ys = {}; targets.forEach(function (t) { ys[t.id] = []; t.values.forEach(function (v) { ys[t.id].push(v.value); }); }); return ys; }; ChartInternal.prototype.checkValueInTargets = function (targets, checker) { var ids = Object.keys(targets), i, j, values; for (i = 0; i < ids.length; i++) { values = targets[ids[i]].values; for (j = 0; j < values.length; j++) { if (checker(values[j].value)) { return true; } } } return false; }; ChartInternal.prototype.hasNegativeValueInTargets = function (targets) { return this.checkValueInTargets(targets, function (v) { return v < 0; }); }; ChartInternal.prototype.hasPositiveValueInTargets = function (targets) { return this.checkValueInTargets(targets, function (v) { return v > 0; }); }; ChartInternal.prototype.isOrderDesc = function () { var config = this.config; return (typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc'); }; ChartInternal.prototype.isOrderAsc = function () { var config = this.config; return (typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc'); }; ChartInternal.prototype.getOrderFunction = function () { var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc(); if (orderAsc || orderDesc) { var reducer = function (p, c) { return p + Math.abs(c.value); }; return function (t1, t2) { var t1Sum = t1.values.reduce(reducer, 0), t2Sum = t2.values.reduce(reducer, 0); return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum; }; } else if (isFunction(config.data_order)) { return config.data_order; } else if (isArray(config.data_order)) { var order = config.data_order; return function (t1, t2) { return order.indexOf(t1.id) - order.indexOf(t2.id); }; } }; ChartInternal.prototype.orderTargets = function (targets) { var fct = this.getOrderFunction(); if (fct) { targets.sort(fct); } return targets; }; /** * Returns all the values from the given targets at the given index. * * @param {Array} targets * @param {Number} index * @return {Array} */ ChartInternal.prototype.filterByIndex = function (targets, index) { return this.d3.merge(targets.map(function (t) { return t.values.filter(function (v) { return v.index === index; }); })); }; ChartInternal.prototype.filterByX = function (targets, x) { return this.d3 .merge(targets.map(function (t) { return t.values; })) .filter(function (v) { return v.x - x === 0; }); }; ChartInternal.prototype.filterRemoveNull = function (data) { return data.filter(function (d) { return isValue(d.value); }); }; ChartInternal.prototype.filterByXDomain = function (targets, xDomain) { return targets.map(function (t) { return { id: t.id, id_org: t.id_org, values: t.values.filter(function (v) { return xDomain[0] <= v.x && v.x <= xDomain[1]; }) }; }); }; ChartInternal.prototype.hasDataLabel = function () { var config = this.config; if (typeof config.data_labels === 'boolean' && config.data_labels) { return true; } else if (typeof config.data_labels === 'object' && notEmpty(config.data_labels)) { return true; } return false; }; ChartInternal.prototype.getDataLabelLength = function (min, max, key) { var $$ = this, lengths = [0, 0], paddingCoef = 1.3; $$.selectChart .select('svg') .selectAll('.dummy') .data([min, max]) .enter() .append('text') .text(function (d) { return $$.dataLabelFormat(d.id)(d); }) .each(function (d, i) { lengths[i] = getBBox(this)[key] * paddingCoef; }) .remove(); return lengths; }; /** * Returns true if the given data point is not arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isNoneArc = function (d) { return this.hasTarget(this.data.targets, d.id); }; /** * Returns true if the given data point is arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isArc = function (d) { return 'data' in d && this.hasTarget(this.data.targets, d.data.id); }; /** * Find the closest point from the given pos among the given targets or * undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} pos An [x,y] coordinate * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargets = function (targets, pos) { var $$ = this; // for each target, find the closest point var candidates = targets .map(function (t) { return $$.findClosest(t.values, pos, $$.config.tooltip_horizontal ? $$.horizontalDistance.bind($$) : $$.dist.bind($$), $$.config.point_sensitivity); }) .filter(function (v) { return v; }); // returns the closest of candidates if (candidates.length === 0) { return undefined; } else if (candidates.length === 1) { return candidates[0]; } else { return $$.findClosest(candidates, pos, $$.dist.bind($$)); } }; /** * Find the closest point from the x value or undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} x A value on X axis * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargetsByX = function (targets, x) { var closest; var diff; targets.forEach(function (t) { t.values.forEach(function (d) { var newDiff = Math.abs(x - d.x); if (diff === undefined || newDiff < diff) { closest = d; diff = newDiff; } }); }); return closest; }; /** * Using given compute distance method, returns the closest data point from the * given position. * * Giving optionally a minimum distance to satisfy. * * @param {Array} dataPoints List of DataPoints * @param {Array} pos An [x,y] coordinate * @param {Function} computeDist Function to compute distance between 2 points * @param {Number} minDist Minimal distance to satisfy * @return {Object|undefined} Closest data point */ ChartInternal.prototype.findClosest = function (dataPoints, pos, computeDist, minDist) { if (minDist === void 0) { minDist = Infinity; } var $$ = this; var closest; // find closest bar dataPoints .filter(function (v) { return v && $$.isBarType(v.id); }) .forEach(function (v) { if (!closest) { var shape = $$.main .select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index) .node(); if ($$.isWithinBar(pos, shape)) { closest = v; } } }); // find closest point from non-bar dataPoints .filter(function (v) { return v && !$$.isBarType(v.id); }) .forEach(function (v) { var d = computeDist(v, pos); if (d < minDist) { minDist = d; closest = v; } }); return closest; }; ChartInternal.prototype.dist = function (data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, yIndex = config.axis_rotated ? 0 : 1, y = $$.circleY(data, data.index), x = $$.x(data.x); return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)); }; ChartInternal.prototype.horizontalDistance = function (data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, x = $$.x(data.x); return Math.abs(x - pos[xIndex]); }; ChartInternal.prototype.convertValuesToStep = function (values) { var converted = [].concat(values), i; if (!this.isCategorized()) { return values; } for (i = values.length + 1; 0 < i; i--) { converted[i] = converted[i - 1]; } converted[0] = { x: converted[0].x - 1, value: converted[0].value, id: converted[0].id }; converted[values.length + 1] = { x: converted[values.length].x + 1, value: converted[values.length].value, id: converted[values.length].id }; return converted; }; /** * Get ratio value * * @param {String} type Ratio for given type * @param {Object} d Data value object * @param {Boolean} asPercent Convert the return as percent or not * @return {Number} Ratio value * @private */ ChartInternal.prototype.getRatio = function (type, d, asPercent) { if (asPercent === void 0) { asPercent = false; } var $$ = this; var api = $$.api; var ratio = 0; if (d && api.data.shown.call(api).length) { ratio = d.ratio || d.value; if (type === 'arc') { if ($$.hasType('gauge')) { ratio = (d.endAngle - d.startAngle) / (Math.PI * ($$.config.gauge_fullCircle ? 2 : 1)); } else { var total = $$.getTotalDataSum(); ratio = d.value / total; } } else if (type === 'index') { var total = $$.getTotalPerIndex($$.axis.getId(d.id)); d.ratio = isNumber(d.value) && total && total[d.index] > 0 ? d.value / total[d.index] : 0; ratio = d.ratio; } } return asPercent && ratio ? ratio * 100 : ratio; }; ChartInternal.prototype.updateDataAttributes = function (name, attrs) { var $$ = this, config = $$.config, current = config['data_' + name]; if (typeof attrs === 'undefined') { return current; } Object.keys(attrs).forEach(function (id) { current[id] = attrs[id]; }); $$.redraw({ withLegend: true }); return current; }; ChartInternal.prototype.load = function (targets, args) { var $$ = this; if (targets) { // filter loading targets if needed if (args.filter) { targets = targets.filter(args.filter); } // set type if args.types || args.type specified if (args.type || args.types) { targets.forEach(function (t) { var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; $$.setTargetType(t.id, type); }); } // Update/Add data $$.data.targets.forEach(function (d) { for (var i = 0; i < targets.length; i++) { if (d.id === targets[i].id) { d.values = targets[i].values; targets.splice(i, 1); break; } } }); $$.data.targets = $$.data.targets.concat(targets); // add remained } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }; ChartInternal.prototype.loadFromArgs = function (args) { var $$ = this; $$.resetCache(); if (args.data) { $$.load($$.convertDataToTargets(args.data), args); } else if (args.url) { $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) { $$.load($$.convertDataToTargets(data), args); }); } else if (args.json) { $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args); } else if (args.rows) { $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); } else if (args.columns) { $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args); } else { $$.load(null, args); } }; ChartInternal.prototype.unload = function (targetIds, done) { var $$ = this; $$.resetCache(); if (!done) { done = function () { }; } // filter existing target targetIds = targetIds.filter(function (id) { return $$.hasTarget($$.data.targets, id); }); // If no target, call done and return if (!targetIds || targetIds.length === 0) { done(); return; } $$.svg .selectAll(targetIds.map(function (id) { return $$.selectorTarget(id); })) .transition() .style('opacity', 0) .remove() .call($$.endall, done); targetIds.forEach(function (id) { // Reset fadein for future load $$.withoutFadeIn[id] = false; // Remove target's elements if ($$.legend) { $$.legend .selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)) .remove(); } // Remove target $$.data.targets = $$.data.targets.filter(function (t) { return t.id !== id; }); }); }; ChartInternal.prototype.getYDomainMin = function (targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasNegativeValue; if (config.data_groups.length > 0) { hasNegativeValue = $$.hasNegativeValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); if (idsInGroup.length === 0) { continue; } baseId = idsInGroup[0]; // Consider negative values if (hasNegativeValue && ys[baseId]) { ys[baseId].forEach(function (v, i) { ys[baseId][i] = v < 0 ? v : 0; }); } // Compute min for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue; } ys[id].forEach(function (v, i) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) { ys[baseId][i] += +v; } }); } } } return $$.d3.min(Object.keys(ys).map(function (key) { return $$.d3.min(ys[key]); })); }; ChartInternal.prototype.getYDomainMax = function (targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasPositiveValue; if (config.data_groups.length > 0) { hasPositiveValue = $$.hasPositiveValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); if (idsInGroup.length === 0) { continue; } baseId = idsInGroup[0]; // Consider positive values if (hasPositiveValue && ys[baseId]) { ys[baseId].forEach(function (v, i) { ys[baseId][i] = v > 0 ? v : 0; }); } // Compute max for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue; } ys[id].forEach(function (v, i) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) { ys[baseId][i] += +v; } }); } } } return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); })); }; ChartInternal.prototype.getYDomain = function (targets, axisId, xDomain) { var $$ = this, config = $$.config; if ($$.isAxisNormalized(axisId)) { return [0, 100]; } var targetsByAxisId = targets.filter(function (t) { return $$.axis.getId(t.id) === axisId; }), yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yDomainMin = $$.getYDomainMin(yTargets), yDomainMax = $$.getYDomainMax(yTargets), domain, domainLength, padding_top, padding_bottom, center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; // MEMO: avoid inverting domain unexpectedly yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin; yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax; if (yTargets.length === 0) { // use current domain if target of axisId is none return axisId === 'y2' ? $$.y2.domain() : $$.y.domain(); } if (isNaN(yDomainMin)) { // set minimum to zero when not number yDomainMin = 0; } if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin yDomainMax = yDomainMin; } if (yDomainMin === yDomainMax) { yDomainMin < 0 ? (yDomainMax = 0) : (yDomainMin = 0); } isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; // Cancel zerobased if axis_*_min / axis_*_max specified if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { isZeroBased = false; } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { yDomainMin = 0; } if (isAllNegative) { yDomainMax = 0; } } domainLength = Math.abs(yDomainMax - yDomainMin); padding_top = padding_bottom = domainLength * 0.1; if (typeof center !== 'undefined') { yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); yDomainMax = center + yDomainAbs; yDomainMin = center - yDomainAbs; } // add padding for data label if (showHorizontalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); diff = diffDomain($$.y.range()); ratio = [lengths[0] / diff, lengths[1] / diff]; padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); } else if (showVerticalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); var pixelsToAxisPadding = $$.getY(config["axis_" + axisId + "_type"], // input domain as pixels [0, config.axis_rotated ? $$.width : $$.height], // output range as axis padding [0, domainLength]); padding_top += pixelsToAxisPadding(lengths[1]); padding_bottom += pixelsToAxisPadding(lengths[0]); } if (axisId === 'y' && notEmpty(config.axis_y_padding)) { padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength); } if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength); } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { padding_bottom = yDomainMin; } if (isAllNegative) { padding_top = -yDomainMax; } } domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; return isInverted ? domain.reverse() : domain; }; ChartInternal.prototype.getXDomainMin = function (targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function (t) { return $$.d3.min(t.values, function (v) { return v.x; }); }); }; ChartInternal.prototype.getXDomainMax = function (targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); }); }; ChartInternal.prototype.getXDomainPadding = function (domain) { var $$ = this, config = $$.config, diff = domain[1] - domain[0], maxDataCount, padding, paddingLeft, paddingRight; if ($$.isCategorized()) { padding = 0; } else if ($$.hasType('bar')) { maxDataCount = $$.getMaxDataCount(); padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5; } else { padding = diff * 0.01; } if (typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding)) { paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; } else if (typeof config.axis_x_padding === 'number') { paddingLeft = paddingRight = config.axis_x_padding; } else { paddingLeft = paddingRight = padding; } return { left: paddingLeft, right: paddingRight }; }; ChartInternal.prototype.getXDomain = function (targets) { var $$ = this, xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], firstX = xDomain[0], lastX = xDomain[1], padding = $$.getXDomainPadding(xDomain), min = 0, max = 0; // show center of x domain if min and max are the same if (firstX - lastX === 0 && !$$.isCategorized()) { if ($$.isTimeSeries()) { firstX = new Date(firstX.getTime() * 0.5); lastX = new Date(lastX.getTime() * 1.5); } else { firstX = firstX === 0 ? 1 : firstX * 0.5; lastX = lastX === 0 ? -1 : lastX * 1.5; } } if (firstX || firstX === 0) { min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; } if (lastX || lastX === 0) { max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; } return [min, max]; }; ChartInternal.prototype.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) { var $$ = this, config = $$.config; if (withUpdateOrgXDomain) { $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); $$.orgXDomain = $$.x.domain(); if (config.zoom_enabled) { $$.zoom.update(); } $$.subX.domain($$.x.domain()); if ($$.brush) { $$.brush.updateScale($$.subX); } } if (withUpdateXDomain) { $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue()); } // Trim domain when too big by zoom mousemove event if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); } return $$.x.domain(); }; ChartInternal.prototype.trimXDomain = function (domain) { var zoomDomain = this.getZoomDomain(), min = zoomDomain[0], max = zoomDomain[1]; if (domain[0] <= min) { domain[1] = +domain[1] + (min - domain[0]); domain[0] = min; } if (max <= domain[1]) { domain[0] = +domain[0] - (domain[1] - max); domain[1] = max; } return domain; }; ChartInternal.prototype.drag = function (mouse) { var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3; var sx, sy, mx, my, minX, maxX, minY, maxY; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable if (!config.data_selection_multiple) { return; } // skip when single selection because drag is used for multiple selection sx = $$.dragStart[0]; sy = $$.dragStart[1]; mx = mouse[0]; my = mouse[1]; minX = Math.min(sx, mx); maxX = Math.max(sx, mx); minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my); maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my); main .select('.' + CLASS.dragarea) .attr('x', minX) .attr('y', minY) .attr('width', maxX - minX) .attr('height', maxY - minY); // TODO: binary search when multiple xs main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { if (!config.data_selection_isselectable(d)) { return; } var shape = d3.select(this), isSelected = shape.classed(CLASS.SELECTED), isIncluded = shape.classed(CLASS.INCLUDED), _x, _y, _w, _h, toggle, isWithin = false, box; if (shape.classed(CLASS.circle)) { _x = shape.attr('cx') * 1; _y = shape.attr('cy') * 1; toggle = $$.togglePoint; isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; } else if (shape.classed(CLASS.bar)) { box = getPathBox(this); _x = box.x; _y = box.y; _w = box.width; _h = box.height; toggle = $$.togglePath; isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); } else { // line/area selection not supported yet return; } if (isWithin ^ isIncluded) { shape.classed(CLASS.INCLUDED, !isIncluded); // TODO: included/unincluded callback here shape.classed(CLASS.SELECTED, !isSelected); toggle.call($$, !isSelected, shape, d, i); } }); }; ChartInternal.prototype.dragstart = function (mouse) { var $$ = this, config = $$.config; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable $$.dragStart = mouse; $$.main .select('.' + CLASS.chart) .append('rect') .attr('class', CLASS.dragarea) .style('opacity', 0.1); $$.dragging = true; }; ChartInternal.prototype.dragend = function () { var $$ = this, config = $$.config; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable $$.main .select('.' + CLASS.dragarea) .transition() .duration(100) .style('opacity', 0) .remove(); $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false); $$.dragging = false; }; ChartInternal.prototype.getYFormat = function (forArc) { var $$ = this, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; return function (v, ratio, id) { var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; return format.call($$, v, ratio); }; }; ChartInternal.prototype.yFormat = function (v) { var $$ = this, config = $$.config, format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; return format(v); }; ChartInternal.prototype.y2Format = function (v) { var $$ = this, config = $$.config, format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; return format(v); }; ChartInternal.prototype.defaultValueFormat = function (v) { return isValue(v) ? +v : ''; }; ChartInternal.prototype.defaultArcValueFormat = function (v, ratio) { return (ratio * 100).toFixed(1) + '%'; }; ChartInternal.prototype.dataLabelFormat = function (targetId) { var $$ = this, data_labels = $$.config.data_labels, format, defaultFormat = function (v) { return isValue(v) ? +v : ''; }; // find format according to axis id if (typeof data_labels.format === 'function') { format = data_labels.format; } else if (typeof data_labels.format === 'object') { if (data_labels.format[targetId]) { format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; } else { format = function () { return ''; }; } } else { format = defaultFormat; } return format; }; ChartInternal.prototype.initGrid = function () { var $$ = this, config = $$.config, d3 = $$.d3; $$.grid = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid); if (config.grid_x_show) { $$.grid.append('g').attr('class', CLASS.xgrids); } if (config.grid_y_show) { $$.grid.append('g').attr('class', CLASS.ygrids); } if (config.grid_focus_show) { $$.grid .append('g') .attr('class', CLASS.xgridFocus) .append('line') .attr('class', CLASS.xgridFocus); } $$.xgrid = d3.selectAll([]); if (!config.grid_lines_front) { $$.initGridLines(); } }; ChartInternal.prototype.initGridLines = function () { var $$ = this, d3 = $$.d3; $$.gridLines = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid + ' ' + CLASS.gridLines); $$.gridLines.append('g').attr('class', CLASS.xgridLines); $$.gridLines.append('g').attr('class', CLASS.ygridLines); $$.xgridLines = d3.selectAll([]); }; ChartInternal.prototype.updateXGrid = function (withoutUpdate) { var $$ = this, config = $$.config, d3 = $$.d3, xgridData = $$.generateGridData(config.grid_x_type, $$.x), tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; $$.xgridAttr = config.axis_rotated ? { x1: 0, x2: $$.width, y1: function (d) { return $$.x(d) - tickOffset; }, y2: function (d) { return $$.x(d) - tickOffset; } } : { x1: function (d) { return $$.x(d) + tickOffset; }, x2: function (d) { return $$.x(d) + tickOffset; }, y1: 0, y2: $$.height }; $$.xgridAttr.opacity = function () { var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1'); return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1; }; var xgrid = $$.main .select('.' + CLASS.xgrids) .selectAll('.' + CLASS.xgrid) .data(xgridData); var xgridEnter = xgrid .enter() .append('line') .attr('class', CLASS.xgrid) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', 0); $$.xgrid = xgridEnter.merge(xgrid); if (!withoutUpdate) { $$.xgrid .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); } xgrid.exit().remove(); }; ChartInternal.prototype.updateYGrid = function () { var $$ = this, config = $$.config, gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); var ygrid = $$.main .select('.' + CLASS.ygrids) .selectAll('.' + CLASS.ygrid) .data(gridValues); var ygridEnter = ygrid .enter() .append('line') // TODO: x1, x2, y1, y2, opacity need to be set here maybe .attr('class', CLASS.ygrid); $$.ygrid = ygridEnter.merge(ygrid); $$.ygrid .attr('x1', config.axis_rotated ? $$.y : 0) .attr('x2', config.axis_rotated ? $$.y : $$.width) .attr('y1', config.axis_rotated ? 0 : $$.y) .attr('y2', config.axis_rotated ? $$.height : $$.y); ygrid.exit().remove(); $$.smoothLines($$.ygrid, 'grid'); }; ChartInternal.prototype.gridTextAnchor = function (d) { return d.position ? d.position : 'end'; }; ChartInternal.prototype.gridTextDx = function (d) { return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4; }; ChartInternal.prototype.xGridTextX = function (d) { return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0; }; ChartInternal.prototype.yGridTextX = function (d) { return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width; }; ChartInternal.prototype.updateGrid = function (duration) { var $$ = this, main = $$.main, config = $$.config, xgridLine, xgridLineEnter, ygridLine, ygridLineEnter, xv = $$.xv.bind($$), yv = $$.yv.bind($$), xGridTextX = $$.xGridTextX.bind($$), yGridTextX = $$.yGridTextX.bind($$); // hide if arc type $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); if (config.grid_x_show) { $$.updateXGrid(); } xgridLine = main .select('.' + CLASS.xgridLines) .selectAll('.' + CLASS.xgridLine) .data(config.grid_x_lines); // enter xgridLineEnter = xgridLine .enter() .append('g') .attr('class', function (d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); }); xgridLineEnter .append('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 0); xgridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .attr('x', config.axis_rotated ? yGridTextX : xGridTextX) .attr('y', xv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // udpate $$.xgridLines = xgridLineEnter.merge(xgridLine); // done in d3.transition() of the end of this function // exit xgridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Y-Grid if (config.grid_y_show) { $$.updateYGrid(); } ygridLine = main .select('.' + CLASS.ygridLines) .selectAll('.' + CLASS.ygridLine) .data(config.grid_y_lines); // enter ygridLineEnter = ygridLine .enter() .append('g') .attr('class', function (d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); }); ygridLineEnter .append('line') .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 0); ygridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .attr('x', config.axis_rotated ? xGridTextX : yGridTextX) .attr('y', yv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // update $$.ygridLines = ygridLineEnter.merge(ygridLine); $$.ygridLines .select('line') .transition() .duration(duration) .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 1); $$.ygridLines .select('text') .transition() .duration(duration) .attr('x', config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)) .attr('y', yv) .text(function (d) { return d.text; }) .style('opacity', 1); // exit ygridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawGrid = function (withTransition, transition) { var $$ = this, config = $$.config, xv = $$.xv.bind($$), lines = $$.xgridLines.select('line'), texts = $$.xgridLines.select('text'); return [ (withTransition ? lines.transition(transition) : lines) .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 1), (withTransition ? texts.transition(transition) : texts) .attr('x', config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)) .attr('y', xv) .text(function (d) { return d.text; }) .style('opacity', 1) ]; }; ChartInternal.prototype.showXGridFocus = function (selectedData) { var $$ = this, config = $$.config, dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), xx = $$.xx.bind($$); if (!config.tooltip_show) { return; } // Hide when stanford plot exists if ($$.hasType('stanford') || $$.hasArcType()) { return; } focusEl .style('visibility', 'visible') .data([dataToShow[0]]) .attr(config.axis_rotated ? 'y1' : 'x1', xx) .attr(config.axis_rotated ? 'y2' : 'x2', xx); $$.smoothLines(focusEl, 'grid'); }; ChartInternal.prototype.hideXGridFocus = function () { this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); }; ChartInternal.prototype.updateXgridFocus = function () { var $$ = this, config = $$.config; $$.main .select('line.' + CLASS.xgridFocus) .attr('x1', config.axis_rotated ? 0 : -10) .attr('x2', config.axis_rotated ? $$.width : -10) .attr('y1', config.axis_rotated ? -10 : 0) .attr('y2', config.axis_rotated ? -10 : $$.height); }; ChartInternal.prototype.generateGridData = function (type, scale) { var $$ = this, gridData = [], xDomain, firstYear, lastYear, i, tickNum = $$.main .select('.' + CLASS.axisX) .selectAll('.tick') .size(); if (type === 'year') { xDomain = $$.getXDomain(); firstYear = xDomain[0].getFullYear(); lastYear = xDomain[1].getFullYear(); for (i = firstYear; i <= lastYear; i++) { gridData.push(new Date(i + '-01-01 00:00:00')); } } else { gridData = scale.ticks(10); if (gridData.length > tickNum) { // use only int gridData = gridData.filter(function (d) { return ('' + d).indexOf('.') < 0; }); } } return gridData; }; ChartInternal.prototype.getGridFilterToRemove = function (params) { return params ? function (line) { var found = false; [].concat(params).forEach(function (param) { if (('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class'])) { found = true; } }); return found; } : function () { return true; }; }; ChartInternal.prototype.removeGridLines = function (params, forX) { var $$ = this, config = $$.config, toRemove = $$.getGridFilterToRemove(params), toShow = function (line) { return !toRemove(line); }, classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; $$.main .select('.' + classLines) .selectAll('.' + classLine) .filter(toRemove) .transition() .duration(config.transition_duration) .style('opacity', 0) .remove(); if (forX) { config.grid_x_lines = config.grid_x_lines.filter(toShow); } else { config.grid_y_lines = config.grid_y_lines.filter(toShow); } }; ChartInternal.prototype.initEventRect = function () { var $$ = this, config = $$.config; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0); $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect); // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null); if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)); } } }; ChartInternal.prototype.redrawEventRect = function () { var $$ = this, d3 = $$.d3, config = $$.config; function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null); $$.hideXGridFocus(); $$.hideTooltip(); $$.unexpandCircles(); $$.unexpandBars(); } var isHoveringDataPoint = function (mouse, closest) { return closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity); }; var withName = function (d) { return (d ? $$.addName(Object.assign({}, d)) : null); }; // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null); $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on('mouseout', config.interaction_enabled ? function () { if (!config) { return; } // chart is destroyed if ($$.hasArcType()) { return; } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } mouseout(); } : null) .on('mousemove', config.interaction_enabled ? function () { // do nothing when dragging if ($$.dragging) { return; } var targetsToShow = $$.getTargetsToShow(); // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return; } var mouse = d3.mouse(this); var closest = withName($$.findClosestFromTargets(targetsToShow, mouse)); var isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest); // ensure onmouseout is always called if mousemove switch between 2 targets if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index)) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest); $$.mouseover = closest; } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null); // if tooltip not grouped, we want to display only data from closest data point var showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow); // find data to highlight var selectedData; if (showSingleDataPoint) { if (closest) { selectedData = [closest]; } } else { var closestByX = void 0; if (closest) { // reuse closest value closestByX = closest; } else { // try to find the closest value by X values from the mouse position var mouseX = config.axis_rotated ? mouse[1] : mouse[0]; closestByX = $$.findClosestFromTargetsByX(targetsToShow, $$.x.invert(mouseX)); } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x); } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout(); } // inject names for each point selectedData = selectedData.map(withName); // show tooltip $$.showTooltip(selectedData, this); // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles(); selectedData.forEach(function (d) { $$.expandCircles(d.index, d.id, false); }); } // expand bars $$.unexpandBars(); selectedData.forEach(function (d) { $$.expandBars(d.index, d.id, false); }); // Show xgrid focus line $$.showXGridFocus(selectedData); } : null) .on('click', config.interaction_enabled ? function () { var targetsToShow = $$.getTargetsToShow(); if ($$.hasArcType(targetsToShow)) { return; } var mouse = d3.mouse(this); var closest = withName($$.findClosestFromTargets(targetsToShow, mouse)); if (!isHoveringDataPoint(mouse, closest)) { return; } // select if selection enabled var sameXData; if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest]; } else { sameXData = $$.filterByX(targetsToShow, closest.x); } // toggle selected state sameXData.forEach(function (d) { $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function () { if (config.data_selection_grouped || $$.isWithinShape(this, d)) { $$.toggleShape(this, d, d.index); } }); }); // call data_onclick on the closest data point if (closest) { var shape = $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)) .select('.' + CLASS.shape + '-' + closest.index); config.data_onclick.call($$.api, closest, shape.node()); } } : null) .call(config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function () { $$.drag(d3.mouse(this)); }) .on('start', function () { $$.dragstart(d3.mouse(this)); }) .on('end', function () { $$.dragend(); }) : function () { }); }; ChartInternal.prototype.getMousePosition = function (data) { var $$ = this; return [$$.x(data.x), $$.getYScale(data.id)(data.value)]; }; ChartInternal.prototype.dispatchEvent = function (type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents'); event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); eventRect.dispatchEvent(event); }; ChartInternal.prototype.initLegend = function () { var $$ = this; $$.legendItemTextBox = {}; $$.legendHasRendered = false; $$.legend = $$.svg.append('g').attr('transform', $$.getTranslate('legend')); if (!$$.config.legend_show) { $$.legend.style('visibility', 'hidden'); $$.hiddenLegendIds = $$.mapToIds($$.data.targets); return; } // MEMO: call here to update legend box and tranlate for all // MEMO: translate will be updated by this, so transform not needed in updateLegend() $$.updateLegendWithDefaults(); }; ChartInternal.prototype.updateLegendWithDefaults = function () { var $$ = this; $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }); }; ChartInternal.prototype.updateSizeForLegend = function (legendHeight, legendWidth) { var $$ = this, config = $$.config, insetLegendPosition = { top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 }; $$.margin3 = { top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, right: NaN, bottom: 0, left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 }; }; ChartInternal.prototype.transformLegend = function (withTransition) { var $$ = this; (withTransition ? $$.legend.transition() : $$.legend).attr('transform', $$.getTranslate('legend')); }; ChartInternal.prototype.updateLegendStep = function (step) { this.legendStep = step; }; ChartInternal.prototype.updateLegendItemWidth = function (w) { this.legendItemWidth = w; }; ChartInternal.prototype.updateLegendItemHeight = function (h) { this.legendItemHeight = h; }; ChartInternal.prototype.getLegendWidth = function () { var $$ = this; return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0; }; ChartInternal.prototype.getLegendHeight = function () { var $$ = this, h = 0; if ($$.config.legend_show) { if ($$.isLegendRight) { h = $$.currentHeight; } else { h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); } } return h; }; ChartInternal.prototype.opacityForLegend = function (legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 1; }; ChartInternal.prototype.opacityForUnfocusedLegend = function (legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3; }; ChartInternal.prototype.toggleFocusLegend = function (targetIds, focus) { var $$ = this; targetIds = $$.mapToTargetIds(targetIds); $$.legend .selectAll('.' + CLASS.legendItem) .filter(function (id) { return targetIds.indexOf(id) >= 0; }) .classed(CLASS.legendItemFocused, focus) .transition() .duration(100) .style('opacity', function () { var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; return opacity.call($$, $$.d3.select(this)); }); }; ChartInternal.prototype.revertLegend = function () { var $$ = this, d3 = $$.d3; $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemFocused, false) .transition() .duration(100) .style('opacity', function () { return $$.opacityForLegend(d3.select(this)); }); }; ChartInternal.prototype.showLegend = function (targetIds) { var $$ = this, config = $$.config; if (!config.legend_show) { config.legend_show = true; $$.legend.style('visibility', 'visible'); if (!$$.legendHasRendered) { $$.updateLegendWithDefaults(); } } $$.removeHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('visibility', 'visible') .transition() .style('opacity', function () { return $$.opacityForLegend($$.d3.select(this)); }); }; ChartInternal.prototype.hideLegend = function (targetIds) { var $$ = this, config = $$.config; if (config.legend_show && isEmpty(targetIds)) { config.legend_show = false; $$.legend.style('visibility', 'hidden'); } $$.addHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('opacity', 0) .style('visibility', 'hidden'); }; ChartInternal.prototype.clearLegendItemTextBoxCache = function () { this.legendItemTextBox = {}; }; ChartInternal.prototype.updateLegend = function (targetIds, options, transitions) { var $$ = this, config = $$.config; var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; // Skip elements when their name is set to null targetIds = targetIds.filter(function (id) { return !isDefined(config.data_names[id]) || config.data_names[id] !== null; }); options = options || {}; withTransition = getOption(options, 'withTransition', true); withTransitionForTransform = getOption(options, 'withTransitionForTransform', true); function getTextBox(textElement, id) { if (!$$.legendItemTextBox[id]) { $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement); } return $$.legendItemTextBox[id]; } function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), margin, maxLength; // MEMO: care about condifion of step, totalLength function updateValues(id, withoutStep) { if (!withoutStep) { margin = (areaLength - totalLength - itemLength) / 2; if (margin < posMin) { margin = (areaLength - itemLength) / 2; totalLength = 0; step++; } } steps[id] = step; margins[step] = $$.isLegendInset ? 10 : margin; offsets[id] = totalLength; totalLength += itemLength; } if (reset) { totalLength = 0; step = 0; maxWidth = 0; maxHeight = 0; } if (config.legend_show && !$$.isLegendToShow(id)) { widths[id] = heights[id] = steps[id] = offsets[id] = 0; return; } widths[id] = itemWidth; heights[id] = itemHeight; if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; } if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; } maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; if (config.legend_equally) { Object.keys(widths).forEach(function (id) { widths[id] = maxWidth; }); Object.keys(heights).forEach(function (id) { heights[id] = maxHeight; }); margin = (areaLength - maxLength * targetIds.length) / 2; if (margin < posMin) { totalLength = 0; step = 0; targetIds.forEach(function (id) { updateValues(id); }); } else { updateValues(id, true); } } else { updateValues(id); } } if ($$.isLegendInset) { step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; $$.updateLegendStep(step); } if ($$.isLegendRight) { xForLegend = function (id) { return maxWidth * steps[id]; }; yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; } else if ($$.isLegendInset) { xForLegend = function (id) { return maxWidth * steps[id] + 10; }; yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; } else { xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; yForLegend = function (id) { return maxHeight * steps[id]; }; } xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; }; yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; xForLegendRect = function (id, i) { return xForLegend(id, i); }; yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; }; x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; }; yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; }; // Define g for legend area l = $$.legend .selectAll('.' + CLASS.legendItem) .data(targetIds) .enter() .append('g') .attr('class', function (id) { return $$.generateClass(CLASS.legendItem, id); }) .style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; }) .style('cursor', function () { return config.interaction_enabled ? 'pointer' : 'auto'; }) .on('click', config.interaction_enabled ? function (id) { if (config.legend_item_onclick) { config.legend_item_onclick.call($$, id); } else { if ($$.d3.event.altKey) { $$.api.hide(); $$.api.show(id); } else { $$.api.toggle(id); $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); } } } : null) .on('mouseover', config.interaction_enabled ? function (id) { if (config.legend_item_onmouseover) { config.legend_item_onmouseover.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, true); if (!$$.transiting && $$.isTargetToShow(id)) { $$.api.focus(id); } } } : null) .on('mouseout', config.interaction_enabled ? function (id) { if (config.legend_item_onmouseout) { config.legend_item_onmouseout.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, false); $$.api.revert(); } } : null); l.append('text') .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) .each(function (id, i) { updatePositions(this, id, i); }) .style('pointer-events', 'none') .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); l.append('rect') .attr('class', CLASS.legendItemEvent) .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); l.append('line') .attr('class', CLASS.legendItemTile) .style('stroke', $$.color) .style('pointer-events', 'none') .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { background = $$.legend .insert('g', '.' + CLASS.legendItem) .attr('class', CLASS.legendBackground) .append('rect'); } texts = $$.legend .selectAll('text') .data(targetIds) .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) // MEMO: needed for update .each(function (id, i) { updatePositions(this, id, i); }); (withTransition ? texts.transition() : texts) .attr('x', xForLegendText) .attr('y', yForLegendText); rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds); (withTransition ? rects.transition() : rects) .attr('width', function (id) { return widths[id]; }) .attr('height', function (id) { return heights[id]; }) .attr('x', xForLegendRect) .attr('y', yForLegendRect); tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds); (withTransition ? tiles.transition() : tiles) .style('stroke', $$.levelColor ? function (id) { return $$.levelColor($$.cache[id].values.reduce(function (total, item) { return total + item.value; }, 0)); } : $$.color) .attr('x1', x1ForLegendTile) .attr('y1', yForLegendTile) .attr('x2', x2ForLegendTile) .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background) .attr('height', $$.getLegendHeight() - 12) .attr('width', maxWidth * (step + 1) + 10); } // toggle legend state $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); }); // Update all to reflect change of legend $$.updateLegendItemWidth(maxWidth); $$.updateLegendItemHeight(maxHeight); $$.updateLegendStep(step); // Update size and scale $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(withTransitionForTransform, transitions); $$.legendHasRendered = true; }; ChartInternal.prototype.initRegion = function () { var $$ = this; $$.region = $$.main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.regions); }; ChartInternal.prototype.updateRegion = function (duration) { var $$ = this, config = $$.config; // hide if arc type $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); var mainRegion = $$.main .select('.' + CLASS.regions) .selectAll('.' + CLASS.region) .data(config.regions); var g = mainRegion.enter().append('g'); g.append('rect') .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }); g.append('text').text($$.labelRegion.bind($$)); $$.mainRegion = g.merge(mainRegion).attr('class', $$.classRegion.bind($$)); mainRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawRegion = function (withTransition, transition) { var $$ = this, regions = $$.mainRegion, regionLabels = $$.mainRegion.selectAll('text'); return [ (withTransition ? regions.transition(transition) : regions) .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }), (withTransition ? regionLabels.transition(transition) : regionLabels) .attr('x', $$.labelOffsetX.bind($$)) .attr('y', $$.labelOffsetY.bind($$)) .attr('transform', $$.labelTransform.bind($$)) .attr('style', 'text-anchor: left;') ]; }; ChartInternal.prototype.regionX = function (d) { var $$ = this, config = $$.config, xPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0; } else { xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0; } return xPos; }; ChartInternal.prototype.regionY = function (d) { var $$ = this, config = $$.config, yPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0; } else { yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0; } return yPos; }; ChartInternal.prototype.regionWidth = function (d) { var $$ = this, config = $$.config, start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width; } else { end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width; } return end < start ? 0 : end - start; }; ChartInternal.prototype.regionHeight = function (d) { var $$ = this, config = $$.config, start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height; } else { end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height; } return end < start ? 0 : end - start; }; ChartInternal.prototype.isRegionOnX = function (d) { return !d.axis || d.axis === 'x'; }; ChartInternal.prototype.labelRegion = function (d) { return 'label' in d ? d.label : ''; }; ChartInternal.prototype.labelTransform = function (d) { return 'vertical' in d && d.vertical ? 'rotate(90)' : ''; }; ChartInternal.prototype.labelOffsetX = function (d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? this.regionY(d) + paddingY : this.regionX(d) + paddingX; }; ChartInternal.prototype.labelOffsetY = function (d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? -(this.regionX(d) + paddingX) : this.regionY(d) + 10 + paddingY; }; function c3LogScale(d3, linearScale, logScale) { var PROJECTION = [0.01, 10]; if (!linearScale) { linearScale = d3.scaleLinear(); linearScale.range(PROJECTION); } if (!logScale) { logScale = d3.scaleLog(); logScale.domain(PROJECTION); logScale.nice(); } // copied from https://github.com/compute-io/logspace function logspace(a, b, len) { var arr, end, tmp, d; if (arguments.length < 3) { len = 10; } else { if (len === 0) { return []; } } // Calculate the increment: end = len - 1; d = (b - a) / end; // Build the output array... arr = new Array(len); tmp = a; arr[0] = Math.pow(10, tmp); for (var i = 1; i < end; i++) { tmp += d; arr[i] = Math.pow(10, tmp); } arr[end] = Math.pow(10, b); return arr; } function scale(x) { return logScale(linearScale(x)); } scale.domain = function (x) { if (!arguments.length) { return linearScale.domain(); } linearScale.domain(x); return scale; }; scale.range = function (x) { if (!arguments.length) { return logScale.range(); } logScale.range(x); return scale; }; scale.ticks = function (m) { return logspace(-2, 1, m || 10).map(function (v) { return linearScale.invert(v); }); }; scale.copy = function () { return c3LogScale(d3, linearScale.copy(), logScale.copy()); }; return scale; } ChartInternal.prototype.getScale = function (min, max, forTimeseries) { return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([ min, max ]); }; ChartInternal.prototype.getX = function (min, max, domain, offset) { var $$ = this, scale = $$.getScale(min, max, $$.isTimeSeries()), _scale = domain ? scale.domain(domain) : scale, key; // Define customized scale if categorized axis if ($$.isCategorized()) { offset = offset || function () { return 0; }; scale = function (d, raw) { var v = _scale(d) + offset(d); return raw ? v : Math.ceil(v); }; } else { scale = function (d, raw) { var v = _scale(d); return raw ? v : Math.ceil(v); }; } // define functions for (key in _scale) { scale[key] = _scale[key]; } scale.orgDomain = function () { return _scale.domain(); }; // define custom domain() for categorized axis if ($$.isCategorized()) { scale.domain = function (domain) { if (!arguments.length) { domain = this.orgDomain(); return [domain[0], domain[1] + 1]; } _scale.domain(domain); return scale; }; } return scale; }; /** * Creates and configures a D3 scale instance for the given type. * * By defaults it returns a Linear scale. * * @param {String} type Type of d3-scale to create. Type can be 'linear', 'time', 'timeseries' or 'log'. * @param {Array} domain The scale domain such as [from, to] * @param {Array} range The scale's range such as [from, to] * * @return A d3-scale instance */ ChartInternal.prototype.getY = function (type, domain, range) { var scale; if (type === 'timeseries' || type === 'time') { scale = this.d3.scaleTime(); } else if (type === 'log') { scale = c3LogScale(this.d3); } else if (type === 'linear' || type === undefined) { scale = this.d3.scaleLinear(); } else { throw new Error("Invalid Y axis type: \"" + type + "\""); } if (domain) { scale.domain(domain); } if (range) { scale.range(range); } return scale; }; ChartInternal.prototype.getYScale = function (id) { return this.axis.getId(id) === 'y2' ? this.y2 : this.y; }; ChartInternal.prototype.getSubYScale = function (id) { return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY; }; ChartInternal.prototype.updateScales = function () { var $$ = this, config = $$.config, forInit = !$$.x; // update edges $$.xMin = config.axis_rotated ? 1 : 0; $$.xMax = config.axis_rotated ? $$.height : $$.width; $$.yMin = config.axis_rotated ? 0 : $$.height; $$.yMax = config.axis_rotated ? $$.width : 1; $$.subXMin = $$.xMin; $$.subXMax = $$.xMax; $$.subYMin = config.axis_rotated ? 0 : $$.height2; $$.subYMax = config.axis_rotated ? $$.width2 : 1; // update scales $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { return $$.xAxis.tickOffset(); }); $$.y = $$.getY(config.axis_y_type, forInit ? config.axis_y_default : $$.y.domain(), [$$.yMin, $$.yMax]); $$.y2 = $$.getY(config.axis_y2_type, forInit ? config.axis_y2_default : $$.y2.domain(), [$$.yMin, $$.yMax]); $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { return d % 1 ? 0 : $$.subXAxis.tickOffset(); }); $$.subY = $$.getY(config.axis_y_type, forInit ? config.axis_y_default : $$.subY.domain(), [$$.subYMin, $$.subYMax]); $$.subY2 = $$.getY(config.axis_y2_type, forInit ? config.axis_y2_default : $$.subY2.domain(), [$$.subYMin, $$.subYMax]); // update axes $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); $$.xAxisTickValues = $$.axis.getXAxisTickValues(); $$.yAxisTickValues = $$.axis.getYAxisTickValues(); $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.yAxis = $$.axis.getYAxis('y', $$.y, $$.yOrient, $$.yAxisTickValues, config.axis_y_tick_outer); $$.y2Axis = $$.axis.getYAxis('y2', $$.y2, $$.y2Orient, $$.y2AxisTickValues, config.axis_y2_tick_outer); // Set initialized scales to brush and zoom if (!forInit) { if ($$.brush) { $$.brush.updateScale($$.subX); } } // update for arc if ($$.updateArc) { $$.updateArc(); } }; ChartInternal.prototype.selectPoint = function (target, d, i) { var $$ = this, config = $$.config, cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), r = $$.pointSelectR.bind($$); config.data_onselected.call($$.api, d, target.node()); // add selected-circle on low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .data([d]) .enter() .append('circle') .attr('class', function () { return $$.generateClass(CLASS.selectedCircle, i); }) .attr('cx', cx) .attr('cy', cy) .attr('stroke', function () { return $$.color(d); }) .attr('r', function (d) { return $$.pointSelectR(d) * 1.4; }) .transition() .duration(100) .attr('r', r); }; ChartInternal.prototype.unselectPoint = function (target, d, i) { var $$ = this; $$.config.data_onunselected.call($$.api, d, target.node()); // remove selected-circle from low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .transition() .duration(100) .attr('r', 0) .remove(); }; ChartInternal.prototype.togglePoint = function (selected, target, d, i) { selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); }; ChartInternal.prototype.selectPath = function (target, d) { var $$ = this; $$.config.data_onselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function () { return $$.d3.rgb($$.color(d)).brighter(0.75); }); } }; ChartInternal.prototype.unselectPath = function (target, d) { var $$ = this; $$.config.data_onunselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function () { return $$.color(d); }); } }; ChartInternal.prototype.togglePath = function (selected, target, d, i) { selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); }; ChartInternal.prototype.getToggle = function (that, d) { var $$ = this, toggle; if (that.nodeName === 'circle') { if ($$.isStepType(d)) { // circle is hidden in step chart, so treat as within the click area toggle = function () { }; // TODO: how to select step chart? } else { toggle = $$.togglePoint; } } else if (that.nodeName === 'path') { toggle = $$.togglePath; } return toggle; }; ChartInternal.prototype.toggleShape = function (that, d, i) { var $$ = this, d3 = $$.d3, config = $$.config, shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), toggle = $$.getToggle(that, d).bind($$); if (config.data_selection_enabled && config.data_selection_isselectable(d)) { if (!config.data_selection_multiple) { $$.main .selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : '')) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this); if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } }); } shape.classed(CLASS.SELECTED, !isSelected); toggle(!isSelected, shape, d, i); } }; ChartInternal.prototype.initBar = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); }; ChartInternal.prototype.updateTargetsForBar = function (targets) { var $$ = this, config = $$.config, mainBars, mainBarEnter, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classFocus = $$.classFocus.bind($$); mainBars = $$.main .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) .attr('class', function (d) { return classChartBar(d) + classFocus(d); }); mainBarEnter = mainBars .enter() .append('g') .attr('class', classChartBar) .style('pointer-events', 'none'); // Bars for each data mainBarEnter .append('g') .attr('class', classBars) .style('cursor', function (d) { return config.data_selection_isselectable(d) ? 'pointer' : null; }); }; ChartInternal.prototype.updateBar = function (durationForExit) { var $$ = this, barData = $$.barData.bind($$), classBar = $$.classBar.bind($$), initialOpacity = $$.initialOpacity.bind($$), color = function (d) { return $$.color(d.id); }; var mainBar = $$.main .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data(barData); var mainBarEnter = mainBar .enter() .append('path') .attr('class', classBar) .style('stroke', color) .style('fill', color); $$.mainBar = mainBarEnter.merge(mainBar).style('opacity', initialOpacity); mainBar .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawBar = function (drawBar, withTransition, transition) { var $$ = this; return [ (withTransition ? this.mainBar.transition(transition) : this.mainBar) .attr('d', drawBar) .style('stroke', this.color) .style('fill', this.color) .style('opacity', function (d) { return ($$.isTargetToShow(d.id) ? 1 : 0); }) ]; }; ChartInternal.prototype.getBarW = function (axis, barTargetsNum) { var $$ = this, config = $$.config, w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0; return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w; }; ChartInternal.prototype.getBars = function (i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')); }; ChartInternal.prototype.expandBars = function (i, id, reset) { var $$ = this; if (reset) { $$.unexpandBars(); } $$.getBars(i, id).classed(CLASS.EXPANDED, true); }; ChartInternal.prototype.unexpandBars = function (i) { var $$ = this; $$.getBars(i).classed(CLASS.EXPANDED, false); }; ChartInternal.prototype.generateDrawBar = function (barIndices, isSub) { var $$ = this, config = $$.config, getPoints = $$.generateGetBarPoints(barIndices, isSub); return function (d, i) { // 4 points that make a bar var points = getPoints(d, i); // switch points if axis is rotated, not applicable for sub chart var indexX = config.axis_rotated ? 1 : 0; var indexY = config.axis_rotated ? 0 : 1; var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z'; return path; }; }; ChartInternal.prototype.generateGetBarPoints = function (barIndices, isSub) { var $$ = this, axis = isSub ? $$.subXAxis : $$.xAxis, barTargetsNum = barIndices.__max__ + 1, barW = $$.getBarW(axis, barTargetsNum), barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), barY = $$.getShapeY(!!isSub), barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), barSpaceOffset = barW * ($$.config.bar_space / 2), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = barOffset(d, i) || y0, // offset is for stacked bar chart posX = barX(d), posY = barY(d); // fix posY not to overflow opposite quadrant if ($$.config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } posY -= y0 - offset; // 4 points that make a bar return [ [posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY], [posX + barW - barSpaceOffset, posY], [posX + barW - barSpaceOffset, offset] ]; }; }; /** * Returns whether the data point is within the given bar shape. * * @param mouse * @param barShape * @return {boolean} */ ChartInternal.prototype.isWithinBar = function (mouse, barShape) { return isWithinBox(mouse, getBBox(barShape), 2); }; ChartInternal.prototype.getShapeIndices = function (typeFilter) { var $$ = this, config = $$.config, indices = {}, i = 0, j, k; $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) { for (j = 0; j < config.data_groups.length; j++) { if (config.data_groups[j].indexOf(d.id) < 0) { continue; } for (k = 0; k < config.data_groups[j].length; k++) { if (config.data_groups[j][k] in indices) { indices[d.id] = indices[config.data_groups[j][k]]; break; } } } if (isUndefined(indices[d.id])) { indices[d.id] = i++; } }); indices.__max__ = i - 1; return indices; }; ChartInternal.prototype.getShapeX = function (offset, targetsNum, indices, isSub) { var $$ = this, scale = isSub ? $$.subX : $$.x; return function (d) { var index = d.id in indices ? indices[d.id] : 0; return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0; }; }; ChartInternal.prototype.getShapeY = function (isSub) { var $$ = this; return function (d) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); return scale($$.isTargetNormalized(d.id) ? $$.getRatio('index', d, true) : d.value); }; }; ChartInternal.prototype.getShapeOffset = function (typeFilter, indices, isSub) { var $$ = this, targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))), targetIds = targets.map(function (t) { return t.id; }); return function (d, i) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), y0 = scale(0), offset = y0; targets.forEach(function (t) { var rowValues = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; var isTargetNormalized = $$.isTargetNormalized(d.id); var values = rowValues.map(function (v) { return isTargetNormalized ? $$.getRatio('index', v, true) : v.value; }); if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; } if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { // check if the x values line up if (isUndefined(rowValues[i]) || +rowValues[i].x !== +d.x) { // "+" for timeseries // if not, try to find the value that does line up i = -1; rowValues.forEach(function (v, j) { var x1 = v.x.constructor === Date ? +v.x : v.x; var x2 = d.x.constructor === Date ? +d.x : d.x; if (x1 === x2) { i = j; } }); } if (i in rowValues && rowValues[i].value * d.value >= 0) { offset += scale(values[i]) - y0; } } }); return offset; }; }; ChartInternal.prototype.isWithinShape = function (that, d) { var $$ = this, shape = $$.d3.select(that), isWithin; if (!$$.isTargetToShow(d.id)) { isWithin = false; } else if (that.nodeName === 'circle') { isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); } else if (that.nodeName === 'path') { isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true; } return isWithin; }; ChartInternal.prototype.getInterpolate = function (d) { var $$ = this, d3 = $$.d3, types = { linear: d3.curveLinear, 'linear-closed': d3.curveLinearClosed, basis: d3.curveBasis, 'basis-open': d3.curveBasisOpen, 'basis-closed': d3.curveBasisClosed, bundle: d3.curveBundle, cardinal: d3.curveCardinal, 'cardinal-open': d3.curveCardinalOpen, 'cardinal-closed': d3.curveCardinalClosed, monotone: d3.curveMonotoneX, step: d3.curveStep, 'step-before': d3.curveStepBefore, 'step-after': d3.curveStepAfter }, type; if ($$.isSplineType(d)) { type = types[$$.config.spline_interpolation_type] || types.cardinal; } else if ($$.isStepType(d)) { type = types[$$.config.line_step_type]; } else { type = types.linear; } return type; }; ChartInternal.prototype.initLine = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); }; ChartInternal.prototype.updateTargetsForLine = function (targets) { var $$ = this, config = $$.config, mainLines, mainLineEnter, classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$), classCircles = $$.classCircles.bind($$), classFocus = $$.classFocus.bind($$); mainLines = $$.main .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) .attr('class', function (d) { return classChartLine(d) + classFocus(d); }); mainLineEnter = mainLines .enter() .append('g') .attr('class', classChartLine) .style('opacity', 0) .style('pointer-events', 'none'); // Lines for each data mainLineEnter.append('g').attr('class', classLines); // Areas mainLineEnter.append('g').attr('class', classAreas); // Circles for each data point on lines mainLineEnter.append('g').attr('class', function (d) { return $$.generateClass(CLASS.selectedCircles, d.id); }); mainLineEnter .append('g') .attr('class', classCircles) .style('cursor', function (d) { return config.data_selection_isselectable(d) ? 'pointer' : null; }); // Update date for selected circles targets.forEach(function (t) { $$.main .selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)) .selectAll('.' + CLASS.selectedCircle) .each(function (d) { d.value = t.values[d.index].value; }); }); // MEMO: can not keep same color... //mainLineUpdate.exit().remove(); }; ChartInternal.prototype.updateLine = function (durationForExit) { var $$ = this; var mainLine = $$.main .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var mainLineEnter = mainLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); $$.mainLine = mainLineEnter .merge(mainLine) .style('opacity', $$.initialOpacity.bind($$)) .style('shape-rendering', function (d) { return $$.isStepType(d) ? 'crispEdges' : ''; }) .attr('transform', null); mainLine .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawLine = function (drawLine, withTransition, transition) { return [ (withTransition ? this.mainLine.transition(transition) : this.mainLine) .attr('d', drawLine) .style('stroke', this.color) .style('opacity', 1) ]; }; ChartInternal.prototype.generateDrawLine = function (lineIndices, isSub) { var $$ = this, config = $$.config, line = $$.d3.line(), getPoints = $$.generateGetLinePoints(lineIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, yValue = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value); }; line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); if (!config.line_connectNull) { line = line.defined(function (d) { return d.value != null; }); } return function (d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x = isSub ? $$.subX : $$.x, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path; if ($$.isLineType(d)) { if (config.data_regions[d.id]) { path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); } else { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = line.curve($$.getInterpolate(d))(values); } } else { if (values[0]) { x0 = x(values[0].x); y0 = y(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0'; }; }; ChartInternal.prototype.generateGetLinePoints = function (lineIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, lineTargetsNum = lineIndices.__max__ + 1, x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), y = $$.getShapeY(!!isSub), lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = lineOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the line position return [ [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)] // needed for compatibility ]; }; }; ChartInternal.prototype.lineWithRegions = function (d, x, y, _regions) { var $$ = this, config = $$.config, prev = -1, i, j, s = 'M', sWithRegion, xp, yp, dx, dy, dd, diff, diffx2, xOffset = $$.isCategorized() ? 0.5 : 0, xValue, yValue, regions = []; function isWithinRegions(x, regions) { var i; for (i = 0; i < regions.length; i++) { if (regions[i].start < x && x <= regions[i].end) { return true; } } return false; } // Check start/end of regions if (isDefined(_regions)) { for (i = 0; i < _regions.length; i++) { regions[i] = {}; if (isUndefined(_regions[i].start)) { regions[i].start = d[0].x; } else { regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; } if (isUndefined(_regions[i].end)) { regions[i].end = d[d.length - 1].x; } else { regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; } } } // Set scales xValue = config.axis_rotated ? function (d) { return y(d.value); } : function (d) { return x(d.x); }; yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); }; // Define svg generator function for region function generateM(points) { return ('M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1]); } if ($$.isTimeSeries()) { sWithRegion = function (d0, d1, j, diff) { var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, xv0 = new Date(x0 + x_diff * j), xv1 = new Date(x0 + x_diff * (j + diff)), points; if (config.axis_rotated) { points = [ [y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)] ]; } else { points = [ [x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))] ]; } return generateM(points); }; } else { sWithRegion = function (d0, d1, j, diff) { var points; if (config.axis_rotated) { points = [ [y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))] ]; } else { points = [ [x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))] ]; } return generateM(points); }; } // Generate for (i = 0; i < d.length; i++) { // Draw as normal if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { s += ' ' + xValue(d[i]) + ' ' + yValue(d[i]); } // Draw with region // TODO: Fix for horizotal charts else { xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries()); yp = $$.getScale(d[i - 1].value, d[i].value); dx = x(d[i].x) - x(d[i - 1].x); dy = y(d[i].value) - y(d[i - 1].value); dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); diff = 2 / dd; diffx2 = diff * 2; for (j = diff; j <= 1; j += diffx2) { s += sWithRegion(d[i - 1], d[i], j, diff); } } prev = d[i].x; } return s; }; ChartInternal.prototype.updateArea = function (durationForExit) { var $$ = this, d3 = $$.d3; var mainArea = $$.main .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var mainAreaEnter = mainArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); $$.mainArea = mainAreaEnter .merge(mainArea) .style('opacity', $$.orgAreaOpacity); mainArea .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawArea = function (drawArea, withTransition, transition) { return [ (withTransition ? this.mainArea.transition(transition) : this.mainArea) .attr('d', drawArea) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) ]; }; ChartInternal.prototype.generateDrawArea = function (areaIndices, isSub) { var $$ = this, config = $$.config, area = $$.d3.area(), getPoints = $$.generateGetAreaPoints(areaIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, value0 = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)); }, value1 = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value); }; area = config.axis_rotated ? area .x0(value0) .x1(value1) .y(xValue) : area .x(xValue) .y0(config.area_above ? 0 : value0) .y1(value1); if (!config.line_connectNull) { area = area.defined(function (d) { return d.value !== null; }); } return function (d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path; if ($$.isAreaType(d)) { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = area.curve($$.getInterpolate(d))(values); } else { if (values[0]) { x0 = $$.x(values[0].x); y0 = $$.getYScale(d.id)(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0'; }; }; ChartInternal.prototype.getAreaBaseValue = function () { return 0; }; ChartInternal.prototype.generateGetAreaPoints = function (areaIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, areaTargetsNum = areaIndices.__max__ + 1, x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), y = $$.getShapeY(!!isSub), areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = areaOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the area position return [ [posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, offset] // needed for compatibility ]; }; }; ChartInternal.prototype.updateCircle = function (cx, cy) { var $$ = this; var mainCircle = $$.main .selectAll('.' + CLASS.circles) .selectAll('.' + CLASS.circle) .data($$.lineOrScatterOrStanfordData.bind($$)); var mainCircleEnter = mainCircle .enter() .append('circle') .attr('shape-rendering', $$.isStanfordGraphType() ? 'crispEdges' : '') .attr('class', $$.classCircle.bind($$)) .attr('cx', cx) .attr('cy', cy) .attr('r', $$.pointR.bind($$)) .style('color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color); $$.mainCircle = mainCircleEnter .merge(mainCircle) .style('opacity', $$.isStanfordGraphType() ? 1 : $$.initialOpacityForCircle.bind($$)); mainCircle.exit().style('opacity', 0); }; ChartInternal.prototype.redrawCircle = function (cx, cy, withTransition, transition) { var $$ = this, selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle); return [ (withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle) .style('opacity', this.opacityForCircle.bind($$)) .style('color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color) .attr('cx', cx) .attr('cy', cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles) .attr('cx', cx) .attr('cy', cy) ]; }; ChartInternal.prototype.circleX = function (d) { return d.x || d.x === 0 ? this.x(d.x) : null; }; ChartInternal.prototype.updateCircleY = function () { var $$ = this, lineIndices, getPoints; if ($$.config.data_groups.length > 0) { (lineIndices = $$.getShapeIndices($$.isLineType)), (getPoints = $$.generateGetLinePoints(lineIndices)); $$.circleY = function (d, i) { return getPoints(d, i)[0][1]; }; } else { $$.circleY = function (d) { return $$.getYScale(d.id)(d.value); }; } }; ChartInternal.prototype.getCircles = function (i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')); }; ChartInternal.prototype.expandCircles = function (i, id, reset) { var $$ = this, r = $$.pointExpandedR.bind($$); if (reset) { $$.unexpandCircles(); } $$.getCircles(i, id) .classed(CLASS.EXPANDED, true) .attr('r', r); }; ChartInternal.prototype.unexpandCircles = function (i) { var $$ = this, r = $$.pointR.bind($$); $$.getCircles(i) .filter(function () { return $$.d3.select(this).classed(CLASS.EXPANDED); }) .classed(CLASS.EXPANDED, false) .attr('r', r); }; ChartInternal.prototype.pointR = function (d) { var $$ = this, config = $$.config; return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r; }; ChartInternal.prototype.pointExpandedR = function (d) { var $$ = this, config = $$.config; if (config.point_focus_expand_enabled) { return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75; } else { return $$.pointR(d); } }; ChartInternal.prototype.pointSelectR = function (d) { var $$ = this, config = $$.config; return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4; }; ChartInternal.prototype.isWithinCircle = function (that, r) { var d3 = this.d3, mouse = d3.mouse(that), d3_this = d3.select(that), cx = +d3_this.attr('cx'), cy = +d3_this.attr('cy'); return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r; }; ChartInternal.prototype.isWithinStep = function (that, y) { return Math.abs(y - this.d3.mouse(that)[1]) < 30; }; ChartInternal.prototype.getCurrentWidth = function () { var $$ = this, config = $$.config; return config.size_width ? config.size_width : $$.getParentWidth(); }; ChartInternal.prototype.getCurrentHeight = function () { var $$ = this, config = $$.config, h = config.size_height ? config.size_height : $$.getParentHeight(); return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1); }; ChartInternal.prototype.getCurrentPaddingTop = function () { var $$ = this, config = $$.config, padding = isValue(config.padding_top) ? config.padding_top : 0; if ($$.title && $$.title.node()) { padding += $$.getTitlePadding(); } return padding; }; ChartInternal.prototype.getCurrentPaddingBottom = function () { var config = this.config; return isValue(config.padding_bottom) ? config.padding_bottom : 0; }; ChartInternal.prototype.getCurrentPaddingLeft = function (withoutRecompute) { var $$ = this, config = $$.config; if (isValue(config.padding_left)) { return config.padding_left; } else if (config.axis_rotated) { return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40); } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1; } else { return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)); } }; ChartInternal.prototype.getCurrentPaddingRight = function () { var $$ = this, config = $$.config, padding = 0, defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; if (isValue(config.padding_right)) { padding = config.padding_right + 1; // 1 is needed not to hide tick line } else if (config.axis_rotated) { padding = defaultPadding + legendWidthOnRight; } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated padding = 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); } else { padding = ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; } if ($$.colorScale && $$.colorScale.node()) { padding += $$.getColorScalePadding(); } return padding; }; ChartInternal.prototype.getParentRectValue = function (key) { var parent = this.selectChart.node(), v; while (parent && parent.tagName !== 'BODY') { try { v = parent.getBoundingClientRect()[key]; } catch (e) { if (key === 'width') { // In IE in certain cases getBoundingClientRect // will cause an "unspecified error" v = parent.offsetWidth; } } if (v) { break; } parent = parent.parentNode; } return v; }; ChartInternal.prototype.getParentWidth = function () { return this.getParentRectValue('width'); }; ChartInternal.prototype.getParentHeight = function () { var h = this.selectChart.style('height'); return h.indexOf('px') > 0 ? +h.replace('px', '') : 0; }; ChartInternal.prototype.getSvgLeft = function (withoutRecompute) { var $$ = this, config = $$.config, hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, leftAxis = $$.main.select('.' + leftAxisClass).node(), svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, chartRect = $$.selectChart.node().getBoundingClientRect(), hasArc = $$.hasArcType(), svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); return svgLeft > 0 ? svgLeft : 0; }; ChartInternal.prototype.getAxisWidthByAxisId = function (id, withoutRecompute) { var $$ = this, position = $$.axis.getLabelPositionById(id); return ($$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40)); }; ChartInternal.prototype.getHorizontalAxisHeight = function (axisId, isSubchart) { var $$ = this, config = $$.config, h = 30; if (axisId === 'x' && !(isDefined(isSubchart) && isSubchart ? config.subchart_axis_x_show : config.axis_x_show)) { return 8; } if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; } if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; } // Calculate x axis height when tick rotated if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_x_tick_rotate))) / 180); } // Calculate y axis height when tick rotated if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_y_tick_rotate))) / 180); } return (h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0)); }; ChartInternal.prototype.initBrush = function (scale) { var $$ = this, d3 = $$.d3; // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function () { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return; } $$.redrawForBrush(); }) .on('end', function () { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return; } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); } }); $$.brush.updateExtent = function () { var range = this.scale.range(), extent; if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ]; } else { extent = [ [range[0], 0], [range[1], $$.height2] ]; } this.extent(extent); return this; }; $$.brush.updateScale = function (scale) { this.scale = scale; return this; }; $$.brush.update = function (scale) { this.updateScale(scale || $$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call(this); }; $$.brush.clear = function () { $$.context.select('.' + CLASS.brush).call($$.brush.move, null); }; $$.brush.selection = function () { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()); }; $$.brush.selectionAsValue = function (selectionAsValue, withTransition) { var selection, brush; if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ]; brush = $$.context.select('.' + CLASS.brush); if (withTransition) { brush = brush.transition(); } $$.brush.move(brush, selection); } return []; } selection = $$.brush.selection() || [0, 0]; return [this.scale.invert(selection[0]), this.scale.invert(selection[1])]; }; $$.brush.empty = function () { var selection = $$.brush.selection(); return !selection || selection[0] === selection[1]; }; return $$.brush.updateScale(scale); }; ChartInternal.prototype.initSubchart = function () { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))); // set style context.style('visibility', 'visible'); // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart); // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush); // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis) .style('visibility', config.subchart_axis_x_show ? 'visible' : 'hidden'); }; ChartInternal.prototype.initSubchartBrush = function () { var $$ = this; // Add extent rect for Brush $$.initBrush($$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call($$.brush); }; ChartInternal.prototype.updateTargetsForSubchart = function (targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$); //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets); contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0); contextBarEnter.merge(contextBar).attr('class', classChartBar); // Bars for each data contextBarEnter.append('g').attr('class', classBars); //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets); contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0); contextLineEnter.merge(contextLine).attr('class', classChartLine); // Lines for each data contextLineEnter.append('g').attr('class', classLines); // Area contextLineEnter.append('g').attr('class', classAreas); //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr(config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2); }; ChartInternal.prototype.updateBarForSubchart = function (durationForExit) { var $$ = this; var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)); var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color); contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar) .attr('d', drawBarOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateLineForSubchart = function (durationForExit) { var $$ = this; var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine) .attr('d', drawLineOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) { var $$ = this, d3 = $$.d3; var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0); }; ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity); }; ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub; // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()); } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()); } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true); drawBarOnSub = $$.generateDrawBar(barIndices, true); drawLineOnSub = $$.generateDrawLine(lineIndices, true); $$.updateBarForSubchart(duration); $$.updateLineForSubchart(duration); $$.updateAreaForSubchart(duration); $$.redrawBarForSubchart(drawBarOnSub, duration, duration); $$.redrawLineForSubchart(drawLineOnSub, duration, duration); $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); } }; ChartInternal.prototype.redrawForBrush = function () { var $$ = this, x = $$.x, d3 = $$.d3, s; $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }); // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range(); $$.main .select('.' + CLASS.eventRect) .call($$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0)); $$.config.subchart_onbrush.call($$.api, x.orgDomain()); }; ChartInternal.prototype.transformContext = function (withTransition, transitions) { var $$ = this, subXAxis; if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX; } else { subXAxis = $$.context.select('.' + CLASS.axisX); if (withTransition) { subXAxis = subXAxis.transition(); } } $$.context.attr('transform', $$.getTranslate('context')); subXAxis.attr('transform', $$.getTranslate('subx')); }; ChartInternal.prototype.getDefaultSelection = function () { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection; if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])]; } return selection; }; ChartInternal.prototype.removeSubchart = function () { var $$ = this; $$.brush = null; $$.context.remove(); $$.context = null; }; ChartInternal.prototype.initText = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartTexts); $$.mainText = $$.d3.selectAll([]); }; ChartInternal.prototype.updateTargetsForText = function (targets) { var $$ = this, classChartText = $$.classChartText.bind($$), classTexts = $$.classTexts.bind($$), classFocus = $$.classFocus.bind($$); var mainText = $$.main .select('.' + CLASS.chartTexts) .selectAll('.' + CLASS.chartText) .data(targets); var mainTextEnter = mainText .enter() .append('g') .attr('class', classChartText) .style('opacity', 0) .style('pointer-events', 'none'); mainTextEnter.append('g').attr('class', classTexts); mainTextEnter.merge(mainText).attr('class', function (d) { return classChartText(d) + classFocus(d); }); }; ChartInternal.prototype.updateText = function (xForText, yForText, durationForExit) { var $$ = this, config = $$.config, barOrLineData = $$.barOrLineData.bind($$), classText = $$.classText.bind($$); var mainText = $$.main .selectAll('.' + CLASS.texts) .selectAll('.' + CLASS.text) .data(barOrLineData); var mainTextEnter = mainText .enter() .append('text') .attr('class', classText) .attr('text-anchor', function (d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle'; }) .style('stroke', 'none') .attr('x', xForText) .attr('y', yForText) .style('fill', function (d) { return $$.color(d); }) .style('fill-opacity', 0); $$.mainText = mainTextEnter.merge(mainText).text(function (d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j); }); mainText .exit() .transition() .duration(durationForExit) .style('fill-opacity', 0) .remove(); }; ChartInternal.prototype.redrawText = function (xForText, yForText, forFlow, withTransition, transition) { return [ (withTransition ? this.mainText.transition(transition) : this.mainText) .attr('x', xForText) .attr('y', yForText) .style('fill', this.color) .style('fill-opacity', forFlow ? 0 : this.opacityForText.bind(this)) ]; }; ChartInternal.prototype.getTextRect = function (text, cls, element) { var dummy = this.d3 .select('body') .append('div') .classed('c3', true), svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0), font = this.d3.select(element).style('font'), rect; svg .selectAll('.dummy') .data([text]) .enter() .append('text') .classed(cls ? cls : '', true) .style('font', font) .text(text) .each(function () { rect = getBBox(this); }); dummy.remove(); return rect; }; ChartInternal.prototype.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) { var $$ = this, getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false), getter = forX ? $$.getXForText : $$.getYForText; return function (d, i) { var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; return getter.call($$, getPoints(d, i), d, this); }; }; ChartInternal.prototype.getXForText = function (points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding; if ($$.config.axis_rotated) { padding = $$.isBarType(d) ? 4 : 6; xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); } else { xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; } // show labels regardless of the domain if value is null if (d.value === null) { if (xPos > $$.width) { xPos = $$.width - box.width; } else if (xPos < 0) { xPos = 4; } } return xPos; }; ChartInternal.prototype.getYForText = function (points, d, textElement) { var $$ = this, box = getBBox(textElement), yPos; if ($$.config.axis_rotated) { yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; } else { yPos = points[2][1]; if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { yPos += box.height; if ($$.isBarType(d) && $$.isSafari()) { yPos -= 3; } else if (!$$.isBarType(d) && $$.isChrome()) { yPos += 3; } } else { yPos += $$.isBarType(d) ? -3 : -6; } } // show labels regardless of the domain if value is null if (d.value === null && !$$.config.axis_rotated) { if (yPos < box.height) { yPos = box.height; } else if (yPos > this.height) { yPos = this.height - 4; } } return yPos; }; ChartInternal.prototype.initTitle = function () { var $$ = this; $$.title = $$.svg .append('text') .text($$.config.title_text) .attr('class', $$.CLASS.title); }; ChartInternal.prototype.redrawTitle = function () { var $$ = this; $$.title.attr('x', $$.xForTitle.bind($$)).attr('y', $$.yForTitle.bind($$)); }; ChartInternal.prototype.xForTitle = function () { var $$ = this, config = $$.config, position = config.title_position || 'left', x; if (position.indexOf('right') >= 0) { x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right; } else if (position.indexOf('center') >= 0) { x = Math.max(($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2, 0); } else { // left x = config.title_padding.left; } return x; }; ChartInternal.prototype.yForTitle = function () { var $$ = this; return ($$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()) .height); }; ChartInternal.prototype.getTitlePadding = function () { var $$ = this; return $$.yForTitle() + $$.config.title_padding.bottom; }; function powerOfTen(d) { return d / Math.pow(10, Math.ceil(Math.log(d) / Math.LN10 - 1e-12)) === 1; } ChartInternal.prototype.drawColorScale = function () { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], barWidth, barHeight, axis, points, legendAxis, axisScale, inverseScale, height; barWidth = !isNaN(config.stanford_scaleWidth) ? config.stanford_scaleWidth : 20; barHeight = 5; if (barHeight < 0 || barWidth < 0) { throw Error("Colorscale's barheight and barwidth must be greater than 0."); } height = $$.height - config.stanford_padding.bottom - config.stanford_padding.top; points = d3.range(config.stanford_padding.bottom, height, barHeight); inverseScale = d3 .scaleSequential(target.colors) .domain([points[points.length - 1], points[0]]); if ($$.colorScale) { $$.colorScale.remove(); } $$.colorScale = $$.svg .append('g') .attr('width', 50) .attr('height', height) .attr('class', CLASS.colorScale); $$.colorScale .append('g') .attr('transform', "translate(0, " + config.stanford_padding.top + ")") .selectAll('bars') .data(points) .enter() .append('rect') .attr('y', function (d, i) { return i * barHeight; }) .attr('x', 0) .attr('width', barWidth) .attr('height', barHeight) .attr('fill', function (d) { return inverseScale(d); }); // Legend Axis axisScale = d3 .scaleLog() .domain([target.minEpochs, target.maxEpochs]) .range([ points[0] + config.stanford_padding.top + points[points.length - 1] + barHeight - 1, points[0] + config.stanford_padding.top ]); legendAxis = d3.axisRight(axisScale); if (config.stanford_scaleFormat === 'pow10') { legendAxis.tickValues([1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]); } else if (isFunction(config.stanford_scaleFormat)) { legendAxis.tickFormat(config.stanford_scaleFormat); } else { legendAxis.tickFormat(d3.format('d')); } if (isFunction(config.stanford_scaleValues)) { legendAxis.tickValues(config.stanford_scaleValues(target.minEpochs, target.maxEpochs)); } // Draw Axis axis = $$.colorScale .append('g') .attr('class', 'legend axis') .attr('transform', "translate(" + barWidth + ",0)") .call(legendAxis); if (config.stanford_scaleFormat === 'pow10') { axis .selectAll('.tick text') .text(null) .filter(powerOfTen) .text(10) .append('tspan') .attr('dy', '-.7em') // https://bl.ocks.org/mbostock/6738229 .text(function (d) { return Math.round(Math.log(d) / Math.LN10); }); } $$.colorScale.attr('transform', "translate(" + ($$.currentWidth - $$.xForColorScale()) + ", 0)"); }; ChartInternal.prototype.xForColorScale = function () { var $$ = this; return $$.config.stanford_padding.right + getBBox($$.colorScale.node()).width; }; ChartInternal.prototype.getColorScalePadding = function () { var $$ = this; return $$.xForColorScale() + $$.config.stanford_padding.left + 20; }; ChartInternal.prototype.isStanfordGraphType = function () { var $$ = this; return $$.config.data_type === 'stanford'; }; ChartInternal.prototype.initStanfordData = function () { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], epochs, maxEpochs, minEpochs; // Make larger values appear on top target.values.sort(compareEpochs); // Get array of epochs epochs = target.values.map(function (a) { return a.epochs; }); minEpochs = !isNaN(config.stanford_scaleMin) ? config.stanford_scaleMin : d3.min(epochs); maxEpochs = !isNaN(config.stanford_scaleMax) ? config.stanford_scaleMax : d3.max(epochs); if (minEpochs > maxEpochs) { throw Error('Number of minEpochs has to be smaller than maxEpochs'); } target.colors = isFunction(config.stanford_colors) ? config.stanford_colors : d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)); target.colorscale = d3 .scaleSequentialLog(target.colors) .domain([minEpochs, maxEpochs]); target.minEpochs = minEpochs; target.maxEpochs = maxEpochs; }; ChartInternal.prototype.getStanfordPointColor = function (d) { var $$ = this, target = $$.data.targets[0]; return target.colorscale(d.epochs); }; // http://jsfiddle.net/Xotic750/KtzLq/ ChartInternal.prototype.getCentroid = function (points) { var area = getRegionArea(points); var x = 0, y = 0, i, j, f, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; f = point1.x * point2.y - point2.x * point1.y; x += (point1.x + point2.x) * f; y += (point1.y + point2.y) * f; } f = area * 6; return { x: x / f, y: y / f }; }; ChartInternal.prototype.getStanfordTooltipTitle = function (d) { var $$ = this, labelX = $$.axis.getLabelText('x'), labelY = $$.axis.getLabelText('y'); return "\n " + (labelX ? sanitise(labelX) : 'x') + "" + d.x + "\n " + (labelY ? sanitise(labelY) : 'y') + "" + d.value + "\n "; }; ChartInternal.prototype.countEpochsInRegion = function (region) { var $$ = this, target = $$.data.targets[0], total, count; total = target.values.reduce(function (accumulator, currentValue) { return accumulator + Number(currentValue.epochs); }, 0); count = target.values.reduce(function (accumulator, currentValue) { if (pointInRegion(currentValue, region)) { return accumulator + Number(currentValue.epochs); } return accumulator; }, 0); return { value: count, percentage: count !== 0 ? ((count / total) * 100).toFixed(1) : 0 }; }; var getRegionArea = function (points) { // thanks to: https://stackoverflow.com/questions/16282330/find-centerpoint-of-polygon-in-javascript var area = 0, i, j, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; area += point1.x * point2.y; area -= point1.y * point2.x; } area /= 2; return area; }; var pointInRegion = function (point, region) { // thanks to: http://bl.ocks.org/bycoffe/5575904 // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html var xi, yi, yj, xj, intersect, x = point.x, y = point.value, inside = false; for (var i = 0, j = region.length - 1; i < region.length; j = i++) { xi = region[i].x; yi = region[i].y; xj = region[j].x; yj = region[j].y; intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside; }; var compareEpochs = function (a, b) { if (a.epochs < b.epochs) { return -1; } if (a.epochs > b.epochs) { return 1; } return 0; }; ChartInternal.prototype.initStanfordElements = function () { var $$ = this; // Avoid blocking eventRect $$.stanfordElements = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.stanfordElements); $$.stanfordElements.append('g').attr('class', CLASS.stanfordLines); $$.stanfordElements.append('g').attr('class', CLASS.stanfordTexts); $$.stanfordElements.append('g').attr('class', CLASS.stanfordRegions); }; ChartInternal.prototype.updateStanfordElements = function (duration) { var $$ = this, main = $$.main, config = $$.config, stanfordLine, stanfordLineEnter, stanfordRegion, stanfordRegionEnter, stanfordText, stanfordTextEnter, xvCustom = $$.xvCustom.bind($$), yvCustom = $$.yvCustom.bind($$), countPointsInRegion = $$.countEpochsInRegion.bind($$); // Stanford-Lines stanfordLine = main .select('.' + CLASS.stanfordLines) .style('shape-rendering', 'geometricprecision') .selectAll('.' + CLASS.stanfordLine) .data(config.stanford_lines); // enter stanfordLineEnter = stanfordLine .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordLine + (d['class'] ? ' ' + d['class'] : ''); }); stanfordLineEnter .append('line') .attr('x1', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1'); }) .attr('x2', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2'); }) .attr('y1', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1'); }) .attr('y2', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2'); }) .style('opacity', 0); // update $$.stanfordLines = stanfordLineEnter.merge(stanfordLine); $$.stanfordLines .select('line') .transition() .duration(duration) .attr('x1', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1'); }) .attr('x2', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2'); }) .attr('y1', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1'); }) .attr('y2', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2'); }) .style('opacity', 1); // exit stanfordLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Text stanfordText = main .select('.' + CLASS.stanfordTexts) .selectAll('.' + CLASS.stanfordText) .data(config.stanford_texts); // enter stanfordTextEnter = stanfordText .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordText + (d['class'] ? ' ' + d['class'] : ''); }); stanfordTextEnter .append('text') .attr('x', function (d) { return (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x')); }) .attr('y', function (d) { return (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y')); }) .style('opacity', 0); // update $$.stanfordTexts = stanfordTextEnter.merge(stanfordText); $$.stanfordTexts .select('text') .transition() .duration(duration) .attr('x', function (d) { return (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x')); }) .attr('y', function (d) { return (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y')); }) .text(function (d) { return d.content; }) .style('opacity', 1); // exit stanfordText .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Regions stanfordRegion = main .select('.' + CLASS.stanfordRegions) .selectAll('.' + CLASS.stanfordRegion) .data(config.stanford_regions); // enter stanfordRegionEnter = stanfordRegion .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordRegion + (d['class'] ? ' ' + d['class'] : ''); }); stanfordRegionEnter .append('polygon') .attr('points', function (d) { return d.points .map(function (value) { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(','); }) .join(' '); }) .style('opacity', 0); stanfordRegionEnter .append('text') .attr('x', function (d) { return $$.getCentroid(d.points).x; }) .attr('y', function (d) { return $$.getCentroid(d.points).y; }) .style('opacity', 0); // update $$.stanfordRegions = stanfordRegionEnter.merge(stanfordRegion); $$.stanfordRegions .select('polygon') .transition() .duration(duration) .attr('points', function (d) { return d.points .map(function (value) { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(','); }) .join(' '); }) .style('opacity', function (d) { return d.opacity ? d.opacity : 0.2; }); $$.stanfordRegions .select('text') .transition() .duration(duration) .attr('x', function (d) { return config.axis_rotated ? yvCustom($$.getCentroid(d.points), 'y') : xvCustom($$.getCentroid(d.points), 'x'); }) .attr('y', function (d) { return config.axis_rotated ? xvCustom($$.getCentroid(d.points), 'x') : yvCustom($$.getCentroid(d.points), 'y'); }) .text(function (d) { if (d.text) { var value, percentage, temp; if ($$.isStanfordGraphType()) { temp = countPointsInRegion(d.points); value = temp.value; percentage = temp.percentage; } return d.text(value, percentage); } return ''; }) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('opacity', 1); // exit stanfordRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.initTooltip = function () { var $$ = this, config = $$.config, i; $$.tooltip = $$.selectChart .style('position', 'relative') .append('div') .attr('class', CLASS.tooltipContainer) .style('position', 'absolute') .style('pointer-events', 'none') .style('display', 'none'); // Show tooltip if needed if (config.tooltip_init_show) { if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); for (i = 0; i < $$.data.targets[0].values.length; i++) { if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { break; } } config.tooltip_init_x = i; } $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) { return $$.addName(d.values[config.tooltip_init_x]); }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color)); $$.tooltip .style('top', config.tooltip_init_position.top) .style('left', config.tooltip_init_position.left) .style('display', 'block'); } }; ChartInternal.prototype.getTooltipSortFunction = function () { var $$ = this, config = $$.config; if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { // if data are not grouped or if an order is specified // for the tooltip values we sort them by their values var order = config.tooltip_order; if (order === undefined) { order = config.data_order; } var valueOf = function (obj) { return obj ? obj.value : null; }; // if data are not grouped, we sort them by their value if (isString(order) && order.toLowerCase() === 'asc') { return function (a, b) { return valueOf(a) - valueOf(b); }; } else if (isString(order) && order.toLowerCase() === 'desc') { return function (a, b) { return valueOf(b) - valueOf(a); }; } else if (isFunction(order)) { // if the function is from data_order we need // to wrap the returned function in order to format // the sorted value to the expected format var sortFunction = order; if (config.tooltip_order === undefined) { sortFunction = function (a, b) { return order(a ? { id: a.id, values: [a] } : null, b ? { id: b.id, values: [b] } : null); }; } return sortFunction; } else if (isArray(order)) { return function (a, b) { return order.indexOf(a.id) - order.indexOf(b.id); }; } } else { // if data are grouped, we follow the order of grouped targets var ids = $$.orderTargets($$.data.targets).map(function (i) { return i.id; }); // if it was either asc or desc we need to invert the order // returned by orderTargets if ($$.isOrderAsc() || $$.isOrderDesc()) { ids = ids.reverse(); } return function (a, b) { return ids.indexOf(a.id) - ids.indexOf(b.id); }; } }; ChartInternal.prototype.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) { var $$ = this, config = $$.config, titleFormat = config.tooltip_format_title || defaultTitleFormat, nameFormat = config.tooltip_format_name || function (name) { return name; }, text, i, title, value, name, bgcolor; var valueFormat = config.tooltip_format_value; if (!valueFormat) { valueFormat = $$.isTargetNormalized(d.id) ? function (v, ratio) { return (ratio * 100).toFixed(2) + "%"; } : defaultValueFormat; } var tooltipSortFunction = this.getTooltipSortFunction(); if (tooltipSortFunction) { d.sort(tooltipSortFunction); } for (i = 0; i < d.length; i++) { if (!(d[i] && (d[i].value || d[i].value === 0))) { continue; } if ($$.isStanfordGraphType()) { // Custom tooltip for stanford plots if (!text) { title = $$.getStanfordTooltipTitle(d[i]); text = "" + title; } bgcolor = $$.getStanfordPointColor(d[i]); name = sanitise(config.data_epochs); // Epochs key name value = d[i].epochs; } else { // Regular tooltip if (!text) { title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x); text = "
" + (title || title === 0 ? "' : ''); } value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue; } name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); } } if (value !== undefined) { text += ""; text += "'; text += "'; text += ''; } } return text + '
" + title + '
" + name + '" + value + '
'; }; ChartInternal.prototype.tooltipPosition = function (dataToShow, tWidth, tHeight, element) { var $$ = this, config = $$.config, d3 = $$.d3; var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; var forArc = $$.hasArcType(), mouse = d3.mouse(element); // Determin tooltip position if (forArc) { tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]; tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20; } else { svgLeft = $$.getSvgLeft(true); if (config.axis_rotated) { tooltipLeft = svgLeft + mouse[0] + 100; tooltipRight = tooltipLeft + tWidth; chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = $$.x(dataToShow[0].x) + 20; } else { tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; tooltipRight = tooltipLeft + tWidth; chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = mouse[1] + 15; } if (tooltipRight > chartRight) { // 20 is needed for Firefox to keep tooltip width tooltipLeft -= tooltipRight - chartRight + 20; } if (tooltipTop + tHeight > $$.currentHeight) { tooltipTop -= tHeight + 30; } } if (tooltipTop < 0) { tooltipTop = 0; } return { top: tooltipTop, left: tooltipLeft }; }; ChartInternal.prototype.showTooltip = function (selectedData, element) { var $$ = this, config = $$.config; var tWidth, tHeight, position; var forArc = $$.hasArcType(), dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition; if (dataToShow.length === 0 || !config.tooltip_show) { $$.hideTooltip(); return; } $$.tooltip .html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)) .style('display', 'block'); // Get tooltip dimensions tWidth = $$.tooltip.property('offsetWidth'); tHeight = $$.tooltip.property('offsetHeight'); position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); // Set tooltip $$.tooltip .style('top', position.top + 'px') .style('left', position.left + 'px'); }; ChartInternal.prototype.hideTooltip = function () { this.tooltip.style('display', 'none'); }; ChartInternal.prototype.setTargetType = function (targetIds, type) { var $$ = this, config = $$.config; $$.mapToTargetIds(targetIds).forEach(function (id) { $$.withoutFadeIn[id] = type === config.data_types[id]; config.data_types[id] = type; }); if (!targetIds) { config.data_type = type; } }; ChartInternal.prototype.hasType = function (type, targets) { var $$ = this, types = $$.config.data_types, has = false; targets = targets || $$.data.targets; if (targets && targets.length) { targets.forEach(function (target) { var t = types[target.id]; if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { has = true; } }); } else if (Object.keys(types).length) { Object.keys(types).forEach(function (id) { if (types[id] === type) { has = true; } }); } else { has = $$.config.data_type === type; } return has; }; ChartInternal.prototype.hasArcType = function (targets) { return (this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets)); }; ChartInternal.prototype.isLineType = function (d) { var config = this.config, id = isString(d) ? d : d.id; return (!config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0); }; ChartInternal.prototype.isStepType = function (d) { var id = isString(d) ? d : d.id; return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0; }; ChartInternal.prototype.isSplineType = function (d) { var id = isString(d) ? d : d.id; return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0; }; ChartInternal.prototype.isAreaType = function (d) { var id = isString(d) ? d : d.id; return (['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0); }; ChartInternal.prototype.isBarType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'bar'; }; ChartInternal.prototype.isScatterType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'scatter'; }; ChartInternal.prototype.isStanfordType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'stanford'; }; ChartInternal.prototype.isPieType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'pie'; }; ChartInternal.prototype.isGaugeType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'gauge'; }; ChartInternal.prototype.isDonutType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'donut'; }; ChartInternal.prototype.isArcType = function (d) { return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d); }; ChartInternal.prototype.lineData = function (d) { return this.isLineType(d) ? [d] : []; }; ChartInternal.prototype.arcData = function (d) { return this.isArcType(d.data) ? [d] : []; }; /* not used function scatterData(d) { return isScatterType(d) ? d.values : []; } */ ChartInternal.prototype.barData = function (d) { return this.isBarType(d) ? d.values : []; }; ChartInternal.prototype.lineOrScatterOrStanfordData = function (d) { return this.isLineType(d) || this.isScatterType(d) || this.isStanfordType(d) ? d.values : []; }; ChartInternal.prototype.barOrLineData = function (d) { return this.isBarType(d) || this.isLineType(d) ? d.values : []; }; ChartInternal.prototype.isSafari = function () { var ua = window.navigator.userAgent; return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0; }; ChartInternal.prototype.isChrome = function () { var ua = window.navigator.userAgent; return ua.indexOf('Chrome') >= 0; }; ChartInternal.prototype.initZoom = function () { var $$ = this, d3 = $$.d3, config = $$.config, startEvent; $$.zoom = d3 .zoom() .on('start', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } startEvent = e; config.zoom_onzoomstart.call($$.api, e); }) .on('zoom', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } $$.redrawForZoom(); config.zoom_onzoom.call($$.api, $$.x.orgDomain()); }) .on('end', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } // if click, do nothing. otherwise, click interaction will be canceled. if (e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY) { return; } config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); }); $$.zoom.updateDomain = function () { if (d3.event && d3.event.transform) { if (config.axis_rotated && config.zoom_type === 'scroll' && d3.event.sourceEvent.type === 'mousemove') { // we're moving the mouse in a rotated chart with zoom = "scroll", so we need rescaleY (i.e. vertical) $$.x.domain(d3.event.transform.rescaleY($$.subX).domain()); } else { $$.x.domain(d3.event.transform.rescaleX($$.subX).domain()); } } return this; }; $$.zoom.updateExtent = function () { this.scaleExtent([1, Infinity]) .translateExtent([ [0, 0], [$$.width, $$.height] ]) .extent([ [0, 0], [$$.width, $$.height] ]); return this; }; $$.zoom.update = function () { return this.updateExtent().updateDomain(); }; return $$.zoom.updateExtent(); }; ChartInternal.prototype.zoomTransform = function (range) { var $$ = this, s = [$$.x(range[0]), $$.x(range[1])]; return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0); }; ChartInternal.prototype.initDragZoom = function () { var $$ = this; var d3 = $$.d3; var config = $$.config; var context = ($$.context = $$.svg); var brushXPos = $$.margin.left + 20.5; var brushYPos = $$.margin.top + 0.5; if (!(config.zoom_type === 'drag' && config.zoom_enabled)) { return; } var getZoomedDomain = function (selection) { return selection && selection.map(function (x) { return $$.x.invert(x); }); }; var brush = ($$.dragZoomBrush = d3 .brushX() .on('start', function () { $$.api.unzoom(); $$.svg.select('.' + CLASS.dragZoom).classed('disabled', false); config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); }) .on('brush', function () { config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection)); }) .on('end', function () { if (d3.event.selection == null) { return; } var zoomedDomain = getZoomedDomain(d3.event.selection); if (!config.zoom_disableDefaultBehavior) { $$.api.zoom(zoomedDomain); } $$.svg.select('.' + CLASS.dragZoom).classed('disabled', true); config.zoom_onzoomend.call($$.api, zoomedDomain); })); context .append('g') .classed(CLASS.dragZoom, true) .attr('clip-path', $$.clipPath) .attr('transform', 'translate(' + brushXPos + ',' + brushYPos + ')') .call(brush); }; ChartInternal.prototype.getZoomDomain = function () { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); return [min, max]; }; ChartInternal.prototype.redrawForZoom = function () { var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x; if (!config.zoom_enabled) { return; } if ($$.filterTargetsToShow($$.data.targets).length === 0) { return; } zoom.update(); if (config.zoom_disableDefaultBehavior) { return; } if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); } $$.redraw({ withTransition: false, withY: config.zoom_rescale, withSubchart: false, withEventRect: false, withDimension: false }); if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') { $$.cancelClick = true; } }; return c3; }))); ================================================ FILE: codecov.yml ================================================ coverage: status: no ================================================ FILE: component.json ================================================ { "name": "c3", "repo": "masayuki0812/c3", "description": "A D3-based reusable chart library", "version": "0.7.20", "keywords": [], "dependencies": { "mbostock/d3": "v5.0.0" }, "development": {}, "license": "MIT", "main": "c3.js", "scripts": ["c3.js"], "styles": ["c3.css"] } ================================================ FILE: config.rb ================================================ helpers do def js_as_plain(id) f = open("docs/js/samples/" + id + ".js") js = f.read f.close js end def data_as_plain(name) f = open("docs/data/" + name) data = f.read f.close data end def css_as_plain(name) f = open("docs/css/samples/" + name) css = f.read f.close css end def get_css_name(path) path.gsub('.html', '') end end set :source, 'docs' set :haml, { :ugly => true, :format => :html5 } set :css_dir, 'css' set :js_dir, 'js' set :images_dir, 'img' configure :build do activate :asset_hash, :ignore => %r{^js/ace/.*} end ================================================ FILE: data/samples.yml ================================================ simple_multiple: title: 'Line Chart' desc: 'Line chart with sequential data.' timeseries: title: Timeseries Chart desc: Simple line chart with timeseries data. chart_spline: title: Spline Chart desc: Display as Spline Chart. simple_xy: title: Simple XY Line Chart desc: Simple line chart with custom x. simple_xy_multiple: title: Multiple XY Line Chart desc: Multiple line chart with multiple custom x. simple_regions: title: Line Chart with Regions desc: Set regions for each data with style. chart_bar: title: Bar Chart desc: Display as Bar Chart. chart_bar_stacked: title: Stacked Bar Chart desc: Display as Stacked Bar Chart. chart_scatter: title: Scatter Plot desc: Display as Scatter Plot. chart_pie: title: Pie Chart desc: Display as Pie Chart. chart_donut: title: Donut Chart desc: Display as Donut Chart. chart_gauge: title: Gauge Chart desc: Display as Gauge Chart. chart_step: title: Step Chart desc: Display as Step Chart. chart_area: title: Area Chart desc: Display as Area Chart. chart_area_stacked: title: Stacked Area Chart desc: Display as Stacked Area Chart. chart_stanford: title: Stanford Chart desc: Display as Stanford Chart. chart_combination: title: Combination Chart desc: Display all kinda charts up in here. categorized: title: Category Axis desc: Show ticks as categorized by each data. axes_y2: title: Additional Y Axis desc: Additional y axis can be added. axes_rotated: title: Rotated Axis desc: Switch x and y axis position. axes_x_localtime: title: X Axis Timezone desc: Convert time to UTC. axes_x_tick_rotate: title: Rotate X Axis Tick Text desc: Rotate x axis tick text. axes_x_tick_format: title: X Axis Tick Format desc: Format x axis tick text. axes_y_tick_format: title: Y Axis Tick Format desc: Format y axis tick text. axes_x_tick_culling: title: X Axis Tick Culling desc: Set cull ticks or not on X Axis. axes_x_tick_values: title: X Axis Tick Values desc: Set tick texts on X Axis. axes_x_tick_count: title: X Axis Tick Count desc: Set the number of ticks on X Axis. axes_x_tick_fit: title: X Axis Tick Fitting desc: Set ticks position to x of data. axes_label: title: Axis Label desc: Set label for axis. axes_label_position: title: Axis Label Position desc: Set axis label position. axes_y_padding: title: Padding for Y Axis desc: Set padding for y axis. axes_y_range: title: Range for Y Axis desc: Set range for y axis. data_columned: title: Column Oriented Data desc: Column-oriented data can be used as input. data_rowed: title: Row Oriented Data desc: Row-oriented data can be used as input. data_json: title: JSON Data desc: JSON can be used as input. data_url: title: Data from URL desc: Data from URL can be used as input. data_load: title: Load Data desc: Load data dynamically. data_name: title: Data Name desc: Set name for each data. data_stringx: title: Category Data desc: Load data with x values on category axis. data_order: title: Data Order desc: Define data order. This will be used for stacked bar chart. data_label: title: Data Label desc: Show label of data. data_label_format: title: Data Label Format desc: Format label of data. data_number_format_l10n: title: Number Format Localization desc: Number format localization using D3 locale settings. data_color: title: Data Color desc: Set color according to data. options_legend: title: Hide Legend desc: Set visibility of legend. legend_position: title: Legend Position desc: Show legend on bottom or right side. legend_custom: title: Custom Legend desc: Build custom legend tooltip_show: title: Hide Tooltip desc: Set visibility of tooltip. tooltip_grouped: title: Tooltip Grouping desc: Show tooltips as grouped or not. tooltip_format: title: Tooltip Format desc: Set format for title and value on tooltip. tooltip_horizontal: title: Horizontal Tooltip desc: Show tooltips based on the horizontal position of the mouse. options_gridline: title: Grid Lines desc: Show grid lines for x and y axis. grid_x_lines: title: Optional X Grid Lines desc: Add optional grid lines on x grid. grid_y_lines: title: Optional Y Grid Lines desc: Add optional grid lines on y grid. options_subchart: title: Sub Chart desc: Show sub chart for zoom and selection range. interaction_zoom: title: Zoom desc: Zoom by mouse wheel event and slide by drag. interaction_zoom_by_drag: title: Zoom by Drag desc: Zoom by dragging area. region: title: Region desc: Show rects on chart. region_timeseries: title: Region with Timeseries desc: Show rects on timeseries chart. options_size: title: Chart Size desc: Set chart size in px. options_color: title: Color Pattern desc: Set custom color pattern. options_padding: title: Padding desc: Change padding for the chart. point_show: title: Hide points desc: Hide points on line chart pie_label_format: title: Pie Label Format desc: Change label format on Pie chart transition_duration: title: Duration of Transition desc: Set duration of transition for chart animation. api_flow: title: Flow desc: Load/Unload data as flowing api_resize: title: Resize desc: Resize chart. api_data_name: title: Data Name desc: Update data names. api_data_color: title: Data Color desc: Update data colors. api_axis_label: title: Axis Label desc: Update axis labels. api_axis_range: title: Axis Range desc: Update axis range. api_grid_x: title: X Grid desc: Update custom x grids. transform_line: title: To Line Chart desc: Transform to line chart. transform_spline: title: To Spline Chart desc: Transform to spline chart. transform_bar: title: To Bar Chart desc: Transform to bar chart. transform_area: title: To Area Chart desc: Transform to area chart. transform_areaspline: title: To Area Spline Chart desc: Transform to area spline chart. transform_scatter: title: To Scatter Plot desc: Transform to scatter plot. transform_pie: title: To Pie Chart desc: Transform to pie chart. transform_donut: title: To Donut Chart desc: Transform to donut chart. style_region: title: Style for Region desc: Set style for regions. style_grid: title: Style for Grid desc: Set style for grids. ================================================ FILE: docs/404.html ================================================ Page Not Found :(

Not found :(

Sorry, but the page you were trying to view does not exist.

It looks like this was the result of either:

================================================ FILE: docs/CNAME ================================================ c3js.org ================================================ FILE: docs/_footer.haml ================================================ %footer %hr %p © Masayuki Tanaka 2014 ================================================ FILE: docs/_index_item.haml ================================================ .large-4.columns %h3 #{data.samples[id].title} %p.margin-small-bottom #{data.samples[id].desc} %a.btn.btn-default( href="/samples/#{id}.html" role="button" ) View details » ================================================ FILE: docs/_index_item_title.haml ================================================ %a( href="/examples.html##{id}" name="#{id}" ) %h2< \# %span #{name} ================================================ FILE: docs/_reference_item_link.haml ================================================ %a( href="##{ id.gsub(/\./, '-') }")< #{ id.gsub(/(api|class)\./, '') } - if defined? experimental Experimental ================================================ FILE: docs/_reference_menu_item.haml ================================================ %li - if defined? experimental = partial :reference_item_link, locals: { id: id, experimental: experimental } - else = partial :reference_item_link, locals: { id: id } ================================================ FILE: docs/_sample.haml ================================================ .container %h1.title #{data.samples[id].title} .chart #chart #ace-error .sourcecode.margin-medium-v.margin-small-h %h3 # #{id}.js #javascript-editor.c3-editor = js_as_plain id - if defined? other_css .sourcecode %h3 # #{other_css} %pre %code.css = css_as_plain other_css - if defined? other_files - other_files.each do |f| .sourcecode %h3 # #{f} %pre %code.html = data_as_plain f = partial :footer = partial :script = partial :sample_editor, locals: { type: 'javascript' } = javascript_include_tag "samples/#{id}.js" ================================================ FILE: docs/_sample_editor.haml ================================================ = javascript_include_tag "ace/ace.js" = javascript_include_tag "ace/mode-javascript.js" = javascript_include_tag "ace/theme-tomorrow.js" :javascript var editor = ace.edit('#{type}-editor'), error = document.getElementById('ace-error'); function debounce(func, wait) { var timeout; return function() { var context = this, args = arguments; var later = function() { func.apply(context, args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; editor.on('change', debounce(function(e) { try { eval(editor.getValue()); error.innerHTML = ''; } catch(e) { error.innerHTML = e; } }, 300)); editor.setOption("maxLines", 100); editor.setOption("showLineNumbers", false); editor.setOption("showGutter", false); editor.setTheme("ace/theme/tomorrow"); editor.getSession().setMode("ace/mode/#{type}"); editor.commands.removeCommand('gotoline') // Disables the override of Command-L ================================================ FILE: docs/_samples_header.haml ================================================ .nav %a( href="/") Home .page-header.title %h1 #{data.samples[id].title} .chart #chart .sourcecode %h3 # #{id}.js %pre %code.html.javascript = js_as_plain id = partial :script = javascript_include_tag "samples/#{id}.js" ================================================ FILE: docs/_script.haml ================================================ = javascript_include_tag 'jquery-1.11.0.min.js' = javascript_include_tag 'foundation.min.js' = javascript_include_tag 'highlight.pack.js' = javascript_include_tag 'd3-5.8.2.min.js' = javascript_include_tag 'c3.min.js' :javascript hljs.initHighlightingOnLoad(); $(document).foundation(); ================================================ FILE: docs/_script_scroll.haml ================================================ :javascript $(function(){ function scrollToHash() { var hash = document.location.hash, target = $('.column-content h3 a[href^=' + hash + ']'), position; if (target.length) { position = target.offset().top - 60; $("html, body").animate({scrollTop:position}, 250, "swing"); } } $(window).on('hashchange', scrollToHash); // When clicked $('a[href^=#]').click(function(){ document.location.hash = $(this).attr("href"); return false; }); // When loaded $(window).trigger('hashchange'); }); ================================================ FILE: docs/_sidemenu_item.haml ================================================ %li %a( href="##{ id }") #{ name } ================================================ FILE: docs/crossdomain.xml ================================================ ================================================ FILE: docs/css/c3.css ================================================ /*-- Chart --*/ .c3 svg { font: 10px sans-serif; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } .c3 path, .c3 line { fill: none; stroke: #000; } .c3 text { -webkit-user-select: none; -moz-user-select: none; user-select: none; } .c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path { shape-rendering: crispEdges; } .c3-chart-arc path { stroke: #fff; } .c3-chart-arc rect { stroke: white; stroke-width: 1; } .c3-chart-arc text { fill: #fff; font-size: 13px; } /*-- Axis --*/ /*-- Grid --*/ .c3-grid line { stroke: #aaa; } .c3-grid text { fill: #aaa; } .c3-xgrid, .c3-ygrid { stroke-dasharray: 3 3; } /*-- Text on Chart --*/ .c3-text.c3-empty { fill: #808080; font-size: 2em; } /*-- Line --*/ .c3-line { stroke-width: 1px; } /*-- Point --*/ .c3-circle { fill: currentColor; } .c3-circle._expanded_ { stroke-width: 1px; stroke: white; } .c3-selected-circle { fill: white; stroke-width: 2px; } /*-- Bar --*/ .c3-bar { stroke-width: 0; } .c3-bar._expanded_ { fill-opacity: 1; fill-opacity: 0.75; } /*-- Focus --*/ .c3-target.c3-focused { opacity: 1; } .c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step { stroke-width: 2px; } .c3-target.c3-defocused { opacity: 0.3 !important; } /*-- Region --*/ .c3-region { fill: steelblue; fill-opacity: 0.1; } .c3-region text { fill-opacity: 1; } /*-- Brush --*/ .c3-brush .extent { fill-opacity: 0.1; } /*-- Select - Drag --*/ /*-- Legend --*/ .c3-legend-item { font-size: 12px; } .c3-legend-item-hidden { opacity: 0.15; } .c3-legend-background { opacity: 0.75; fill: white; stroke: lightgray; stroke-width: 1; } /*-- Title --*/ .c3-title { font: 14px sans-serif; } /*-- Tooltip --*/ .c3-tooltip-container { z-index: 10; } .c3-tooltip { border-collapse: collapse; border-spacing: 0; background-color: #fff; empty-cells: show; -webkit-box-shadow: 7px 7px 12px -9px #777777; -moz-box-shadow: 7px 7px 12px -9px #777777; box-shadow: 7px 7px 12px -9px #777777; opacity: 0.9; } .c3-tooltip tr { border: 1px solid #CCC; } .c3-tooltip th { background-color: #aaa; font-size: 14px; padding: 2px 5px; text-align: left; color: #FFF; } .c3-tooltip td { font-size: 13px; padding: 3px 6px; background-color: #fff; border-left: 1px dotted #999; } .c3-tooltip td > span { display: inline-block; width: 10px; height: 10px; margin-right: 6px; } .c3-tooltip .value { text-align: right; } /*-- Area --*/ .c3-area { stroke-width: 0; opacity: 0.2; } /*-- Arc --*/ .c3-chart-arcs-title { dominant-baseline: middle; font-size: 1.3em; } .c3-chart-arcs .c3-chart-arcs-background { fill: #e0e0e0; stroke: #FFF; } .c3-chart-arcs .c3-chart-arcs-gauge-unit { fill: #000; font-size: 16px; } .c3-chart-arcs .c3-chart-arcs-gauge-max { fill: #777; } .c3-chart-arcs .c3-chart-arcs-gauge-min { fill: #777; } .c3-chart-arc .c3-gauge-value { fill: #000; /* font-size: 28px !important;*/ } .c3-chart-arc.c3-target g path { opacity: 1; } .c3-chart-arc.c3-target.c3-focused g path { opacity: 1; } /*-- Zoom --*/ .c3-drag-zoom.enabled { pointer-events: all !important; visibility: visible; } .c3-drag-zoom.disabled { pointer-events: none !important; visibility: hidden; } .c3-drag-zoom .extent { fill-opacity: 0.1; } ================================================ FILE: docs/css/examples.css ================================================ ================================================ FILE: docs/css/foundation.css ================================================ meta.foundation-version { font-family: "/5.2.2/"; } meta.foundation-mq-small { font-family: "/only screen/"; width: 0em; } meta.foundation-mq-medium { font-family: "/only screen and (min-width:40.063em)/"; width: 40.063em; } meta.foundation-mq-large { font-family: "/only screen and (min-width:64.063em)/"; width: 64.063em; } meta.foundation-mq-xlarge { font-family: "/only screen and (min-width:90.063em)/"; width: 90.063em; } meta.foundation-mq-xxlarge { font-family: "/only screen and (min-width:120.063em)/"; width: 120.063em; } meta.foundation-data-attribute-namespace { font-family: false; } html, body { height: 100%; } *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html, body { font-size: 100%; } body { background: white; color: #222222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: default; } a:hover { cursor: pointer; } img { max-width: 100%; height: auto; } img { -ms-interpolation-mode: bicubic; } #map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } .left { float: left !important; } .right { float: right !important; } .clearfix { *zoom: 1; } .clearfix:before, .clearfix:after { content: " "; display: table; } .clearfix:after { clear: both; } .hide { display: none; } .antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } img { display: inline-block; vertical-align: middle; } textarea { height: auto; min-height: 50px; } select { width: 100%; } .row { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5rem; *zoom: 1; } .row:before, .row:after { content: " "; display: table; } .row:after { clear: both; } .row.collapse > .column, .row.collapse > .columns { padding-left: 0; padding-right: 0; } .row.collapse .row { margin-left: 0; margin-right: 0; } .row .row { width: auto; margin-left: -0.9375rem; margin-right: -0.9375rem; margin-top: 0; margin-bottom: 0; max-width: none; *zoom: 1; } .row .row:before, .row .row:after { content: " "; display: table; } .row .row:after { clear: both; } .row .row.collapse { width: auto; margin: 0; max-width: none; *zoom: 1; } .row .row.collapse:before, .row .row.collapse:after { content: " "; display: table; } .row .row.collapse:after { clear: both; } .column, .columns { padding-left: 0.9375rem; padding-right: 0.9375rem; width: 100%; float: left; } @media only screen { .small-push-0 { position: relative; left: 0%; right: auto; } .small-pull-0 { position: relative; right: 0%; left: auto; } .small-push-1 { position: relative; left: 8.33333%; right: auto; } .small-pull-1 { position: relative; right: 8.33333%; left: auto; } .small-push-2 { position: relative; left: 16.66667%; right: auto; } .small-pull-2 { position: relative; right: 16.66667%; left: auto; } .small-push-3 { position: relative; left: 25%; right: auto; } .small-pull-3 { position: relative; right: 25%; left: auto; } .small-push-4 { position: relative; left: 33.33333%; right: auto; } .small-pull-4 { position: relative; right: 33.33333%; left: auto; } .small-push-5 { position: relative; left: 41.66667%; right: auto; } .small-pull-5 { position: relative; right: 41.66667%; left: auto; } .small-push-6 { position: relative; left: 50%; right: auto; } .small-pull-6 { position: relative; right: 50%; left: auto; } .small-push-7 { position: relative; left: 58.33333%; right: auto; } .small-pull-7 { position: relative; right: 58.33333%; left: auto; } .small-push-8 { position: relative; left: 66.66667%; right: auto; } .small-pull-8 { position: relative; right: 66.66667%; left: auto; } .small-push-9 { position: relative; left: 75%; right: auto; } .small-pull-9 { position: relative; right: 75%; left: auto; } .small-push-10 { position: relative; left: 83.33333%; right: auto; } .small-pull-10 { position: relative; right: 83.33333%; left: auto; } .small-push-11 { position: relative; left: 91.66667%; right: auto; } .small-pull-11 { position: relative; right: 91.66667%; left: auto; } .column, .columns { position: relative; padding-left: 0.9375rem; padding-right: 0.9375rem; float: left; } .small-1 { width: 8.33333%; } .small-2 { width: 16.66667%; } .small-3 { width: 25%; } .small-4 { width: 33.33333%; } .small-5 { width: 41.66667%; } .small-6 { width: 50%; } .small-7 { width: 58.33333%; } .small-8 { width: 66.66667%; } .small-9 { width: 75%; } .small-10 { width: 83.33333%; } .small-11 { width: 91.66667%; } .small-12 { width: 100%; } [class*="column"] + [class*="column"]:last-child { float: right; } [class*="column"] + [class*="column"].end { float: left; } .small-offset-0 { margin-left: 0% !important; } .small-offset-1 { margin-left: 8.33333% !important; } .small-offset-2 { margin-left: 16.66667% !important; } .small-offset-3 { margin-left: 25% !important; } .small-offset-4 { margin-left: 33.33333% !important; } .small-offset-5 { margin-left: 41.66667% !important; } .small-offset-6 { margin-left: 50% !important; } .small-offset-7 { margin-left: 58.33333% !important; } .small-offset-8 { margin-left: 66.66667% !important; } .small-offset-9 { margin-left: 75% !important; } .small-offset-10 { margin-left: 83.33333% !important; } .small-offset-11 { margin-left: 91.66667% !important; } .small-reset-order, .small-reset-order { margin-left: 0; margin-right: 0; left: auto; right: auto; float: left; } .column.small-centered, .columns.small-centered { margin-left: auto; margin-right: auto; float: none; } .column.small-uncentered, .columns.small-uncentered { margin-left: 0; margin-right: 0; float: left !important; } .column.small-uncentered.opposite, .columns.small-uncentered.opposite { float: right; } } @media only screen and (min-width: 40.063em) { .medium-push-0 { position: relative; left: 0%; right: auto; } .medium-pull-0 { position: relative; right: 0%; left: auto; } .medium-push-1 { position: relative; left: 8.33333%; right: auto; } .medium-pull-1 { position: relative; right: 8.33333%; left: auto; } .medium-push-2 { position: relative; left: 16.66667%; right: auto; } .medium-pull-2 { position: relative; right: 16.66667%; left: auto; } .medium-push-3 { position: relative; left: 25%; right: auto; } .medium-pull-3 { position: relative; right: 25%; left: auto; } .medium-push-4 { position: relative; left: 33.33333%; right: auto; } .medium-pull-4 { position: relative; right: 33.33333%; left: auto; } .medium-push-5 { position: relative; left: 41.66667%; right: auto; } .medium-pull-5 { position: relative; right: 41.66667%; left: auto; } .medium-push-6 { position: relative; left: 50%; right: auto; } .medium-pull-6 { position: relative; right: 50%; left: auto; } .medium-push-7 { position: relative; left: 58.33333%; right: auto; } .medium-pull-7 { position: relative; right: 58.33333%; left: auto; } .medium-push-8 { position: relative; left: 66.66667%; right: auto; } .medium-pull-8 { position: relative; right: 66.66667%; left: auto; } .medium-push-9 { position: relative; left: 75%; right: auto; } .medium-pull-9 { position: relative; right: 75%; left: auto; } .medium-push-10 { position: relative; left: 83.33333%; right: auto; } .medium-pull-10 { position: relative; right: 83.33333%; left: auto; } .medium-push-11 { position: relative; left: 91.66667%; right: auto; } .medium-pull-11 { position: relative; right: 91.66667%; left: auto; } .column, .columns { position: relative; padding-left: 0.9375rem; padding-right: 0.9375rem; float: left; } .medium-1 { width: 8.33333%; } .medium-2 { width: 16.66667%; } .medium-3 { width: 25%; } .medium-4 { width: 33.33333%; } .medium-5 { width: 41.66667%; } .medium-6 { width: 50%; } .medium-7 { width: 58.33333%; } .medium-8 { width: 66.66667%; } .medium-9 { width: 75%; } .medium-10 { width: 83.33333%; } .medium-11 { width: 91.66667%; } .medium-12 { width: 100%; } [class*="column"] + [class*="column"]:last-child { float: right; } [class*="column"] + [class*="column"].end { float: left; } .medium-offset-0 { margin-left: 0% !important; } .medium-offset-1 { margin-left: 8.33333% !important; } .medium-offset-2 { margin-left: 16.66667% !important; } .medium-offset-3 { margin-left: 25% !important; } .medium-offset-4 { margin-left: 33.33333% !important; } .medium-offset-5 { margin-left: 41.66667% !important; } .medium-offset-6 { margin-left: 50% !important; } .medium-offset-7 { margin-left: 58.33333% !important; } .medium-offset-8 { margin-left: 66.66667% !important; } .medium-offset-9 { margin-left: 75% !important; } .medium-offset-10 { margin-left: 83.33333% !important; } .medium-offset-11 { margin-left: 91.66667% !important; } .medium-reset-order, .medium-reset-order { margin-left: 0; margin-right: 0; left: auto; right: auto; float: left; } .column.medium-centered, .columns.medium-centered { margin-left: auto; margin-right: auto; float: none; } .column.medium-uncentered, .columns.medium-uncentered { margin-left: 0; margin-right: 0; float: left !important; } .column.medium-uncentered.opposite, .columns.medium-uncentered.opposite { float: right; } .push-0 { position: relative; left: 0%; right: auto; } .pull-0 { position: relative; right: 0%; left: auto; } .push-1 { position: relative; left: 8.33333%; right: auto; } .pull-1 { position: relative; right: 8.33333%; left: auto; } .push-2 { position: relative; left: 16.66667%; right: auto; } .pull-2 { position: relative; right: 16.66667%; left: auto; } .push-3 { position: relative; left: 25%; right: auto; } .pull-3 { position: relative; right: 25%; left: auto; } .push-4 { position: relative; left: 33.33333%; right: auto; } .pull-4 { position: relative; right: 33.33333%; left: auto; } .push-5 { position: relative; left: 41.66667%; right: auto; } .pull-5 { position: relative; right: 41.66667%; left: auto; } .push-6 { position: relative; left: 50%; right: auto; } .pull-6 { position: relative; right: 50%; left: auto; } .push-7 { position: relative; left: 58.33333%; right: auto; } .pull-7 { position: relative; right: 58.33333%; left: auto; } .push-8 { position: relative; left: 66.66667%; right: auto; } .pull-8 { position: relative; right: 66.66667%; left: auto; } .push-9 { position: relative; left: 75%; right: auto; } .pull-9 { position: relative; right: 75%; left: auto; } .push-10 { position: relative; left: 83.33333%; right: auto; } .pull-10 { position: relative; right: 83.33333%; left: auto; } .push-11 { position: relative; left: 91.66667%; right: auto; } .pull-11 { position: relative; right: 91.66667%; left: auto; } } @media only screen and (min-width: 64.063em) { .large-push-0 { position: relative; left: 0%; right: auto; } .large-pull-0 { position: relative; right: 0%; left: auto; } .large-push-1 { position: relative; left: 8.33333%; right: auto; } .large-pull-1 { position: relative; right: 8.33333%; left: auto; } .large-push-2 { position: relative; left: 16.66667%; right: auto; } .large-pull-2 { position: relative; right: 16.66667%; left: auto; } .large-push-3 { position: relative; left: 25%; right: auto; } .large-pull-3 { position: relative; right: 25%; left: auto; } .large-push-4 { position: relative; left: 33.33333%; right: auto; } .large-pull-4 { position: relative; right: 33.33333%; left: auto; } .large-push-5 { position: relative; left: 41.66667%; right: auto; } .large-pull-5 { position: relative; right: 41.66667%; left: auto; } .large-push-6 { position: relative; left: 50%; right: auto; } .large-pull-6 { position: relative; right: 50%; left: auto; } .large-push-7 { position: relative; left: 58.33333%; right: auto; } .large-pull-7 { position: relative; right: 58.33333%; left: auto; } .large-push-8 { position: relative; left: 66.66667%; right: auto; } .large-pull-8 { position: relative; right: 66.66667%; left: auto; } .large-push-9 { position: relative; left: 75%; right: auto; } .large-pull-9 { position: relative; right: 75%; left: auto; } .large-push-10 { position: relative; left: 83.33333%; right: auto; } .large-pull-10 { position: relative; right: 83.33333%; left: auto; } .large-push-11 { position: relative; left: 91.66667%; right: auto; } .large-pull-11 { position: relative; right: 91.66667%; left: auto; } .column, .columns { position: relative; padding-left: 0.9375rem; padding-right: 0.9375rem; float: left; } .large-1 { width: 8.33333%; } .large-2 { width: 16.66667%; } .large-3 { width: 25%; } .large-4 { width: 33.33333%; } .large-5 { width: 41.66667%; } .large-6 { width: 50%; } .large-7 { width: 58.33333%; } .large-8 { width: 66.66667%; } .large-9 { width: 75%; } .large-10 { width: 83.33333%; } .large-11 { width: 91.66667%; } .large-12 { width: 100%; } [class*="column"] + [class*="column"]:last-child { float: right; } [class*="column"] + [class*="column"].end { float: left; } .large-offset-0 { margin-left: 0% !important; } .large-offset-1 { margin-left: 8.33333% !important; } .large-offset-2 { margin-left: 16.66667% !important; } .large-offset-3 { margin-left: 25% !important; } .large-offset-4 { margin-left: 33.33333% !important; } .large-offset-5 { margin-left: 41.66667% !important; } .large-offset-6 { margin-left: 50% !important; } .large-offset-7 { margin-left: 58.33333% !important; } .large-offset-8 { margin-left: 66.66667% !important; } .large-offset-9 { margin-left: 75% !important; } .large-offset-10 { margin-left: 83.33333% !important; } .large-offset-11 { margin-left: 91.66667% !important; } .large-reset-order, .large-reset-order { margin-left: 0; margin-right: 0; left: auto; right: auto; float: left; } .column.large-centered, .columns.large-centered { margin-left: auto; margin-right: auto; float: none; } .column.large-uncentered, .columns.large-uncentered { margin-left: 0; margin-right: 0; float: left !important; } .column.large-uncentered.opposite, .columns.large-uncentered.opposite { float: right; } .push-0 { position: relative; left: 0%; right: auto; } .pull-0 { position: relative; right: 0%; left: auto; } .push-1 { position: relative; left: 8.33333%; right: auto; } .pull-1 { position: relative; right: 8.33333%; left: auto; } .push-2 { position: relative; left: 16.66667%; right: auto; } .pull-2 { position: relative; right: 16.66667%; left: auto; } .push-3 { position: relative; left: 25%; right: auto; } .pull-3 { position: relative; right: 25%; left: auto; } .push-4 { position: relative; left: 33.33333%; right: auto; } .pull-4 { position: relative; right: 33.33333%; left: auto; } .push-5 { position: relative; left: 41.66667%; right: auto; } .pull-5 { position: relative; right: 41.66667%; left: auto; } .push-6 { position: relative; left: 50%; right: auto; } .pull-6 { position: relative; right: 50%; left: auto; } .push-7 { position: relative; left: 58.33333%; right: auto; } .pull-7 { position: relative; right: 58.33333%; left: auto; } .push-8 { position: relative; left: 66.66667%; right: auto; } .pull-8 { position: relative; right: 66.66667%; left: auto; } .push-9 { position: relative; left: 75%; right: auto; } .pull-9 { position: relative; right: 75%; left: auto; } .push-10 { position: relative; left: 83.33333%; right: auto; } .pull-10 { position: relative; right: 83.33333%; left: auto; } .push-11 { position: relative; left: 91.66667%; right: auto; } .pull-11 { position: relative; right: 91.66667%; left: auto; } } button, .button { border-style: solid; border-width: 0px; cursor: pointer; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; line-height: normal; margin: 0 0 1.25rem; position: relative; text-decoration: none; text-align: center; -webkit-appearance: none; -webkit-border-radius: 0; display: inline-block; padding-top: 1rem; padding-right: 2rem; padding-bottom: 1.0625rem; padding-left: 2rem; font-size: 1rem; background-color: #008cba; border-color: #007095; color: white; transition: background-color 300ms ease-out; } button:hover, button:focus, .button:hover, .button:focus { background-color: #007095; } button:hover, button:focus, .button:hover, .button:focus { color: white; } button.secondary, .button.secondary { background-color: #e7e7e7; border-color: #b9b9b9; color: #333333; } button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { background-color: #b9b9b9; } button.secondary:hover, button.secondary:focus, .button.secondary:hover, .button.secondary:focus { color: #333333; } button.success, .button.success { background-color: #43ac6a; border-color: #368a55; color: white; } button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { background-color: #368a55; } button.success:hover, button.success:focus, .button.success:hover, .button.success:focus { color: white; } button.alert, .button.alert { background-color: #f04124; border-color: #cf2a0e; color: white; } button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { background-color: #cf2a0e; } button.alert:hover, button.alert:focus, .button.alert:hover, .button.alert:focus { color: white; } button.large, .button.large { padding-top: 1.125rem; padding-right: 2.25rem; padding-bottom: 1.1875rem; padding-left: 2.25rem; font-size: 1.25rem; } button.small, .button.small { padding-top: 0.875rem; padding-right: 1.75rem; padding-bottom: 0.9375rem; padding-left: 1.75rem; font-size: 0.8125rem; } button.tiny, .button.tiny { padding-top: 0.625rem; padding-right: 1.25rem; padding-bottom: 0.6875rem; padding-left: 1.25rem; font-size: 0.6875rem; } button.expand, .button.expand { padding-right: 0; padding-left: 0; width: 100%; } button.left-align, .button.left-align { text-align: left; text-indent: 0.75rem; } button.right-align, .button.right-align { text-align: right; padding-right: 0.75rem; } button.radius, .button.radius { border-radius: 3px; } button.round, .button.round { border-radius: 1000px; } button.disabled, button[disabled], .button.disabled, .button[disabled] { background-color: #008cba; border-color: #007095; color: white; cursor: default; opacity: 0.7; box-shadow: none; } button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { background-color: #007095; } button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { color: white; } button.disabled:hover, button.disabled:focus, button[disabled]:hover, button[disabled]:focus, .button.disabled:hover, .button.disabled:focus, .button[disabled]:hover, .button[disabled]:focus { background-color: #008cba; } button.disabled.secondary, button[disabled].secondary, .button.disabled.secondary, .button[disabled].secondary { background-color: #e7e7e7; border-color: #b9b9b9; color: #333333; cursor: default; opacity: 0.7; box-shadow: none; } button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { background-color: #b9b9b9; } button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { color: #333333; } button.disabled.secondary:hover, button.disabled.secondary:focus, button[disabled].secondary:hover, button[disabled].secondary:focus, .button.disabled.secondary:hover, .button.disabled.secondary:focus, .button[disabled].secondary:hover, .button[disabled].secondary:focus { background-color: #e7e7e7; } button.disabled.success, button[disabled].success, .button.disabled.success, .button[disabled].success { background-color: #43ac6a; border-color: #368a55; color: white; cursor: default; opacity: 0.7; box-shadow: none; } button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { background-color: #368a55; } button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { color: white; } button.disabled.success:hover, button.disabled.success:focus, button[disabled].success:hover, button[disabled].success:focus, .button.disabled.success:hover, .button.disabled.success:focus, .button[disabled].success:hover, .button[disabled].success:focus { background-color: #43ac6a; } button.disabled.alert, button[disabled].alert, .button.disabled.alert, .button[disabled].alert { background-color: #f04124; border-color: #cf2a0e; color: white; cursor: default; opacity: 0.7; box-shadow: none; } button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { background-color: #cf2a0e; } button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { color: white; } button.disabled.alert:hover, button.disabled.alert:focus, button[disabled].alert:hover, button[disabled].alert:focus, .button.disabled.alert:hover, .button.disabled.alert:focus, .button[disabled].alert:hover, .button[disabled].alert:focus { background-color: #f04124; } @media only screen and (min-width: 40.063em) { button, .button { display: inline-block; } } meta.foundation-mq-topbar { font-family: "/only screen and (min-width:40.063em)/"; width: 40.063em; } /* Wrapped around .top-bar to contain to grid width */ .contain-to-grid { width: 100%; background: #333333; } .contain-to-grid .top-bar { margin-bottom: 0; } .fixed { width: 100%; left: 0; position: fixed; top: 0; z-index: 99; } .fixed.expanded:not(.top-bar) { overflow-y: auto; height: auto; width: 100%; max-height: 100%; } .fixed.expanded:not(.top-bar) .title-area { position: fixed; width: 100%; z-index: 99; } .fixed.expanded:not(.top-bar) .top-bar-section { z-index: 98; margin-top: 45px; } .top-bar { overflow: hidden; height: 45px; line-height: 45px; position: relative; background: #333333; margin-bottom: 0; } .top-bar ul { margin-bottom: 0; list-style: none; } .top-bar .row { max-width: none; } .top-bar form, .top-bar input { margin-bottom: 0; } .top-bar input { height: auto; padding-top: .35rem; padding-bottom: .35rem; font-size: 0.75rem; } .top-bar .button, .top-bar button { padding-top: .45rem; padding-bottom: .35rem; margin-bottom: 0; font-size: 0.75rem; } .top-bar .title-area { position: relative; margin: 0; } .top-bar .name { height: 45px; margin: 0; font-size: 16px; } .top-bar .name h1 { line-height: 45px; /* font-size: 1.0625rem;*/ font-size: 0.94444rem; margin: 0; } .top-bar .name h1 a { font-weight: normal; color: white; width: 75%; display: block; padding: 0 15px; } .top-bar .toggle-topbar { position: absolute; right: 0; top: 0; } .top-bar .toggle-topbar a { color: white; text-transform: uppercase; font-size: 0.8125rem; font-weight: bold; position: relative; display: block; padding: 0 15px; height: 45px; line-height: 45px; } .top-bar .toggle-topbar.menu-icon { right: 15px; top: 50%; margin-top: -16px; padding-left: 40px; } .top-bar .toggle-topbar.menu-icon a { height: 34px; line-height: 33px; padding: 0; padding-right: 25px; color: white; position: relative; } .top-bar .toggle-topbar.menu-icon a::after { content: ""; position: absolute; right: 0; display: block; width: 16px; top: 0; height: 0; box-shadow: 0 10px 0 1px white, 0 16px 0 1px white, 0 22px 0 1px white; } .top-bar.expanded { height: auto; background: transparent; } .top-bar.expanded .title-area { background: #333333; } .top-bar.expanded .toggle-topbar a { color: #888888; } .top-bar.expanded .toggle-topbar a::after { box-shadow: 0 10px 0 1px #888888, 0 16px 0 1px #888888, 0 22px 0 1px #888888; } .top-bar-section { left: 0; position: relative; width: auto; transition: left 300ms ease-out; } .top-bar-section ul { width: 100%; height: auto; display: block; background: #333333; font-size: 16px; margin: 0; } .top-bar-section .divider, .top-bar-section [role="separator"] { border-top: solid 1px #1a1a1a; clear: both; height: 1px; width: 100%; } .top-bar-section ul li > a { display: block; width: 100%; color: white; padding: 12px 0 12px 0; padding-left: 15px; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 0.8125rem; font-weight: normal; text-transform: none; background: #333333; } .top-bar-section ul li > a.button { font-size: 0.8125rem; padding-right: 15px; padding-left: 15px; background-color: #008cba; border-color: #007095; color: white; } .top-bar-section ul li > a.button:hover, .top-bar-section ul li > a.button:focus { background-color: #007095; } .top-bar-section ul li > a.button:hover, .top-bar-section ul li > a.button:focus { color: white; } .top-bar-section ul li > a.button.secondary { background-color: #e7e7e7; border-color: #b9b9b9; color: #333333; } .top-bar-section ul li > a.button.secondary:hover, .top-bar-section ul li > a.button.secondary:focus { background-color: #b9b9b9; } .top-bar-section ul li > a.button.secondary:hover, .top-bar-section ul li > a.button.secondary:focus { color: #333333; } .top-bar-section ul li > a.button.success { background-color: #43ac6a; border-color: #368a55; color: white; } .top-bar-section ul li > a.button.success:hover, .top-bar-section ul li > a.button.success:focus { background-color: #368a55; } .top-bar-section ul li > a.button.success:hover, .top-bar-section ul li > a.button.success:focus { color: white; } .top-bar-section ul li > a.button.alert { background-color: #f04124; border-color: #cf2a0e; color: white; } .top-bar-section ul li > a.button.alert:hover, .top-bar-section ul li > a.button.alert:focus { background-color: #cf2a0e; } .top-bar-section ul li > a.button.alert:hover, .top-bar-section ul li > a.button.alert:focus { color: white; } .top-bar-section ul li > button { font-size: 0.8125rem; padding-right: 15px; padding-left: 15px; background-color: #008cba; border-color: #007095; color: white; } .top-bar-section ul li > button:hover, .top-bar-section ul li > button:focus { background-color: #007095; } .top-bar-section ul li > button:hover, .top-bar-section ul li > button:focus { color: white; } .top-bar-section ul li > button.secondary { background-color: #e7e7e7; border-color: #b9b9b9; color: #333333; } .top-bar-section ul li > button.secondary:hover, .top-bar-section ul li > button.secondary:focus { background-color: #b9b9b9; } .top-bar-section ul li > button.secondary:hover, .top-bar-section ul li > button.secondary:focus { color: #333333; } .top-bar-section ul li > button.success { background-color: #43ac6a; border-color: #368a55; color: white; } .top-bar-section ul li > button.success:hover, .top-bar-section ul li > button.success:focus { background-color: #368a55; } .top-bar-section ul li > button.success:hover, .top-bar-section ul li > button.success:focus { color: white; } .top-bar-section ul li > button.alert { background-color: #f04124; border-color: #cf2a0e; color: white; } .top-bar-section ul li > button.alert:hover, .top-bar-section ul li > button.alert:focus { background-color: #cf2a0e; } .top-bar-section ul li > button.alert:hover, .top-bar-section ul li > button.alert:focus { color: white; } .top-bar-section ul li:hover:not(.has-form) > a { background: #272727; color: white; } .top-bar-section ul li.active > a { background: #008cba; color: white; } .top-bar-section ul li.active > a:hover { background: #0078a0; color: white; } .top-bar-section .has-form { padding: 15px; } .top-bar-section .has-dropdown { position: relative; } .top-bar-section .has-dropdown > a:after { content: ""; display: block; width: 0; height: 0; border: inset 5px; border-color: transparent transparent transparent rgba(255, 255, 255, 0.4); border-left-style: solid; margin-right: 15px; margin-top: -4.5px; position: absolute; top: 50%; right: 0; } .top-bar-section .has-dropdown.moved { position: static; } .top-bar-section .has-dropdown.moved > .dropdown { display: block; position: static !important; height: auto; width: auto; overflow: visible; clip: auto; position: absolute !important; width: 100%; } .top-bar-section .has-dropdown.moved > a:after { display: none; } .top-bar-section .dropdown { position: absolute; left: 100%; top: 0; z-index: 99; display: block; position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px, 1px, 1px, 1px); } .top-bar-section .dropdown li { width: 100%; height: auto; } .top-bar-section .dropdown li a { font-weight: normal; padding: 8px 15px; } .top-bar-section .dropdown li a.parent-link { font-weight: normal; } .top-bar-section .dropdown li.title h5 { margin-bottom: 0; } .top-bar-section .dropdown li.title h5 a { color: white; line-height: 22.5px; display: block; } .top-bar-section .dropdown li.has-form { padding: 8px 15px; } .top-bar-section .dropdown li .button, .top-bar-section .dropdown li button { top: auto; } .top-bar-section .dropdown label { padding: 8px 15px 2px; margin-bottom: 0; text-transform: uppercase; color: #777777; font-weight: bold; font-size: 0.625rem; } .js-generated { display: block; } @media only screen and (min-width: 40.063em) { .top-bar { background: #333333; *zoom: 1; overflow: visible; } .top-bar:before, .top-bar:after { content: " "; display: table; } .top-bar:after { clear: both; } .top-bar .toggle-topbar { display: none; } .top-bar .title-area { float: left; } .top-bar .name h1 a { width: auto; } .top-bar input, .top-bar .button, .top-bar button { font-size: 0.875rem; position: relative; top: 7px; } .top-bar.expanded { background: #333333; } .contain-to-grid .top-bar { max-width: 62.5rem; margin: 0 auto; margin-bottom: 0; } .top-bar-section { transition: none 0 0; left: 0 !important; } .top-bar-section ul { width: auto; height: auto !important; display: inline; } .top-bar-section ul li { float: left; } .top-bar-section ul li .js-generated { display: none; } .top-bar-section li.hover > a:not(.button) { background: #272727; color: white; } .top-bar-section li:not(.has-form) a:not(.button) { padding: 0 15px; line-height: 45px; background: #333333; } .top-bar-section li:not(.has-form) a:not(.button):hover { background: #272727; } .top-bar-section li.active:not(.has-form) a:not(.button) { padding: 0 15px; line-height: 45px; color: white; background: #008cba; } .top-bar-section li.active:not(.has-form) a:not(.button):hover { background: #0078a0; } .top-bar-section .has-dropdown > a { padding-right: 35px !important; } .top-bar-section .has-dropdown > a:after { content: ""; display: block; width: 0; height: 0; border: inset 5px; border-color: rgba(255, 255, 255, 0.4) transparent transparent transparent; border-top-style: solid; margin-top: -2.5px; top: 22.5px; } .top-bar-section .has-dropdown.moved { position: relative; } .top-bar-section .has-dropdown.moved > .dropdown { display: block; position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px, 1px, 1px, 1px); } .top-bar-section .has-dropdown.hover > .dropdown, .top-bar-section .has-dropdown.not-click:hover > .dropdown { display: block; position: static !important; height: auto; width: auto; overflow: visible; clip: auto; position: absolute !important; } .top-bar-section .has-dropdown .dropdown li.has-dropdown > a:after { border: none; content: "\00bb"; top: 1rem; margin-top: -1px; right: 5px; line-height: 1.2; } .top-bar-section .dropdown { left: 0; top: auto; background: transparent; min-width: 100%; } .top-bar-section .dropdown li a { color: white; line-height: 1; white-space: nowrap; padding: 12px 15px; background: #333333; } .top-bar-section .dropdown li:not(.has-form) a:not(.button) { color: white; background: #333333; } .top-bar-section .dropdown li:not(.has-form):hover > a:not(.button) { color: white; background: #272727; } .top-bar-section .dropdown li label { white-space: nowrap; background: #333333; } .top-bar-section .dropdown li .dropdown { left: 100%; top: 0; } .top-bar-section > ul > .divider, .top-bar-section > ul > [role="separator"] { border-bottom: none; border-top: none; border-right: solid 1px #4e4e4e; clear: none; height: 45px; width: 0; } .top-bar-section .has-form { background: #333333; padding: 0 15px; height: 45px; } .top-bar-section .right li .dropdown { left: auto; right: 0; } .top-bar-section .right li .dropdown li .dropdown { right: 100%; } .top-bar-section .left li .dropdown { right: auto; left: 0; } .top-bar-section .left li .dropdown li .dropdown { left: 100%; } .no-js .top-bar-section ul li:hover > a { background: #272727; color: white; } .no-js .top-bar-section ul li:active > a { background: #008cba; color: white; } .no-js .top-bar-section .has-dropdown:hover > .dropdown { display: block; position: static !important; height: auto; width: auto; overflow: visible; clip: auto; position: absolute !important; } } .breadcrumbs { display: block; padding: 0.5625rem 0.875rem 0.5625rem; overflow: hidden; margin-left: 0; list-style: none; border-style: solid; border-width: 1px; background-color: #f4f4f4; border-color: gainsboro; border-radius: 3px; } .breadcrumbs > * { margin: 0; float: left; font-size: 0.6875rem; line-height: 0.6875rem; text-transform: uppercase; color: #008cba; } .breadcrumbs > *:hover a, .breadcrumbs > *:focus a { text-decoration: underline; } .breadcrumbs > * a { color: #008cba; } .breadcrumbs > *.current { cursor: default; color: #333333; } .breadcrumbs > *.current a { cursor: default; color: #333333; } .breadcrumbs > *.current:hover, .breadcrumbs > *.current:hover a, .breadcrumbs > *.current:focus, .breadcrumbs > *.current:focus a { text-decoration: none; } .breadcrumbs > *.unavailable { color: #999999; } .breadcrumbs > *.unavailable a { color: #999999; } .breadcrumbs > *.unavailable:hover, .breadcrumbs > *.unavailable:hover a, .breadcrumbs > *.unavailable:focus, .breadcrumbs > *.unavailable a:focus { text-decoration: none; color: #999999; cursor: default; } .breadcrumbs > *:before { content: "/"; color: #aaaaaa; margin: 0 0.75rem; position: relative; top: 1px; } .breadcrumbs > *:first-child:before { content: " "; margin: 0; } .alert-box { border-style: solid; border-width: 1px; display: block; font-weight: normal; margin-bottom: 1.25rem; position: relative; padding: 0.875rem 1.5rem 0.875rem 0.875rem; font-size: 0.8125rem; transition: opacity 300ms ease-out; background-color: #008cba; border-color: #0078a0; color: white; } .alert-box .close { font-size: 1.375rem; padding: 9px 6px 4px; line-height: 0; position: absolute; top: 50%; margin-top: -0.6875rem; right: 0.25rem; color: #333333; opacity: 0.3; } .alert-box .close:hover, .alert-box .close:focus { opacity: 0.5; } .alert-box.radius { border-radius: 3px; } .alert-box.round { border-radius: 1000px; } .alert-box.success { background-color: #43ac6a; border-color: #3a945b; color: white; } .alert-box.alert { background-color: #f04124; border-color: #de2d0f; color: white; } .alert-box.secondary { background-color: #e7e7e7; border-color: #c7c7c7; color: #4f4f4f; } .alert-box.warning { background-color: #f08a24; border-color: #de770f; color: white; } .alert-box.info { background-color: #a0d3e8; border-color: #74bfdd; color: #4f4f4f; } .alert-box.alert-close { opacity: 0; } .inline-list { margin: 0 auto 1.0625rem auto; margin-left: -1.375rem; margin-right: 0; padding: 0; list-style: none; overflow: hidden; } .inline-list > li { list-style: none; float: left; margin-left: 1.375rem; display: block; } .inline-list > li > * { display: block; } .button-group { list-style: none; margin: 0; left: 0; *zoom: 1; } .button-group:before, .button-group:after { content: " "; display: table; } .button-group:after { clear: both; } .button-group li { margin: 0; float: left; } .button-group li > button, .button-group li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group li:first-child button, .button-group li:first-child .button { border-left: 0; } .button-group li:first-child { margin-left: 0; } .button-group.radius > * > button, .button-group.radius > * .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.radius > *:first-child button, .button-group.radius > *:first-child .button { border-left: 0; } .button-group.radius > *:first-child, .button-group.radius > *:first-child > a, .button-group.radius > *:first-child > button, .button-group.radius > *:first-child > .button { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .button-group.radius > *:last-child, .button-group.radius > *:last-child > a, .button-group.radius > *:last-child > button, .button-group.radius > *:last-child > .button { border-bottom-right-radius: 3px; border-top-right-radius: 3px; } .button-group.round > * > button, .button-group.round > * .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.round > *:first-child button, .button-group.round > *:first-child .button { border-left: 0; } .button-group.round > *:first-child, .button-group.round > *:first-child > a, .button-group.round > *:first-child > button, .button-group.round > *:first-child > .button { border-bottom-left-radius: 1000px; border-top-left-radius: 1000px; } .button-group.round > *:last-child, .button-group.round > *:last-child > a, .button-group.round > *:last-child > button, .button-group.round > *:last-child > .button { border-bottom-right-radius: 1000px; border-top-right-radius: 1000px; } .button-group.even-2 li { width: 50%; } .button-group.even-2 li > button, .button-group.even-2 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-2 li:first-child button, .button-group.even-2 li:first-child .button { border-left: 0; } .button-group.even-2 li button, .button-group.even-2 li .button { width: 100%; } .button-group.even-3 li { width: 33.33333%; } .button-group.even-3 li > button, .button-group.even-3 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-3 li:first-child button, .button-group.even-3 li:first-child .button { border-left: 0; } .button-group.even-3 li button, .button-group.even-3 li .button { width: 100%; } .button-group.even-4 li { width: 25%; } .button-group.even-4 li > button, .button-group.even-4 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-4 li:first-child button, .button-group.even-4 li:first-child .button { border-left: 0; } .button-group.even-4 li button, .button-group.even-4 li .button { width: 100%; } .button-group.even-5 li { width: 20%; } .button-group.even-5 li > button, .button-group.even-5 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-5 li:first-child button, .button-group.even-5 li:first-child .button { border-left: 0; } .button-group.even-5 li button, .button-group.even-5 li .button { width: 100%; } .button-group.even-6 li { width: 16.66667%; } .button-group.even-6 li > button, .button-group.even-6 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-6 li:first-child button, .button-group.even-6 li:first-child .button { border-left: 0; } .button-group.even-6 li button, .button-group.even-6 li .button { width: 100%; } .button-group.even-7 li { width: 14.28571%; } .button-group.even-7 li > button, .button-group.even-7 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-7 li:first-child button, .button-group.even-7 li:first-child .button { border-left: 0; } .button-group.even-7 li button, .button-group.even-7 li .button { width: 100%; } .button-group.even-8 li { width: 12.5%; } .button-group.even-8 li > button, .button-group.even-8 li .button { border-left: 1px solid; border-color: rgba(255, 255, 255, 0.5); } .button-group.even-8 li:first-child button, .button-group.even-8 li:first-child .button { border-left: 0; } .button-group.even-8 li button, .button-group.even-8 li .button { width: 100%; } .button-bar { *zoom: 1; } .button-bar:before, .button-bar:after { content: " "; display: table; } .button-bar:after { clear: both; } .button-bar .button-group { float: left; margin-right: 0.625rem; } .button-bar .button-group div { overflow: hidden; } /* Panels */ .panel { border-style: solid; border-width: 1px; border-color: #d8d8d8; margin-bottom: 1.25rem; padding: 1.25rem; background: #f2f2f2; } .panel > :first-child { margin-top: 0; } .panel > :last-child { margin-bottom: 0; } .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6, .panel p { color: #333333; } .panel h1, .panel h2, .panel h3, .panel h4, .panel h5, .panel h6 { line-height: 1; margin-bottom: 0.625rem; } .panel h1.subheader, .panel h2.subheader, .panel h3.subheader, .panel h4.subheader, .panel h5.subheader, .panel h6.subheader { line-height: 1.4; } .panel.callout { border-style: solid; border-width: 1px; border-color: #b6edff; margin-bottom: 1.25rem; padding: 1.25rem; background: #ecfaff; } .panel.callout > :first-child { margin-top: 0; } .panel.callout > :last-child { margin-bottom: 0; } .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6, .panel.callout p { color: #333333; } .panel.callout h1, .panel.callout h2, .panel.callout h3, .panel.callout h4, .panel.callout h5, .panel.callout h6 { line-height: 1; margin-bottom: 0.625rem; } .panel.callout h1.subheader, .panel.callout h2.subheader, .panel.callout h3.subheader, .panel.callout h4.subheader, .panel.callout h5.subheader, .panel.callout h6.subheader { line-height: 1.4; } .panel.callout a:not(.button) { color: #008cba; } .panel.radius { border-radius: 3px; } .dropdown.button, button.dropdown { position: relative; padding-right: 3.5625rem; } .dropdown.button:before, button.dropdown:before { position: absolute; content: ""; width: 0; height: 0; display: block; border-style: solid; border-color: white transparent transparent transparent; top: 50%; } .dropdown.button:before, button.dropdown:before { border-width: 0.375rem; right: 1.40625rem; margin-top: -0.15625rem; } .dropdown.button:before, button.dropdown:before { border-color: white transparent transparent transparent; } .dropdown.button.tiny, button.dropdown.tiny { padding-right: 2.625rem; } .dropdown.button.tiny:before, button.dropdown.tiny:before { border-width: 0.375rem; right: 1.125rem; margin-top: -0.125rem; } .dropdown.button.tiny:before, button.dropdown.tiny:before { border-color: white transparent transparent transparent; } .dropdown.button.small, button.dropdown.small { padding-right: 3.0625rem; } .dropdown.button.small:before, button.dropdown.small:before { border-width: 0.4375rem; right: 1.3125rem; margin-top: -0.15625rem; } .dropdown.button.small:before, button.dropdown.small:before { border-color: white transparent transparent transparent; } .dropdown.button.large, button.dropdown.large { padding-right: 3.625rem; } .dropdown.button.large:before, button.dropdown.large:before { border-width: 0.3125rem; right: 1.71875rem; margin-top: -0.15625rem; } .dropdown.button.large:before, button.dropdown.large:before { border-color: white transparent transparent transparent; } .dropdown.button.secondary:before, button.dropdown.secondary:before { border-color: #333333 transparent transparent transparent; } div.switch { position: relative; padding: 0; display: block; overflow: hidden; border-style: solid; border-width: 1px; margin-bottom: 1.25rem; height: 2.25rem; background: white; border-color: #cccccc; } div.switch label { position: relative; left: 0; z-index: 2; float: left; width: 50%; height: 100%; margin: 0; font-weight: bold; text-align: left; transition: all 0.1s ease-out; } div.switch input { position: absolute; z-index: 3; opacity: 0; width: 100%; height: 100%; -moz-appearance: none; } div.switch input:hover, div.switch input:focus { cursor: pointer; } div.switch span:last-child { position: absolute; top: -1px; left: -1px; z-index: 1; display: block; padding: 0; border-width: 1px; border-style: solid; transition: all 0.1s ease-out; } div.switch input:not(:checked) + label { opacity: 0; } div.switch input:checked { display: none !important; } div.switch input { left: 0; display: block !important; } div.switch input:first-of-type + label, div.switch input:first-of-type + span + label { left: -50%; } div.switch input:first-of-type:checked + label, div.switch input:first-of-type:checked + span + label { left: 0%; } div.switch input:last-of-type + label, div.switch input:last-of-type + span + label { right: -50%; left: auto; text-align: right; } div.switch input:last-of-type:checked + label, div.switch input:last-of-type:checked + span + label { right: 0%; left: auto; } div.switch span.custom { display: none !important; } form.custom div.switch .hidden-field { margin-left: auto; position: absolute; visibility: visible; } div.switch label { padding: 0; line-height: 2.3rem; font-size: 0.875rem; } div.switch input:first-of-type:checked ~ span:last-child { left: 100%; margin-left: -2.1875rem; } div.switch span:last-child { width: 2.25rem; height: 2.25rem; } div.switch span:last-child { border-color: #b3b3b3; background: white; background: linear-gradient(to bottom, white 0%, #f2f2f2 100%); box-shadow: 2px 0 10px 0 rgba(0, 0, 0, 0.07), 1000px 0 0 980px #f3faf6, -2px 0 10px 0 rgba(0, 0, 0, 0.07), -1000px 0 0 1000px whitesmoke; } div.switch:hover span:last-child, div.switch:focus span:last-child { background: white; background: linear-gradient(to bottom, white 0%, #e6e6e6 100%); } div.switch:active { background: transparent; } div.switch.large { height: 2.75rem; } div.switch.large label { padding: 0; line-height: 2.3rem; font-size: 1.0625rem; } div.switch.large input:first-of-type:checked ~ span:last-child { left: 100%; margin-left: -2.6875rem; } div.switch.large span:last-child { width: 2.75rem; height: 2.75rem; } div.switch.small { height: 1.75rem; } div.switch.small label { padding: 0; line-height: 2.1rem; font-size: 0.75rem; } div.switch.small input:first-of-type:checked ~ span:last-child { left: 100%; margin-left: -1.6875rem; } div.switch.small span:last-child { width: 1.75rem; height: 1.75rem; } div.switch.tiny { height: 1.375rem; } div.switch.tiny label { padding: 0; line-height: 1.9rem; font-size: 0.6875rem; } div.switch.tiny input:first-of-type:checked ~ span:last-child { left: 100%; margin-left: -1.3125rem; } div.switch.tiny span:last-child { width: 1.375rem; height: 1.375rem; } div.switch.radius { border-radius: 4px; } div.switch.radius span:last-child { border-radius: 3px; } div.switch.round { border-radius: 1000px; } div.switch.round span:last-child { border-radius: 999px; } div.switch.round label { padding: 0 0.5625rem; } /* Image Thumbnails */ .th { line-height: 0; display: inline-block; border: solid 4px white; max-width: 100%; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.2); transition: all 200ms ease-out; } .th:hover, .th:focus { box-shadow: 0 0 6px 1px rgba(0, 140, 186, 0.5); } .th.radius { border-radius: 3px; } /* Pricing Tables */ .pricing-table { border: solid 1px #dddddd; margin-left: 0; margin-bottom: 1.25rem; } .pricing-table * { list-style: none; line-height: 1; } .pricing-table .title { background-color: #333333; padding: 0.9375rem 1.25rem; text-align: center; color: #eeeeee; font-weight: normal; font-size: 1rem; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } .pricing-table .price { background-color: #f6f6f6; padding: 0.9375rem 1.25rem; text-align: center; color: #333333; font-weight: normal; font-size: 2rem; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } .pricing-table .description { background-color: white; padding: 0.9375rem; text-align: center; color: #777777; font-size: 0.75rem; font-weight: normal; line-height: 1.4; border-bottom: dotted 1px #dddddd; } .pricing-table .bullet-item { background-color: white; padding: 0.9375rem; text-align: center; color: #333333; font-size: 0.875rem; font-weight: normal; border-bottom: dotted 1px #dddddd; } .pricing-table .cta-button { background-color: white; text-align: center; padding: 1.25rem 1.25rem 0; } @keyframes rotate { from { -webkit-transform: rotate(0deg); -moz-transform: rotate(0deg); -ms-transform: rotate(0deg); -o-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(360deg); -moz-transform: rotate(360deg); -ms-transform: rotate(360deg); -o-transform: rotate(360deg); transform: rotate(360deg); } } /* Orbit Graceful Loading */ .slideshow-wrapper { position: relative; } .slideshow-wrapper ul { list-style-type: none; margin: 0; } .slideshow-wrapper ul li, .slideshow-wrapper ul li .orbit-caption { display: none; } .slideshow-wrapper ul li:first-child { display: block; } .slideshow-wrapper .orbit-container { background-color: transparent; } .slideshow-wrapper .orbit-container li { display: block; } .slideshow-wrapper .orbit-container li .orbit-caption { display: block; } .slideshow-wrapper .preloader { display: block; width: 40px; height: 40px; position: absolute; top: 50%; left: 50%; margin-top: -20px; margin-left: -20px; border: solid 3px; border-color: #555555 white; border-radius: 1000px; animation-name: rotate; animation-duration: 1.5s; animation-iteration-count: infinite; animation-timing-function: linear; } .orbit-container { overflow: hidden; width: 100%; position: relative; background: none; } .orbit-container .orbit-slides-container { list-style: none; margin: 0; padding: 0; position: relative; -webkit-transform: translateZ(0); } .orbit-container .orbit-slides-container img { display: block; max-width: 100%; } .orbit-container .orbit-slides-container.fade li { opacity: 0; transition: opacity 500ms ease-in-out; -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .orbit-container .orbit-slides-container.fade li.animate-in { opacity: 1; z-index: 20; transition: opacity 500ms ease-in-out; } .orbit-container .orbit-slides-container.fade li.animate-out { z-index: 10; transition: opacity 500ms ease-in-out; } .orbit-container .orbit-slides-container.swipe-next li { -ms-transform: translate(100%, 0); -webkit-transform: translate3d(100%, 0, 0); -moz-transform: translate3d(100%, 0, 0); -o-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } .orbit-container .orbit-slides-container.swipe-next li.animate-in { -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); transition-duration: 500ms; } .orbit-container .orbit-slides-container.swipe-next li.animate-out { -ms-transform: translate(-100%, 0); -webkit-transform: translate3d(-100%, 0, 0); -moz-transform: translate3d(-100%, 0, 0); -o-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); transition-duration: 500ms; } .orbit-container .orbit-slides-container.swipe-prev li { -ms-transform: translate(-100%, 0); -webkit-transform: translate3d(-100%, 0, 0); -moz-transform: translate3d(-100%, 0, 0); -o-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } .orbit-container .orbit-slides-container.swipe-prev li.animate-in { -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); transition-duration: 500ms; } .orbit-container .orbit-slides-container.swipe-prev li.animate-out { -ms-transform: translate(100%, 0); -webkit-transform: translate3d(100%, 0, 0); -moz-transform: translate3d(100%, 0, 0); -o-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); transition-duration: 500ms; } .orbit-container .orbit-slides-container li { position: absolute; top: 0; left: 0; width: 100%; -ms-transform: translate(100%, 0); -webkit-transform: translate3d(100%, 0, 0); -moz-transform: translate3d(100%, 0, 0); -o-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } .orbit-container .orbit-slides-container li.active { opacity: 1; top: 0; left: 0; -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .orbit-container .orbit-slides-container li .orbit-caption { position: absolute; bottom: 0; background-color: rgba(51, 51, 51, 0.8); color: white; width: 100%; padding: 0.625rem 0.875rem; font-size: 0.875rem; } .orbit-container .orbit-slide-number { position: absolute; top: 10px; left: 10px; font-size: 12px; color: white; background: rgba(0, 0, 0, 0); z-index: 10; } .orbit-container .orbit-slide-number span { font-weight: 700; padding: 0.3125rem; } .orbit-container .orbit-timer { position: absolute; top: 12px; right: 10px; height: 6px; width: 100px; z-index: 10; } .orbit-container .orbit-timer .orbit-progress { height: 3px; background-color: rgba(255, 255, 255, 0.3); display: block; width: 0%; position: relative; right: 20px; top: 5px; } .orbit-container .orbit-timer > span { display: none; position: absolute; top: 0px; right: 0; width: 11px; height: 14px; border: solid 4px white; border-top: none; border-bottom: none; } .orbit-container .orbit-timer.paused > span { right: -4px; top: 0px; width: 11px; height: 14px; border: inset 8px; border-left-style: solid; -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); border-color: transparent white transparent transparent; } .orbit-container .orbit-timer.paused > span.dark { border-color: transparent #333333 transparent transparent; } .orbit-container:hover .orbit-timer > span { display: block; } .orbit-container .orbit-prev, .orbit-container .orbit-next { position: absolute; top: 45%; margin-top: -25px; width: 36px; height: 60px; line-height: 50px; color: white; background-color: transparent; text-indent: -9999px !important; z-index: 10; } .orbit-container .orbit-prev:hover, .orbit-container .orbit-next:hover { background-color: rgba(0, 0, 0, 0.3); } .orbit-container .orbit-prev > span, .orbit-container .orbit-next > span { position: absolute; top: 50%; margin-top: -10px; display: block; width: 0; height: 0; border: inset 10px; } .orbit-container .orbit-prev { left: 0; } .orbit-container .orbit-prev > span { border-right-style: solid; border-color: transparent; border-right-color: white; } .orbit-container .orbit-prev:hover > span { border-right-color: white; } .orbit-container .orbit-next { right: 0; } .orbit-container .orbit-next > span { border-color: transparent; border-left-style: solid; border-left-color: white; left: 50%; margin-left: -4px; } .orbit-container .orbit-next:hover > span { border-left-color: white; } .orbit-container .orbit-bullets-container { text-align: center; } .orbit-container .orbit-bullets { margin: 0 auto 30px auto; overflow: hidden; position: relative; top: 10px; float: none; text-align: center; display: block; } .orbit-container .orbit-bullets li { display: inline-block; width: 0.5625rem; height: 0.5625rem; background: #cccccc; float: none; margin-right: 6px; border-radius: 1000px; } .orbit-container .orbit-bullets li.active { background: #999999; } .orbit-container .orbit-bullets li:last-child { margin-right: 0; } .touch .orbit-container .orbit-prev, .touch .orbit-container .orbit-next { display: none; } .touch .orbit-bullets { display: none; } @media only screen and (min-width: 40.063em) { .touch .orbit-container .orbit-prev, .touch .orbit-container .orbit-next { display: inherit; } .touch .orbit-bullets { display: block; } } @media only screen and (max-width: 40em) { .orbit-stack-on-small .orbit-slides-container { height: auto !important; } .orbit-stack-on-small .orbit-slides-container > * { position: relative; margin-left: 0% !important; opacity: 1 !important; -webkit-transform: none !important; -moz-transform: none !important; -ms-transform: none !important; -o-transform: none !important; transform: none !important; transition: none !important; } .orbit-stack-on-small .orbit-timer { display: none; } .orbit-stack-on-small .orbit-next, .orbit-stack-on-small .orbit-prev { display: none; } .orbit-stack-on-small .orbit-bullets { display: none; } } [data-magellan-expedition], [data-magellan-expedition-clone] { background: white; z-index: 50; min-width: 100%; padding: 10px; } [data-magellan-expedition] .sub-nav, [data-magellan-expedition-clone] .sub-nav { margin-bottom: 0; } [data-magellan-expedition] .sub-nav dd, [data-magellan-expedition-clone] .sub-nav dd { margin-bottom: 0; } [data-magellan-expedition] .sub-nav a, [data-magellan-expedition-clone] .sub-nav a { line-height: 1.8em; } .tabs { *zoom: 1; margin-bottom: 0 !important; } .tabs:before, .tabs:after { content: " "; display: table; } .tabs:after { clear: both; } .tabs dd { position: relative; margin-bottom: 0 !important; float: left; } .tabs dd > a { display: block; background: #efefef; color: #222222; padding: 1rem 2rem; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 1rem; } .tabs dd > a:hover { background: #e1e1e1; } .tabs dd.active a { background: white; } .tabs.radius dd:first-child a { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .tabs.radius dd:last-child a { border-bottom-right-radius: 3px; border-top-right-radius: 3px; } .tabs.vertical dd { position: inherit; float: none; display: block; top: auto; } .tabs-content { *zoom: 1; margin-bottom: 1.5rem; width: 100%; } .tabs-content:before, .tabs-content:after { content: " "; display: table; } .tabs-content:after { clear: both; } .tabs-content > .content { display: none; float: left; padding: 0.9375rem 0; width: 100%; } .tabs-content > .content.active { display: block; float: none; } .tabs-content > .content.contained { padding: 0.9375rem; } .tabs-content.vertical { display: block; } .tabs-content.vertical > .content { padding: 0 0.9375rem; } @media only screen and (min-width: 40.063em) { .tabs.vertical { width: 20%; float: left; margin-bottom: 1.25rem; } .tabs-content.vertical { width: 80%; float: left; margin-left: -1px; } } .no-js .tabs-content > .content { display: block; float: none; } ul.pagination { display: block; height: 1.5rem; margin-left: -0.3125rem; } ul.pagination li { height: 1.5rem; color: #222222; font-size: 0.875rem; margin-left: 0.3125rem; } ul.pagination li a { display: block; padding: 0.0625rem 0.625rem 0.0625rem; color: #999999; border-radius: 3px; } ul.pagination li:hover a, ul.pagination li a:focus { background: #e6e6e6; } ul.pagination li.unavailable a { cursor: default; color: #999999; } ul.pagination li.unavailable:hover a, ul.pagination li.unavailable a:focus { background: transparent; } ul.pagination li.current a { background: #008cba; color: white; font-weight: bold; cursor: default; } ul.pagination li.current a:hover, ul.pagination li.current a:focus { background: #008cba; } ul.pagination li { float: left; display: block; } /* Pagination centred wrapper */ .pagination-centered { text-align: center; } .pagination-centered ul.pagination li { float: none; display: inline-block; } .side-nav { display: block; margin: 0; padding: 0.875rem 0; list-style-type: none; list-style-position: inside; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } .side-nav li { margin: 0 0 0.4375rem 0; font-size: 0.875rem; } .side-nav li a:not(.button) { display: block; color: #008cba; } .side-nav li a:not(.button):hover, .side-nav li a:not(.button):focus { color: #1cc7ff; } .side-nav li.active > a:first-child:not(.button) { color: #1cc7ff; font-weight: normal; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; } .side-nav li.divider { border-top: 1px solid; height: 0; padding: 0; list-style: none; border-top-color: white; } .accordion { *zoom: 1; margin-bottom: 0; } .accordion:before, .accordion:after { content: " "; display: table; } .accordion:after { clear: both; } .accordion dd { display: block; margin-bottom: 0 !important; } .accordion dd.active > a { background: #e8e8e8; } .accordion dd > a { background: #efefef; color: #222222; padding: 1rem; display: block; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 1rem; } .accordion dd > a:hover { background: #e3e3e3; } .accordion .content { display: none; padding: 0.9375rem; } .accordion .content.active { display: block; background: white; } .text-left { text-align: left !important; } .text-right { text-align: right !important; } .text-center { text-align: center !important; } .text-justify { text-align: justify !important; } @media only screen and (max-width: 40em) { .small-only-text-left { text-align: left !important; } .small-only-text-right { text-align: right !important; } .small-only-text-center { text-align: center !important; } .small-only-text-justify { text-align: justify !important; } } @media only screen { .small-text-left { text-align: left !important; } .small-text-right { text-align: right !important; } .small-text-center { text-align: center !important; } .small-text-justify { text-align: justify !important; } } @media only screen and (min-width: 40.063em) and (max-width: 64em) { .medium-only-text-left { text-align: left !important; } .medium-only-text-right { text-align: right !important; } .medium-only-text-center { text-align: center !important; } .medium-only-text-justify { text-align: justify !important; } } @media only screen and (min-width: 40.063em) { .medium-text-left { text-align: left !important; } .medium-text-right { text-align: right !important; } .medium-text-center { text-align: center !important; } .medium-text-justify { text-align: justify !important; } } @media only screen and (min-width: 64.063em) and (max-width: 90em) { .large-only-text-left { text-align: left !important; } .large-only-text-right { text-align: right !important; } .large-only-text-center { text-align: center !important; } .large-only-text-justify { text-align: justify !important; } } @media only screen and (min-width: 64.063em) { .large-text-left { text-align: left !important; } .large-text-right { text-align: right !important; } .large-text-center { text-align: center !important; } .large-text-justify { text-align: justify !important; } } @media only screen and (min-width: 90.063em) and (max-width: 120em) { .xlarge-only-text-left { text-align: left !important; } .xlarge-only-text-right { text-align: right !important; } .xlarge-only-text-center { text-align: center !important; } .xlarge-only-text-justify { text-align: justify !important; } } @media only screen and (min-width: 90.063em) { .xlarge-text-left { text-align: left !important; } .xlarge-text-right { text-align: right !important; } .xlarge-text-center { text-align: center !important; } .xlarge-text-justify { text-align: justify !important; } } @media only screen and (min-width: 120.063em) and (max-width: 99999999em) { .xxlarge-only-text-left { text-align: left !important; } .xxlarge-only-text-right { text-align: right !important; } .xxlarge-only-text-center { text-align: center !important; } .xxlarge-only-text-justify { text-align: justify !important; } } @media only screen and (min-width: 120.063em) { .xxlarge-text-left { text-align: left !important; } .xxlarge-text-right { text-align: right !important; } .xxlarge-text-center { text-align: center !important; } .xxlarge-text-justify { text-align: justify !important; } } /* Typography resets */ div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; } /* Default Link Styles */ a { color: #008cba; text-decoration: none; line-height: inherit; } a:hover, a:focus { color: #0078a0; } a img { border: none; } /* Default paragraph styles */ p { font-family: inherit; font-weight: normal; font-size: 1rem; line-height: 1.6; margin-bottom: 1.25rem; text-rendering: optimizeLegibility; } p.lead { font-size: 1.21875rem; line-height: 1.6; } p aside { font-size: 0.875rem; line-height: 1.35; font-style: italic; } /* Default header styles */ h1, h2, h3, h4, h5, h6 { font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif !important; font-weight: normal; font-style: normal; color: #222222; text-rendering: optimizeLegibility; margin-top: 0.2rem; margin-bottom: 0.5rem; line-height: 1.4; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { font-size: 60%; color: #6f6f6f; line-height: 0; } h1 { font-size: 2.125rem; } h2 { font-size: 1.6875rem; } h3 { font-size: 1.375rem; } h4 { font-size: 1.125rem; } h5 { font-size: 1.125rem; } h6 { font-size: 1rem; } .subheader { line-height: 1.4; color: #6f6f6f; font-weight: normal; margin-top: 0.2rem; margin-bottom: 0.5rem; } hr { border: solid #dddddd; border-width: 1px 0 0; clear: both; margin: 1.25rem 0 1.1875rem; height: 0; } /* Helpful Typography Defaults */ em, i { font-style: italic; line-height: inherit; } strong, b { font-weight: bold; line-height: inherit; } small { font-size: 60%; line-height: inherit; } code { font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: bold; color: #bd260d; } /* Lists */ ul, ol, dl { font-size: 1rem; line-height: 1.6; margin-bottom: 1.25rem; list-style-position: outside; font-family: inherit; } ul { margin-left: 1.1rem; } ul.no-bullet { margin-left: 0; } ul.no-bullet li ul, ul.no-bullet li ol { margin-left: 1.25rem; margin-bottom: 0; list-style: none; } /* Unordered Lists */ ul li ul, ul li ol { margin-left: 1.25rem; margin-bottom: 0; } ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } ul.square { list-style-type: square; margin-left: 1.1rem; } ul.circle { list-style-type: circle; margin-left: 1.1rem; } ul.disc { list-style-type: disc; margin-left: 1.1rem; } ul.no-bullet { list-style: none; } /* Ordered Lists */ ol { margin-left: 1.4rem; } ol li ul, ol li ol { margin-left: 1.25rem; margin-bottom: 0; } /* Definition Lists */ dl dt { margin-bottom: 0.3rem; font-weight: bold; } dl dd { margin-bottom: 0.75rem; } /* Abbreviations */ abbr, acronym { text-transform: uppercase; font-size: 90%; color: #222222; border-bottom: 1px dotted #dddddd; cursor: help; } abbr { text-transform: none; } /* Blockquotes */ blockquote { margin: 0 0 1.25rem; padding: 0.5625rem 1.25rem 0 1.1875rem; border-left: 1px solid #dddddd; } blockquote cite { display: block; font-size: 0.8125rem; color: #555555; } blockquote cite:before { content: "\2014 \0020"; } blockquote cite a, blockquote cite a:visited { color: #555555; } blockquote, blockquote p { line-height: 1.6; color: #6f6f6f; } /* Microformats */ .vcard { display: inline-block; margin: 0 0 1.25rem 0; border: 1px solid #dddddd; padding: 0.625rem 0.75rem; } .vcard li { margin: 0; display: block; } .vcard .fn { font-weight: bold; font-size: 0.9375rem; } .vevent .summary { font-weight: bold; } .vevent abbr { cursor: default; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625rem; } @media only screen and (min-width: 40.063em) { h1, h2, h3, h4, h5, h6 { line-height: 1.4; } h1 { font-size: 2.75rem; } h2 { font-size: 2.3125rem; } h3 { font-size: 1.6875rem; } h4 { font-size: 1.4375rem; } } /* * Print styles. * * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) */ .print-only { display: none !important; } @media print { * { background: transparent !important; color: black !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999999; page-break-inside: avoid; } thead { display: table-header-group; /* h5bp.com/t */ } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } .hide-on-print { display: none !important; } .print-only { display: block !important; } .hide-for-print { display: none !important; } .show-for-print { display: inherit !important; } } .split.button { position: relative; padding-right: 5.0625rem; } .split.button span { display: block; height: 100%; position: absolute; right: 0; top: 0; border-left: solid 1px; } .split.button span:before { position: absolute; content: ""; width: 0; height: 0; display: block; border-style: inset; top: 50%; left: 50%; } .split.button span:active { background-color: rgba(0, 0, 0, 0.1); } .split.button span { border-left-color: rgba(255, 255, 255, 0.5); } .split.button span { width: 3.09375rem; } .split.button span:before { border-top-style: solid; border-width: 0.375rem; top: 48%; margin-left: -0.375rem; } .split.button span:before { border-color: white transparent transparent transparent; } .split.button.secondary span { border-left-color: rgba(255, 255, 255, 0.5); } .split.button.secondary span:before { border-color: white transparent transparent transparent; } .split.button.alert span { border-left-color: rgba(255, 255, 255, 0.5); } .split.button.success span { border-left-color: rgba(255, 255, 255, 0.5); } .split.button.tiny { padding-right: 3.75rem; } .split.button.tiny span { width: 2.25rem; } .split.button.tiny span:before { border-top-style: solid; border-width: 0.375rem; top: 48%; margin-left: -0.375rem; } .split.button.small { padding-right: 4.375rem; } .split.button.small span { width: 2.625rem; } .split.button.small span:before { border-top-style: solid; border-width: 0.4375rem; top: 48%; margin-left: -0.375rem; } .split.button.large { padding-right: 5.5rem; } .split.button.large span { width: 3.4375rem; } .split.button.large span:before { border-top-style: solid; border-width: 0.3125rem; top: 48%; margin-left: -0.375rem; } .split.button.expand { padding-left: 2rem; } .split.button.secondary span:before { border-color: #333333 transparent transparent transparent; } .split.button.radius span { border-bottom-right-radius: 3px; border-top-right-radius: 3px; } .split.button.round span { border-bottom-right-radius: 1000px; border-top-right-radius: 1000px; } .reveal-modal-bg { position: fixed; height: 100%; width: 100%; background: black; background: rgba(0, 0, 0, 0.45); z-index: 99; display: none; top: 0; left: 0; } dialog, .reveal-modal { visibility: hidden; display: none; position: absolute; z-index: 100; width: 100vw; top: 0; left: 0; background-color: white; padding: 1.25rem; border: solid 1px #666666; box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); } @media only screen and (max-width: 40em) { dialog, .reveal-modal { min-height: 100vh; } } @media only screen and (min-width: 40.063em) { dialog, .reveal-modal { left: 50%; } } dialog .column, dialog .columns, .reveal-modal .column, .reveal-modal .columns { min-width: 0; } dialog > :first-child, .reveal-modal > :first-child { margin-top: 0; } dialog > :last-child, .reveal-modal > :last-child { margin-bottom: 0; } @media only screen and (min-width: 40.063em) { dialog, .reveal-modal { margin-left: -40%; width: 80%; } } @media only screen and (min-width: 40.063em) { dialog, .reveal-modal { top: 6.25rem; } } dialog .close-reveal-modal, .reveal-modal .close-reveal-modal { font-size: 2.5rem; line-height: 1; position: absolute; top: 0.5rem; right: 0.6875rem; color: #aaaaaa; font-weight: bold; cursor: pointer; } dialog[open] { display: block; visibility: visible; } @media only screen and (min-width: 40.063em) { dialog, .reveal-modal { padding: 1.875rem; } dialog.radius, .reveal-modal.radius { border-radius: 3px; } dialog.round, .reveal-modal.round { border-radius: 1000px; } dialog.collapse, .reveal-modal.collapse { padding: 0; } dialog.full, .reveal-modal.full { top: 0; left: 0; height: 100vh; min-height: 100vh; margin-left: 0 !important; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.tiny, .reveal-modal.tiny { margin-left: -15%; width: 30%; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.small, .reveal-modal.small { margin-left: -20%; width: 40%; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.medium, .reveal-modal.medium { margin-left: -30%; width: 60%; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.large, .reveal-modal.large { margin-left: -35%; width: 70%; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.xlarge, .reveal-modal.xlarge { margin-left: -47.5%; width: 95%; } } @media only screen and (min-width: 40.063em) and (min-width: 40.063em) { dialog.full, .reveal-modal.full { margin-left: -50vw; width: 100vw; } } @media print { dialog, .reveal-modal { background: white !important; } } /* Tooltips */ .has-tip { border-bottom: dotted 1px #cccccc; cursor: help; font-weight: bold; color: #333333; } .has-tip:hover, .has-tip:focus { border-bottom: dotted 1px #003f54; color: #008cba; } .has-tip.tip-left, .has-tip.tip-right { float: none !important; } .tooltip { display: none; position: absolute; z-index: 999; font-weight: normal; font-size: 0.875rem; line-height: 1.3; padding: 0.75rem; max-width: 85%; left: 50%; width: 100%; color: white; background: #333333; } .tooltip > .nub { display: block; left: 5px; position: absolute; width: 0; height: 0; border: solid 5px; border-color: transparent transparent #333333 transparent; top: -10px; } .tooltip > .nub.rtl { left: auto; right: 5px; } .tooltip.radius { border-radius: 3px; } .tooltip.round { border-radius: 1000px; } .tooltip.round > .nub { left: 2rem; } .tooltip.opened { color: #008cba !important; border-bottom: dotted 1px #003f54 !important; } .tap-to-close { display: block; font-size: 0.625rem; color: #777777; font-weight: normal; } @media only screen and (min-width: 40.063em) { .tooltip > .nub { border-color: transparent transparent #333333 transparent; top: -10px; } .tooltip.tip-top > .nub { border-color: #333333 transparent transparent transparent; top: auto; bottom: -10px; } .tooltip.tip-left, .tooltip.tip-right { float: none !important; } .tooltip.tip-left > .nub { border-color: transparent transparent transparent #333333; right: -10px; left: auto; top: 50%; margin-top: -5px; } .tooltip.tip-right > .nub { border-color: transparent #333333 transparent transparent; right: auto; left: -10px; top: 50%; margin-top: -5px; } } /* Clearing Styles */ .clearing-thumbs, [data-clearing] { *zoom: 1; margin-bottom: 0; margin-left: 0; list-style: none; } .clearing-thumbs:before, .clearing-thumbs:after, [data-clearing]:before, [data-clearing]:after { content: " "; display: table; } .clearing-thumbs:after, [data-clearing]:after { clear: both; } .clearing-thumbs li, [data-clearing] li { float: left; margin-right: 10px; } .clearing-thumbs[class*="block-grid-"] li, [data-clearing][class*="block-grid-"] li { margin-right: 0; } .clearing-blackout { background: #333333; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 998; } .clearing-blackout .clearing-close { display: block; } .clearing-container { position: relative; z-index: 998; height: 100%; overflow: hidden; margin: 0; } .clearing-touch-label { position: absolute; top: 50%; left: 50%; color: #aaa; font-size: 0.6em; } .visible-img { height: 95%; position: relative; } .visible-img img { position: absolute; left: 50%; top: 50%; margin-left: -50%; max-height: 100%; max-width: 100%; } .clearing-caption { color: #cccccc; font-size: 0.875em; line-height: 1.3; margin-bottom: 0; text-align: center; bottom: 0; background: #333333; width: 100%; padding: 10px 30px 20px; position: absolute; left: 0; } .clearing-close { z-index: 999; padding-left: 20px; padding-top: 10px; font-size: 30px; line-height: 1; color: #cccccc; display: none; } .clearing-close:hover, .clearing-close:focus { color: #ccc; } .clearing-assembled .clearing-container { height: 100%; } .clearing-assembled .clearing-container .carousel > ul { display: none; } .clearing-feature li { display: none; } .clearing-feature li.clearing-featured-img { display: block; } @media only screen and (min-width: 40.063em) { .clearing-main-prev, .clearing-main-next { position: absolute; height: 100%; width: 40px; top: 0; } .clearing-main-prev > span, .clearing-main-next > span { position: absolute; top: 50%; display: block; width: 0; height: 0; border: solid 12px; } .clearing-main-prev > span:hover, .clearing-main-next > span:hover { opacity: 0.8; } .clearing-main-prev { left: 0; } .clearing-main-prev > span { left: 5px; border-color: transparent; border-right-color: #cccccc; } .clearing-main-next { right: 0; } .clearing-main-next > span { border-color: transparent; border-left-color: #cccccc; } .clearing-main-prev.disabled, .clearing-main-next.disabled { opacity: 0.3; } .clearing-assembled .clearing-container .carousel { background: rgba(51, 51, 51, 0.8); height: 120px; margin-top: 10px; text-align: center; } .clearing-assembled .clearing-container .carousel > ul { display: inline-block; z-index: 999; height: 100%; position: relative; float: none; } .clearing-assembled .clearing-container .carousel > ul li { display: block; width: 120px; min-height: inherit; float: left; overflow: hidden; margin-right: 0; padding: 0; position: relative; cursor: pointer; opacity: 0.4; clear: none; } .clearing-assembled .clearing-container .carousel > ul li.fix-height img { height: 100%; max-width: none; } .clearing-assembled .clearing-container .carousel > ul li a.th { border: none; box-shadow: none; display: block; } .clearing-assembled .clearing-container .carousel > ul li img { cursor: pointer !important; width: 100% !important; } .clearing-assembled .clearing-container .carousel > ul li.visible { opacity: 1; } .clearing-assembled .clearing-container .carousel > ul li:hover { opacity: 0.8; } .clearing-assembled .clearing-container .visible-img { background: #333333; overflow: hidden; height: 85%; } .clearing-close { position: absolute; top: 10px; right: 20px; padding-left: 0; padding-top: 0; } } /* Progress Bar */ .progress { background-color: #f6f6f6; height: 1.5625rem; border: 1px solid white; padding: 0.125rem; margin-bottom: 0.625rem; } .progress .meter { background: #008cba; height: 100%; display: block; } .progress.secondary .meter { background: #e7e7e7; height: 100%; display: block; } .progress.success .meter { background: #43ac6a; height: 100%; display: block; } .progress.alert .meter { background: #f04124; height: 100%; display: block; } .progress.radius { border-radius: 3px; } .progress.radius .meter { border-radius: 2px; } .progress.round { border-radius: 1000px; } .progress.round .meter { border-radius: 999px; } .sub-nav { display: block; width: auto; overflow: hidden; margin: -0.25rem 0 1.125rem; padding-top: 0.25rem; margin-right: 0; margin-left: -0.75rem; } .sub-nav dt { text-transform: uppercase; } .sub-nav dt, .sub-nav dd, .sub-nav li { float: left; display: inline; margin-left: 1rem; margin-bottom: 0.625rem; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-size: 0.875rem; color: #999999; } .sub-nav dt a, .sub-nav dd a, .sub-nav li a { text-decoration: none; color: #999999; padding: 0.1875rem 1rem; } .sub-nav dt a:hover, .sub-nav dd a:hover, .sub-nav li a:hover { color: #737373; } .sub-nav dt.active a, .sub-nav dd.active a, .sub-nav li.active a { border-radius: 3px; font-weight: normal; background: #008cba; padding: 0.1875rem 1rem; cursor: default; color: white; } .sub-nav dt.active a:hover, .sub-nav dd.active a:hover, .sub-nav li.active a:hover { background: #0078a0; } /* Foundation Joyride */ .joyride-list { display: none; } /* Default styles for the container */ .joyride-tip-guide { display: none; position: absolute; background: #333333; color: white; z-index: 101; top: 0; left: 2.5%; font-family: inherit; font-weight: normal; width: 95%; } .lt-ie9 .joyride-tip-guide { max-width: 800px; left: 50%; margin-left: -400px; } .joyride-content-wrapper { width: 100%; padding: 1.125rem 1.25rem 1.5rem; } .joyride-content-wrapper .button { margin-bottom: 0 !important; } /* Add a little css triangle pip, older browser just miss out on the fanciness of it */ .joyride-tip-guide .joyride-nub { display: block; position: absolute; left: 22px; width: 0; height: 0; border: 10px solid #333333; } .joyride-tip-guide .joyride-nub.top { border-top-style: solid; border-color: #333333; border-top-color: transparent !important; border-left-color: transparent !important; border-right-color: transparent !important; top: -20px; } .joyride-tip-guide .joyride-nub.bottom { border-bottom-style: solid; border-color: #333333 !important; border-bottom-color: transparent !important; border-left-color: transparent !important; border-right-color: transparent !important; bottom: -20px; } .joyride-tip-guide .joyride-nub.right { right: -20px; } .joyride-tip-guide .joyride-nub.left { left: -20px; } /* Typography */ .joyride-tip-guide h1, .joyride-tip-guide h2, .joyride-tip-guide h3, .joyride-tip-guide h4, .joyride-tip-guide h5, .joyride-tip-guide h6 { line-height: 1.25; margin: 0; font-weight: bold; color: white; } .joyride-tip-guide p { margin: 0 0 1.125rem 0; font-size: 0.875rem; line-height: 1.3; } .joyride-timer-indicator-wrap { width: 50px; height: 3px; border: solid 1px #555555; position: absolute; right: 1.0625rem; bottom: 1rem; } .joyride-timer-indicator { display: block; width: 0; height: inherit; background: #666666; } .joyride-close-tip { position: absolute; right: 12px; top: 10px; color: #777777 !important; text-decoration: none; font-size: 24px; font-weight: normal; line-height: 0.5 !important; } .joyride-close-tip:hover, .joyride-close-tip:focus { color: #eeeeee !important; } .joyride-modal-bg { position: fixed; height: 100%; width: 100%; background: transparent; background: rgba(0, 0, 0, 0.5); z-index: 100; display: none; top: 0; left: 0; cursor: pointer; } .joyride-expose-wrapper { background-color: #ffffff; position: absolute; border-radius: 3px; z-index: 102; box-shadow: 0 0 15px white; } .joyride-expose-cover { background: transparent; border-radius: 3px; position: absolute; z-index: 9999; top: 0; left: 0; } /* Styles for screens that are at least 768px; */ @media only screen and (min-width: 40.063em) { .joyride-tip-guide { width: 300px; left: inherit; } .joyride-tip-guide .joyride-nub.bottom { border-color: #333333 !important; border-bottom-color: transparent !important; border-left-color: transparent !important; border-right-color: transparent !important; bottom: -20px; } .joyride-tip-guide .joyride-nub.right { border-color: #333333 !important; border-top-color: transparent !important; border-right-color: transparent !important; border-bottom-color: transparent !important; top: 22px; left: auto; right: -20px; } .joyride-tip-guide .joyride-nub.left { border-color: #333333 !important; border-top-color: transparent !important; border-left-color: transparent !important; border-bottom-color: transparent !important; top: 22px; left: -20px; right: auto; } } .label { font-weight: normal; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; text-align: center; text-decoration: none; line-height: 1; white-space: nowrap; display: inline-block; position: relative; margin-bottom: inherit; padding: 0.25rem 0.5rem 0.375rem; font-size: 0.6875rem; background-color: #008cba; color: white; } .label.radius { border-radius: 3px; } .label.round { border-radius: 1000px; } .label.alert { background-color: #f04124; color: white; } .label.success { background-color: #43ac6a; color: white; } .label.secondary { background-color: #e7e7e7; color: #333333; } .off-canvas-wrap { -webkit-backface-visibility: hidden; position: relative; width: 100%; overflow: hidden; } .off-canvas-wrap.move-right, .off-canvas-wrap.move-left { min-height: 100%; -webkit-overflow-scrolling: touch; } .inner-wrap { -webkit-backface-visibility: hidden; position: relative; width: 100%; *zoom: 1; -webkit-transition: -webkit-transform 500ms ease; -moz-transition: -moz-transform 500ms ease; -ms-transition: -ms-transform 500ms ease; -o-transition: -o-transform 500ms ease; transition: transform 500ms ease; } .inner-wrap:before, .inner-wrap:after { content: " "; display: table; } .inner-wrap:after { clear: both; } .tab-bar { -webkit-backface-visibility: hidden; background: #333333; color: white; height: 2.8125rem; line-height: 2.8125rem; position: relative; } .tab-bar h1, .tab-bar h2, .tab-bar h3, .tab-bar h4, .tab-bar h5, .tab-bar h6 { color: white; font-weight: bold; line-height: 2.8125rem; margin: 0; } .tab-bar h1, .tab-bar h2, .tab-bar h3, .tab-bar h4 { font-size: 1.125rem; } .left-small { width: 2.8125rem; height: 2.8125rem; position: absolute; top: 0; border-right: solid 1px #1a1a1a; left: 0; } .right-small { width: 2.8125rem; height: 2.8125rem; position: absolute; top: 0; border-left: solid 1px #1a1a1a; right: 0; } .tab-bar-section { padding: 0 0.625rem; position: absolute; text-align: center; height: 2.8125rem; top: 0; } @media only screen and (min-width: 40.063em) { .tab-bar-section { text-align: left; } } .tab-bar-section.left { left: 0; right: 2.8125rem; } .tab-bar-section.right { left: 2.8125rem; right: 0; } .tab-bar-section.middle { left: 2.8125rem; right: 2.8125rem; } .tab-bar .menu-icon { text-indent: 2.1875rem; width: 2.8125rem; height: 2.8125rem; display: block; line-height: 2.0625rem; padding: 0; color: white; position: relative; -ms-transform: translate(0, 0); -webkit-transform: translate3d(0, 0, 0); -moz-transform: translate3d(0, 0, 0); -ms-transform: translate3d(0, 0, 0); -o-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } .tab-bar .menu-icon span { position: absolute; display: block; height: 0; width: 1rem; line-height: 1; top: 0.9375rem; left: 0.90625rem; box-shadow: 0 0px 0 1px white, 0 7px 0 1px white, 0 14px 0 1px white; } .tab-bar .menu-icon:hover span { box-shadow: 0 0px 0 1px #b3b3b3, 0 7px 0 1px #b3b3b3, 0 14px 0 1px #b3b3b3; } .left-off-canvas-menu { -webkit-backface-visibility: hidden; width: 15.625rem; top: 0; bottom: 0; position: absolute; overflow-y: auto; background: #333333; z-index: 1001; box-sizing: content-box; -webkit-overflow-scrolling: touch; -ms-transform: translate(-100%, 0); -webkit-transform: translate3d(-100%, 0, 0); -moz-transform: translate3d(-100%, 0, 0); -ms-transform: translate3d(-100%, 0, 0); -o-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); left: 0; } .left-off-canvas-menu * { -webkit-backface-visibility: hidden; } .right-off-canvas-menu { -webkit-backface-visibility: hidden; width: 15.625rem; top: 0; bottom: 0; position: absolute; overflow-y: auto; background: #333333; z-index: 1001; box-sizing: content-box; -webkit-overflow-scrolling: touch; -ms-transform: translate(100%, 0); -webkit-transform: translate3d(100%, 0, 0); -moz-transform: translate3d(100%, 0, 0); -ms-transform: translate3d(100%, 0, 0); -o-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); right: 0; } .right-off-canvas-menu * { -webkit-backface-visibility: hidden; } ul.off-canvas-list { list-style-type: none; padding: 0; margin: 0; } ul.off-canvas-list li label { padding: 0.3rem 0.9375rem; color: #999999; text-transform: uppercase; font-weight: bold; background: #444444; border-top: 1px solid #5e5e5e; border-bottom: none; margin: 0; } ul.off-canvas-list li a { display: block; padding: 0.66667rem; color: rgba(255, 255, 255, 0.7); border-bottom: 1px solid #262626; transition: background 300ms ease; } ul.off-canvas-list li a:hover { background: #242424; } .move-right > .inner-wrap { -ms-transform: translate(15.625rem, 0); -webkit-transform: translate3d(15.625rem, 0, 0); -moz-transform: translate3d(15.625rem, 0, 0); -ms-transform: translate3d(15.625rem, 0, 0); -o-transform: translate3d(15.625rem, 0, 0); transform: translate3d(15.625rem, 0, 0); } .move-right .exit-off-canvas { -webkit-backface-visibility: hidden; transition: background 300ms ease; cursor: pointer; box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); display: block; position: absolute; background: rgba(255, 255, 255, 0.2); top: 0; bottom: 0; left: 0; right: 0; z-index: 1002; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } @media only screen and (min-width: 40.063em) { .move-right .exit-off-canvas:hover { background: rgba(255, 255, 255, 0.05); } } .move-left > .inner-wrap { -ms-transform: translate(-15.625rem, 0); -webkit-transform: translate3d(-15.625rem, 0, 0); -moz-transform: translate3d(-15.625rem, 0, 0); -ms-transform: translate3d(-15.625rem, 0, 0); -o-transform: translate3d(-15.625rem, 0, 0); transform: translate3d(-15.625rem, 0, 0); } .move-left .exit-off-canvas { -webkit-backface-visibility: hidden; transition: background 300ms ease; cursor: pointer; box-shadow: -4px 0 4px rgba(0, 0, 0, 0.5), 4px 0 4px rgba(0, 0, 0, 0.5); display: block; position: absolute; background: rgba(255, 255, 255, 0.2); top: 0; bottom: 0; left: 0; right: 0; z-index: 1002; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } @media only screen and (min-width: 40.063em) { .move-left .exit-off-canvas:hover { background: rgba(255, 255, 255, 0.05); } } .no-csstransforms .left-off-canvas-menu { left: -15.625rem; } .no-csstransforms .right-off-canvas-menu { right: -15.625rem; } .no-csstransforms .move-left > .inner-wrap { right: 15.625rem; } .no-csstransforms .move-right > .inner-wrap { left: 15.625rem; } /* Foundation Dropdowns */ .f-dropdown { position: absolute; left: -9999px; list-style: none; margin-left: 0; width: 100%; max-height: none; height: auto; background: white; border: solid 1px #cccccc; font-size: 0.875rem; z-index: 99; margin-top: 2px; max-width: 200px; } .f-dropdown > *:first-child { margin-top: 0; } .f-dropdown > *:last-child { margin-bottom: 0; } .f-dropdown:before { content: ""; display: block; width: 0; height: 0; border: inset 6px; border-color: transparent transparent white transparent; border-bottom-style: solid; position: absolute; top: -12px; left: 10px; z-index: 99; } .f-dropdown:after { content: ""; display: block; width: 0; height: 0; border: inset 7px; border-color: transparent transparent #cccccc transparent; border-bottom-style: solid; position: absolute; top: -14px; left: 9px; z-index: 98; } .f-dropdown.right:before { left: auto; right: 10px; } .f-dropdown.right:after { left: auto; right: 9px; } .f-dropdown.drop-right { position: absolute; left: -9999px; list-style: none; margin-left: 0; width: 100%; max-height: none; height: auto; background: white; border: solid 1px #cccccc; font-size: 0.875rem; z-index: 99; margin-top: 0; margin-left: 2px; max-width: 200px; } .f-dropdown.drop-right > *:first-child { margin-top: 0; } .f-dropdown.drop-right > *:last-child { margin-bottom: 0; } .f-dropdown.drop-right:before { content: ""; display: block; width: 0; height: 0; border: inset 6px; border-color: transparent white transparent transparent; border-right-style: solid; position: absolute; top: 10px; left: -12px; z-index: 99; } .f-dropdown.drop-right:after { content: ""; display: block; width: 0; height: 0; border: inset 7px; border-color: transparent #cccccc transparent transparent; border-right-style: solid; position: absolute; top: 9px; left: -14px; z-index: 98; } .f-dropdown.drop-left { position: absolute; left: -9999px; list-style: none; margin-left: 0; width: 100%; max-height: none; height: auto; background: white; border: solid 1px #cccccc; font-size: 0.875rem; z-index: 99; margin-top: 0; margin-left: -2px; max-width: 200px; } .f-dropdown.drop-left > *:first-child { margin-top: 0; } .f-dropdown.drop-left > *:last-child { margin-bottom: 0; } .f-dropdown.drop-left:before { content: ""; display: block; width: 0; height: 0; border: inset 6px; border-color: transparent transparent transparent white; border-left-style: solid; position: absolute; top: 10px; right: -12px; left: auto; z-index: 99; } .f-dropdown.drop-left:after { content: ""; display: block; width: 0; height: 0; border: inset 7px; border-color: transparent transparent transparent #cccccc; border-left-style: solid; position: absolute; top: 9px; right: -14px; left: auto; z-index: 98; } .f-dropdown.drop-top { position: absolute; left: -9999px; list-style: none; margin-left: 0; width: 100%; max-height: none; height: auto; background: white; border: solid 1px #cccccc; font-size: 0.875rem; z-index: 99; margin-top: -2px; margin-left: 0; max-width: 200px; } .f-dropdown.drop-top > *:first-child { margin-top: 0; } .f-dropdown.drop-top > *:last-child { margin-bottom: 0; } .f-dropdown.drop-top:before { content: ""; display: block; width: 0; height: 0; border: inset 6px; border-color: white transparent transparent transparent; border-top-style: solid; position: absolute; top: auto; bottom: -12px; left: 10px; right: auto; z-index: 99; } .f-dropdown.drop-top:after { content: ""; display: block; width: 0; height: 0; border: inset 7px; border-color: #cccccc transparent transparent transparent; border-top-style: solid; position: absolute; top: auto; bottom: -14px; left: 9px; right: auto; z-index: 98; } .f-dropdown li { font-size: 0.875rem; cursor: pointer; line-height: 1.125rem; margin: 0; } .f-dropdown li:hover, .f-dropdown li:focus { background: #eeeeee; } .f-dropdown li a { display: block; padding: 0.5rem; color: #555555; } .f-dropdown.content { position: absolute; left: -9999px; list-style: none; margin-left: 0; padding: 1.25rem; width: 100%; height: auto; max-height: none; background: white; border: solid 1px #cccccc; font-size: 0.875rem; z-index: 99; max-width: 200px; } .f-dropdown.content > *:first-child { margin-top: 0; } .f-dropdown.content > *:last-child { margin-bottom: 0; } .f-dropdown.tiny { max-width: 200px; } .f-dropdown.small { max-width: 300px; } .f-dropdown.medium { max-width: 500px; } .f-dropdown.large { max-width: 800px; } table { background: white; margin-bottom: 1.25rem; border: solid 1px #dddddd; } table thead, table tfoot { background: whitesmoke; } table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5rem 0.625rem 0.625rem; font-size: 0.875rem; font-weight: bold; color: #222222; text-align: left; } table tr th, table tr td { padding: 0.5625rem 0.625rem; font-size: 0.875rem; color: #222222; } table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #f9f9f9; } table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.125rem; } /* Standard Forms */ form { margin: 0 0 1rem; } /* Using forms within rows, we need to set some defaults */ form .row .row { margin: 0 -0.5rem; } form .row .row .column, form .row .row .columns { padding: 0 0.5rem; } form .row .row.collapse { margin: 0; } form .row .row.collapse .column, form .row .row.collapse .columns { padding: 0; } form .row .row.collapse input { border-bottom-right-radius: 0; border-top-right-radius: 0; } form .row input.column, form .row input.columns, form .row textarea.column, form .row textarea.columns { padding-left: 0.5rem; } /* Label Styles */ label { font-size: 0.875rem; color: #4d4d4d; cursor: pointer; display: block; font-weight: normal; line-height: 1.5; margin-bottom: 0; /* Styles for required inputs */ } label.right { float: none; text-align: right; } label.inline { margin: 0 0 1rem 0; padding: 0.5625rem 0; } label small { text-transform: capitalize; color: #676767; } select::-ms-expand { display: none; } @-moz-document url-prefix() { select { background: #fafafa; } select:hover { background: #f3f3f3; } } /* Attach elements to the beginning or end of an input */ .prefix, .postfix { display: block; position: relative; z-index: 2; text-align: center; width: 100%; padding-top: 0; padding-bottom: 0; border-style: solid; border-width: 1px; overflow: hidden; font-size: 0.875rem; height: 2.3125rem; line-height: 2.3125rem; } /* Adjust padding, alignment and radius if pre/post element is a button */ .postfix.button { padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0; text-align: center; line-height: 2.125rem; border: none; } .prefix.button { padding-left: 0; padding-right: 0; padding-top: 0; padding-bottom: 0; text-align: center; line-height: 2.125rem; border: none; } .prefix.button.radius { border-radius: 0; border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .postfix.button.radius { border-radius: 0; border-bottom-right-radius: 3px; border-top-right-radius: 3px; } .prefix.button.round { border-radius: 0; border-bottom-left-radius: 1000px; border-top-left-radius: 1000px; } .postfix.button.round { border-radius: 0; border-bottom-right-radius: 1000px; border-top-right-radius: 1000px; } /* Separate prefix and postfix styles when on span or label so buttons keep their own */ span.prefix, label.prefix { background: #f2f2f2; border-right: none; color: #333333; border-color: #cccccc; } span.prefix.radius, label.prefix.radius { border-radius: 0; border-bottom-left-radius: 3px; border-top-left-radius: 3px; } span.postfix, label.postfix { background: #f2f2f2; border-left: none; color: #333333; border-color: #cccccc; } span.postfix.radius, label.postfix.radius { border-radius: 0; border-bottom-right-radius: 3px; border-top-right-radius: 3px; } /* We use this to get basic styling on all basic form elements */ input[type="text"], input[type="password"], input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="month"], input[type="week"], input[type="email"], input[type="number"], input[type="search"], input[type="tel"], input[type="time"], input[type="url"], textarea { -webkit-appearance: none; background-color: white; font-family: inherit; border: 1px solid #cccccc; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); color: rgba(0, 0, 0, 0.75); display: block; font-size: 0.875rem; margin: 0 0 1rem 0; padding: 0.5rem; height: 2.3125rem; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; transition: box-shadow 0.45s, border-color 0.45s ease-in-out; } input[type="text"]:focus, input[type="password"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="week"]:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="time"]:focus, input[type="url"]:focus, textarea:focus { box-shadow: 0 0 5px #999999; border-color: #999999; } input[type="text"]:focus, input[type="password"]:focus, input[type="date"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="month"]:focus, input[type="week"]:focus, input[type="email"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="time"]:focus, input[type="url"]:focus, textarea:focus { background: #fafafa; border-color: #999999; outline: none; } input[type="text"][disabled], fieldset[disabled] input[type="text"], input[type="password"][disabled], fieldset[disabled] input[type="password"], input[type="date"][disabled], fieldset[disabled] input[type="date"], input[type="datetime"][disabled], fieldset[disabled] input[type="datetime"], input[type="datetime-local"][disabled], fieldset[disabled] input[type="datetime-local"], input[type="month"][disabled], fieldset[disabled] input[type="month"], input[type="week"][disabled], fieldset[disabled] input[type="week"], input[type="email"][disabled], fieldset[disabled] input[type="email"], input[type="number"][disabled], fieldset[disabled] input[type="number"], input[type="search"][disabled], fieldset[disabled] input[type="search"], input[type="tel"][disabled], fieldset[disabled] input[type="tel"], input[type="time"][disabled], fieldset[disabled] input[type="time"], input[type="url"][disabled], fieldset[disabled] input[type="url"], textarea[disabled], fieldset[disabled] textarea { background-color: #dddddd; } input[type="text"].radius, input[type="password"].radius, input[type="date"].radius, input[type="datetime"].radius, input[type="datetime-local"].radius, input[type="month"].radius, input[type="week"].radius, input[type="email"].radius, input[type="number"].radius, input[type="search"].radius, input[type="tel"].radius, input[type="time"].radius, input[type="url"].radius, textarea.radius { border-radius: 3px; } input[type="submit"] { -webkit-appearance: none; } /* Respect enforced amount of rows for textarea */ textarea[rows] { height: auto; } /* Add height value for select elements to match text input height */ select { -webkit-appearance: none !important; background-color: #fafafa; background-image: url("data:image/svg+xml;base64, PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg=="); background-repeat: no-repeat; background-position: 97% center; border: 1px solid #cccccc; padding: 0.5rem; font-size: 0.875rem; border-radius: 0; height: 2.3125rem; } select.radius { border-radius: 3px; } select:hover { background-color: #f3f3f3; border-color: #999999; } /* Adjust margin for form elements below */ input[type="file"], input[type="checkbox"], input[type="radio"], select { margin: 0 0 1rem 0; } input[type="checkbox"] + label, input[type="radio"] + label { display: inline-block; margin-left: 0.5rem; margin-right: 1rem; margin-bottom: 0; vertical-align: baseline; } /* Normalize file input width */ input[type="file"] { width: 100%; } /* We add basic fieldset styling */ fieldset { border: 1px solid #dddddd; padding: 1.25rem; margin: 1.125rem 0; } fieldset legend { font-weight: bold; background: white; padding: 0 0.1875rem; margin: 0; margin-left: -0.1875rem; } /* Error Handling */ [data-abide] .error small.error, [data-abide] span.error, [data-abide] small.error { display: block; padding: 0.375rem 0.5625rem 0.5625rem; margin-top: -1px; margin-bottom: 1rem; font-size: 0.75rem; font-weight: normal; font-style: italic; background: #f04124; color: white; } [data-abide] span.error, [data-abide] small.error { display: none; } span.error, small.error { display: block; padding: 0.375rem 0.5625rem 0.5625rem; margin-top: -1px; margin-bottom: 1rem; font-size: 0.75rem; font-weight: normal; font-style: italic; background: #f04124; color: white; } .error input, .error textarea, .error select { margin-bottom: 0; } .error input[type="checkbox"], .error input[type="radio"] { margin-bottom: 1rem; } .error label, .error label.error { color: #f04124; } .error small.error { display: block; padding: 0.375rem 0.5625rem 0.5625rem; margin-top: -1px; margin-bottom: 1rem; font-size: 0.75rem; font-weight: normal; font-style: italic; background: #f04124; color: white; } .error > label > small { color: #676767; background: transparent; padding: 0; text-transform: capitalize; font-style: normal; font-size: 60%; margin: 0; display: inline; } .error span.error-message { display: block; } input.error, textarea.error { margin-bottom: 0; } label.error { color: #f04124; } .range-slider { display: block; position: relative; width: 100%; height: 1rem; border: 1px solid #dddddd; margin: 1.25rem 0; -ms-touch-action: none; touch-action: none; background: #fafafa; } .range-slider.vertical-range { display: block; position: relative; width: 100%; height: 1rem; border: 1px solid #dddddd; margin: 1.25rem 0; -ms-touch-action: none; touch-action: none; display: inline-block; width: 1rem; height: 12.5rem; } .range-slider.vertical-range .range-slider-handle { margin-top: 0; margin-left: -0.5rem; position: absolute; bottom: -10.5rem; } .range-slider.vertical-range .range-slider-active-segment { width: 0.875rem; height: auto; bottom: 0; } .range-slider.radius { background: #fafafa; border-radius: 3px; } .range-slider.radius .range-slider-handle { background: #008cba; border-radius: 3px; } .range-slider.radius .range-slider-handle:hover { background: #007ba4; } .range-slider.round { background: #fafafa; border-radius: 1000px; } .range-slider.round .range-slider-handle { background: #008cba; border-radius: 1000px; } .range-slider.round .range-slider-handle:hover { background: #007ba4; } .range-slider-active-segment { display: inline-block; position: absolute; height: 0.875rem; background: #e5e5e5; } .range-slider-handle { display: inline-block; position: absolute; z-index: 1; top: -0.3125rem; width: 2rem; height: 1.375rem; border: 1px solid none; cursor: pointer; background: #008cba; } .range-slider-handle:hover { background: #007ba4; } [class*="block-grid-"] { display: block; padding: 0; margin: 0 -0.625rem; *zoom: 1; } [class*="block-grid-"]:before, [class*="block-grid-"]:after { content: " "; display: table; } [class*="block-grid-"]:after { clear: both; } [class*="block-grid-"] > li { display: block; height: auto; float: left; padding: 0 0.625rem 1.25rem; } @media only screen { .small-block-grid-1 > li { width: 100%; list-style: none; } .small-block-grid-1 > li:nth-of-type(n) { clear: none; } .small-block-grid-1 > li:nth-of-type(1n+1) { clear: both; } .small-block-grid-2 > li { width: 50%; list-style: none; } .small-block-grid-2 > li:nth-of-type(n) { clear: none; } .small-block-grid-2 > li:nth-of-type(2n+1) { clear: both; } .small-block-grid-3 > li { width: 33.33333%; list-style: none; } .small-block-grid-3 > li:nth-of-type(n) { clear: none; } .small-block-grid-3 > li:nth-of-type(3n+1) { clear: both; } .small-block-grid-4 > li { width: 25%; list-style: none; } .small-block-grid-4 > li:nth-of-type(n) { clear: none; } .small-block-grid-4 > li:nth-of-type(4n+1) { clear: both; } .small-block-grid-5 > li { width: 20%; list-style: none; } .small-block-grid-5 > li:nth-of-type(n) { clear: none; } .small-block-grid-5 > li:nth-of-type(5n+1) { clear: both; } .small-block-grid-6 > li { width: 16.66667%; list-style: none; } .small-block-grid-6 > li:nth-of-type(n) { clear: none; } .small-block-grid-6 > li:nth-of-type(6n+1) { clear: both; } .small-block-grid-7 > li { width: 14.28571%; list-style: none; } .small-block-grid-7 > li:nth-of-type(n) { clear: none; } .small-block-grid-7 > li:nth-of-type(7n+1) { clear: both; } .small-block-grid-8 > li { width: 12.5%; list-style: none; } .small-block-grid-8 > li:nth-of-type(n) { clear: none; } .small-block-grid-8 > li:nth-of-type(8n+1) { clear: both; } .small-block-grid-9 > li { width: 11.11111%; list-style: none; } .small-block-grid-9 > li:nth-of-type(n) { clear: none; } .small-block-grid-9 > li:nth-of-type(9n+1) { clear: both; } .small-block-grid-10 > li { width: 10%; list-style: none; } .small-block-grid-10 > li:nth-of-type(n) { clear: none; } .small-block-grid-10 > li:nth-of-type(10n+1) { clear: both; } .small-block-grid-11 > li { width: 9.09091%; list-style: none; } .small-block-grid-11 > li:nth-of-type(n) { clear: none; } .small-block-grid-11 > li:nth-of-type(11n+1) { clear: both; } .small-block-grid-12 > li { width: 8.33333%; list-style: none; } .small-block-grid-12 > li:nth-of-type(n) { clear: none; } .small-block-grid-12 > li:nth-of-type(12n+1) { clear: both; } } @media only screen and (min-width: 40.063em) { .medium-block-grid-1 > li { width: 100%; list-style: none; } .medium-block-grid-1 > li:nth-of-type(n) { clear: none; } .medium-block-grid-1 > li:nth-of-type(1n+1) { clear: both; } .medium-block-grid-2 > li { width: 50%; list-style: none; } .medium-block-grid-2 > li:nth-of-type(n) { clear: none; } .medium-block-grid-2 > li:nth-of-type(2n+1) { clear: both; } .medium-block-grid-3 > li { width: 33.33333%; list-style: none; } .medium-block-grid-3 > li:nth-of-type(n) { clear: none; } .medium-block-grid-3 > li:nth-of-type(3n+1) { clear: both; } .medium-block-grid-4 > li { width: 25%; list-style: none; } .medium-block-grid-4 > li:nth-of-type(n) { clear: none; } .medium-block-grid-4 > li:nth-of-type(4n+1) { clear: both; } .medium-block-grid-5 > li { width: 20%; list-style: none; } .medium-block-grid-5 > li:nth-of-type(n) { clear: none; } .medium-block-grid-5 > li:nth-of-type(5n+1) { clear: both; } .medium-block-grid-6 > li { width: 16.66667%; list-style: none; } .medium-block-grid-6 > li:nth-of-type(n) { clear: none; } .medium-block-grid-6 > li:nth-of-type(6n+1) { clear: both; } .medium-block-grid-7 > li { width: 14.28571%; list-style: none; } .medium-block-grid-7 > li:nth-of-type(n) { clear: none; } .medium-block-grid-7 > li:nth-of-type(7n+1) { clear: both; } .medium-block-grid-8 > li { width: 12.5%; list-style: none; } .medium-block-grid-8 > li:nth-of-type(n) { clear: none; } .medium-block-grid-8 > li:nth-of-type(8n+1) { clear: both; } .medium-block-grid-9 > li { width: 11.11111%; list-style: none; } .medium-block-grid-9 > li:nth-of-type(n) { clear: none; } .medium-block-grid-9 > li:nth-of-type(9n+1) { clear: both; } .medium-block-grid-10 > li { width: 10%; list-style: none; } .medium-block-grid-10 > li:nth-of-type(n) { clear: none; } .medium-block-grid-10 > li:nth-of-type(10n+1) { clear: both; } .medium-block-grid-11 > li { width: 9.09091%; list-style: none; } .medium-block-grid-11 > li:nth-of-type(n) { clear: none; } .medium-block-grid-11 > li:nth-of-type(11n+1) { clear: both; } .medium-block-grid-12 > li { width: 8.33333%; list-style: none; } .medium-block-grid-12 > li:nth-of-type(n) { clear: none; } .medium-block-grid-12 > li:nth-of-type(12n+1) { clear: both; } } @media only screen and (min-width: 64.063em) { .large-block-grid-1 > li { width: 100%; list-style: none; } .large-block-grid-1 > li:nth-of-type(n) { clear: none; } .large-block-grid-1 > li:nth-of-type(1n+1) { clear: both; } .large-block-grid-2 > li { width: 50%; list-style: none; } .large-block-grid-2 > li:nth-of-type(n) { clear: none; } .large-block-grid-2 > li:nth-of-type(2n+1) { clear: both; } .large-block-grid-3 > li { width: 33.33333%; list-style: none; } .large-block-grid-3 > li:nth-of-type(n) { clear: none; } .large-block-grid-3 > li:nth-of-type(3n+1) { clear: both; } .large-block-grid-4 > li { width: 25%; list-style: none; } .large-block-grid-4 > li:nth-of-type(n) { clear: none; } .large-block-grid-4 > li:nth-of-type(4n+1) { clear: both; } .large-block-grid-5 > li { width: 20%; list-style: none; } .large-block-grid-5 > li:nth-of-type(n) { clear: none; } .large-block-grid-5 > li:nth-of-type(5n+1) { clear: both; } .large-block-grid-6 > li { width: 16.66667%; list-style: none; } .large-block-grid-6 > li:nth-of-type(n) { clear: none; } .large-block-grid-6 > li:nth-of-type(6n+1) { clear: both; } .large-block-grid-7 > li { width: 14.28571%; list-style: none; } .large-block-grid-7 > li:nth-of-type(n) { clear: none; } .large-block-grid-7 > li:nth-of-type(7n+1) { clear: both; } .large-block-grid-8 > li { width: 12.5%; list-style: none; } .large-block-grid-8 > li:nth-of-type(n) { clear: none; } .large-block-grid-8 > li:nth-of-type(8n+1) { clear: both; } .large-block-grid-9 > li { width: 11.11111%; list-style: none; } .large-block-grid-9 > li:nth-of-type(n) { clear: none; } .large-block-grid-9 > li:nth-of-type(9n+1) { clear: both; } .large-block-grid-10 > li { width: 10%; list-style: none; } .large-block-grid-10 > li:nth-of-type(n) { clear: none; } .large-block-grid-10 > li:nth-of-type(10n+1) { clear: both; } .large-block-grid-11 > li { width: 9.09091%; list-style: none; } .large-block-grid-11 > li:nth-of-type(n) { clear: none; } .large-block-grid-11 > li:nth-of-type(11n+1) { clear: both; } .large-block-grid-12 > li { width: 8.33333%; list-style: none; } .large-block-grid-12 > li:nth-of-type(n) { clear: none; } .large-block-grid-12 > li:nth-of-type(12n+1) { clear: both; } } .flex-video { position: relative; padding-top: 1.5625rem; padding-bottom: 67.5%; height: 0; margin-bottom: 1rem; overflow: hidden; } .flex-video.widescreen { padding-bottom: 56.34%; } .flex-video.vimeo { padding-top: 0; } .flex-video iframe, .flex-video object, .flex-video embed, .flex-video video { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .keystroke, kbd { background-color: #ededed; border-color: #dddddd; color: #222222; border-style: solid; border-width: 1px; margin: 0; font-family: "Consolas", "Menlo", "Courier", monospace; font-size: inherit; padding: 0.125rem 0.25rem 0; border-radius: 3px; } /* small displays */ @media only screen { .show-for-small-only, .show-for-small-up, .show-for-small, .show-for-small-down, .hide-for-medium-only, .hide-for-medium-up, .hide-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { display: inherit !important; } .hide-for-small-only, .hide-for-small-up, .hide-for-small, .hide-for-small-down, .show-for-medium-only, .show-for-medium-up, .show-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { display: none !important; } table.show-for-small-only, table.show-for-small-up, table.show-for-small, table.show-for-small-down, table.hide-for-medium-only, table.hide-for-medium-up, table.hide-for-medium, table.show-for-medium-down, table.hide-for-large-only, table.hide-for-large-up, table.hide-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { display: table; } thead.show-for-small-only, thead.show-for-small-up, thead.show-for-small, thead.show-for-small-down, thead.hide-for-medium-only, thead.hide-for-medium-up, thead.hide-for-medium, thead.show-for-medium-down, thead.hide-for-large-only, thead.hide-for-large-up, thead.hide-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { display: table-header-group !important; } tbody.show-for-small-only, tbody.show-for-small-up, tbody.show-for-small, tbody.show-for-small-down, tbody.hide-for-medium-only, tbody.hide-for-medium-up, tbody.hide-for-medium, tbody.show-for-medium-down, tbody.hide-for-large-only, tbody.hide-for-large-up, tbody.hide-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { display: table-row-group !important; } tr.show-for-small-only, tr.show-for-small-up, tr.show-for-small, tr.show-for-small-down, tr.hide-for-medium-only, tr.hide-for-medium-up, tr.hide-for-medium, tr.show-for-medium-down, tr.hide-for-large-only, tr.hide-for-large-up, tr.hide-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { display: table-row !important; } th.show-for-small-only, td.show-for-small-only, th.show-for-small-up, td.show-for-small-up, th.show-for-small, td.show-for-small, th.show-for-small-down, td.show-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.hide-for-medium-up, td.hide-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.show-for-medium-down, td.show-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.hide-for-large-up, td.hide-for-large-up, th.hide-for-large, td.hide-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { display: table-cell !important; } } /* medium displays */ @media only screen and (min-width: 40.063em) { .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .show-for-medium-only, .show-for-medium-up, .show-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { display: inherit !important; } .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .hide-for-medium-only, .hide-for-medium-up, .hide-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { display: none !important; } table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.show-for-medium-only, table.show-for-medium-up, table.show-for-medium, table.show-for-medium-down, table.hide-for-large-only, table.hide-for-large-up, table.hide-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { display: table; } thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.show-for-medium-only, thead.show-for-medium-up, thead.show-for-medium, thead.show-for-medium-down, thead.hide-for-large-only, thead.hide-for-large-up, thead.hide-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { display: table-header-group !important; } tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.show-for-medium-only, tbody.show-for-medium-up, tbody.show-for-medium, tbody.show-for-medium-down, tbody.hide-for-large-only, tbody.hide-for-large-up, tbody.hide-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { display: table-row-group !important; } tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.show-for-medium-only, tr.show-for-medium-up, tr.show-for-medium, tr.show-for-medium-down, tr.hide-for-large-only, tr.hide-for-large-up, tr.hide-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { display: table-row !important; } th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.show-for-medium-only, td.show-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.show-for-medium, td.show-for-medium, th.show-for-medium-down, td.show-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.hide-for-large-up, td.hide-for-large-up, th.hide-for-large, td.hide-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { display: table-cell !important; } } /* large displays */ @media only screen and (min-width: 64.063em) { .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .show-for-large-only, .show-for-large-up, .show-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { display: inherit !important; } .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .hide-for-large-only, .hide-for-large-up, .hide-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { display: none !important; } table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.show-for-large-only, table.show-for-large-up, table.show-for-large, table.show-for-large-down, table.hide-for-xlarge-only, table.hide-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { display: table; } thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.show-for-large-only, thead.show-for-large-up, thead.show-for-large, thead.show-for-large-down, thead.hide-for-xlarge-only, thead.hide-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { display: table-header-group !important; } tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.show-for-large-only, tbody.show-for-large-up, tbody.show-for-large, tbody.show-for-large-down, tbody.hide-for-xlarge-only, tbody.hide-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { display: table-row-group !important; } tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.show-for-large-only, tr.show-for-large-up, tr.show-for-large, tr.show-for-large-down, tr.hide-for-xlarge-only, tr.hide-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { display: table-row !important; } th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.show-for-large-only, td.show-for-large-only, th.show-for-large-up, td.show-for-large-up, th.show-for-large, td.show-for-large, th.show-for-large-down, td.show-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.hide-for-xlarge-up, td.hide-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { display: table-cell !important; } } /* xlarge displays */ @media only screen and (min-width: 90.063em) { .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .hide-for-large-only, .show-for-large-up, .hide-for-large, .hide-for-large-down, .show-for-xlarge-only, .show-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { display: inherit !important; } .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .show-for-large-only, .hide-for-large-up, .show-for-large, .show-for-large-down, .hide-for-xlarge-only, .hide-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { display: none !important; } table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-large-only, table.show-for-large-up, table.hide-for-large, table.hide-for-large-down, table.show-for-xlarge-only, table.show-for-xlarge-up, table.hide-for-xxlarge-only, table.hide-for-xxlarge-up { display: table; } thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-large-only, thead.show-for-large-up, thead.hide-for-large, thead.hide-for-large-down, thead.show-for-xlarge-only, thead.show-for-xlarge-up, thead.hide-for-xxlarge-only, thead.hide-for-xxlarge-up { display: table-header-group !important; } tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-large-only, tbody.show-for-large-up, tbody.hide-for-large, tbody.hide-for-large-down, tbody.show-for-xlarge-only, tbody.show-for-xlarge-up, tbody.hide-for-xxlarge-only, tbody.hide-for-xxlarge-up { display: table-row-group !important; } tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-large-only, tr.show-for-large-up, tr.hide-for-large, tr.hide-for-large-down, tr.show-for-xlarge-only, tr.show-for-xlarge-up, tr.hide-for-xxlarge-only, tr.hide-for-xxlarge-up { display: table-row !important; } th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.show-for-large-up, td.show-for-large-up, th.hide-for-large, td.hide-for-large, th.hide-for-large-down, td.hide-for-large-down, th.show-for-xlarge-only, td.show-for-xlarge-only, th.show-for-xlarge-up, td.show-for-xlarge-up, th.hide-for-xxlarge-only, td.hide-for-xxlarge-only, th.hide-for-xxlarge-up, td.hide-for-xxlarge-up { display: table-cell !important; } } /* xxlarge displays */ @media only screen and (min-width: 120.063em) { .hide-for-small-only, .show-for-small-up, .hide-for-small, .hide-for-small-down, .hide-for-medium-only, .show-for-medium-up, .hide-for-medium, .hide-for-medium-down, .hide-for-large-only, .show-for-large-up, .hide-for-large, .hide-for-large-down, .hide-for-xlarge-only, .show-for-xlarge-up, .show-for-xxlarge-only, .show-for-xxlarge-up { display: inherit !important; } .show-for-small-only, .hide-for-small-up, .show-for-small, .show-for-small-down, .show-for-medium-only, .hide-for-medium-up, .show-for-medium, .show-for-medium-down, .show-for-large-only, .hide-for-large-up, .show-for-large, .show-for-large-down, .show-for-xlarge-only, .hide-for-xlarge-up, .hide-for-xxlarge-only, .hide-for-xxlarge-up { display: none !important; } table.hide-for-small-only, table.show-for-small-up, table.hide-for-small, table.hide-for-small-down, table.hide-for-medium-only, table.show-for-medium-up, table.hide-for-medium, table.hide-for-medium-down, table.hide-for-large-only, table.show-for-large-up, table.hide-for-large, table.hide-for-large-down, table.hide-for-xlarge-only, table.show-for-xlarge-up, table.show-for-xxlarge-only, table.show-for-xxlarge-up { display: table; } thead.hide-for-small-only, thead.show-for-small-up, thead.hide-for-small, thead.hide-for-small-down, thead.hide-for-medium-only, thead.show-for-medium-up, thead.hide-for-medium, thead.hide-for-medium-down, thead.hide-for-large-only, thead.show-for-large-up, thead.hide-for-large, thead.hide-for-large-down, thead.hide-for-xlarge-only, thead.show-for-xlarge-up, thead.show-for-xxlarge-only, thead.show-for-xxlarge-up { display: table-header-group !important; } tbody.hide-for-small-only, tbody.show-for-small-up, tbody.hide-for-small, tbody.hide-for-small-down, tbody.hide-for-medium-only, tbody.show-for-medium-up, tbody.hide-for-medium, tbody.hide-for-medium-down, tbody.hide-for-large-only, tbody.show-for-large-up, tbody.hide-for-large, tbody.hide-for-large-down, tbody.hide-for-xlarge-only, tbody.show-for-xlarge-up, tbody.show-for-xxlarge-only, tbody.show-for-xxlarge-up { display: table-row-group !important; } tr.hide-for-small-only, tr.show-for-small-up, tr.hide-for-small, tr.hide-for-small-down, tr.hide-for-medium-only, tr.show-for-medium-up, tr.hide-for-medium, tr.hide-for-medium-down, tr.hide-for-large-only, tr.show-for-large-up, tr.hide-for-large, tr.hide-for-large-down, tr.hide-for-xlarge-only, tr.show-for-xlarge-up, tr.show-for-xxlarge-only, tr.show-for-xxlarge-up { display: table-row !important; } th.hide-for-small-only, td.hide-for-small-only, th.show-for-small-up, td.show-for-small-up, th.hide-for-small, td.hide-for-small, th.hide-for-small-down, td.hide-for-small-down, th.hide-for-medium-only, td.hide-for-medium-only, th.show-for-medium-up, td.show-for-medium-up, th.hide-for-medium, td.hide-for-medium, th.hide-for-medium-down, td.hide-for-medium-down, th.hide-for-large-only, td.hide-for-large-only, th.show-for-large-up, td.show-for-large-up, th.hide-for-large, td.hide-for-large, th.hide-for-large-down, td.hide-for-large-down, th.hide-for-xlarge-only, td.hide-for-xlarge-only, th.show-for-xlarge-up, td.show-for-xlarge-up, th.show-for-xxlarge-only, td.show-for-xxlarge-only, th.show-for-xxlarge-up, td.show-for-xxlarge-up { display: table-cell !important; } } /* Orientation targeting */ .show-for-landscape, .hide-for-portrait { display: inherit !important; } .hide-for-landscape, .show-for-portrait { display: none !important; } /* Specific visibility for tables */ table.hide-for-landscape, table.show-for-portrait { display: table; } thead.hide-for-landscape, thead.show-for-portrait { display: table-header-group !important; } tbody.hide-for-landscape, tbody.show-for-portrait { display: table-row-group !important; } tr.hide-for-landscape, tr.show-for-portrait { display: table-row !important; } td.hide-for-landscape, td.show-for-portrait, th.hide-for-landscape, th.show-for-portrait { display: table-cell !important; } @media only screen and (orientation: landscape) { .show-for-landscape, .hide-for-portrait { display: inherit !important; } .hide-for-landscape, .show-for-portrait { display: none !important; } /* Specific visibility for tables */ table.show-for-landscape, table.hide-for-portrait { display: table; } thead.show-for-landscape, thead.hide-for-portrait { display: table-header-group !important; } tbody.show-for-landscape, tbody.hide-for-portrait { display: table-row-group !important; } tr.show-for-landscape, tr.hide-for-portrait { display: table-row !important; } td.show-for-landscape, td.hide-for-portrait, th.show-for-landscape, th.hide-for-portrait { display: table-cell !important; } } @media only screen and (orientation: portrait) { .show-for-portrait, .hide-for-landscape { display: inherit !important; } .hide-for-portrait, .show-for-landscape { display: none !important; } /* Specific visibility for tables */ table.show-for-portrait, table.hide-for-landscape { display: table; } thead.show-for-portrait, thead.hide-for-landscape { display: table-header-group !important; } tbody.show-for-portrait, tbody.hide-for-landscape { display: table-row-group !important; } tr.show-for-portrait, tr.hide-for-landscape { display: table-row !important; } td.show-for-portrait, td.hide-for-landscape, th.show-for-portrait, th.hide-for-landscape { display: table-cell !important; } } /* Touch-enabled device targeting */ .show-for-touch { display: none !important; } .hide-for-touch { display: inherit !important; } .touch .show-for-touch { display: inherit !important; } .touch .hide-for-touch { display: none !important; } /* Specific visibility for tables */ table.hide-for-touch { display: table; } .touch table.show-for-touch { display: table; } thead.hide-for-touch { display: table-header-group !important; } .touch thead.show-for-touch { display: table-header-group !important; } tbody.hide-for-touch { display: table-row-group !important; } .touch tbody.show-for-touch { display: table-row-group !important; } tr.hide-for-touch { display: table-row !important; } .touch tr.show-for-touch { display: table-row !important; } td.hide-for-touch { display: table-cell !important; } .touch td.show-for-touch { display: table-cell !important; } th.hide-for-touch { display: table-cell !important; } .touch th.show-for-touch { display: table-cell !important; } ================================================ FILE: docs/css/gettingstarted.css ================================================ #chart5_1 .c3-line-data2 { stroke-width: 5px; } ================================================ FILE: docs/css/index.css ================================================ ================================================ FILE: docs/css/normalize.css ================================================ /*! normalize.css v3.0.0 | MIT License | git.io/normalize */ /** * 1. Set default font family to sans-serif. * 2. Prevent iOS text size adjust after orientation change, without disabling * user zoom. */ html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } /** * Remove default margin. */ body { margin: 0; } /* HTML5 display definitions ========================================================================== */ /** * Correct `block` display not defined in IE 8/9. */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } /** * 1. Correct `inline-block` display not defined in IE 8/9. * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. */ audio, canvas, progress, video { display: inline-block; /* 1 */ vertical-align: baseline; /* 2 */ } /** * Prevent modern browsers from displaying `audio` without controls. * Remove excess height in iOS 5 devices. */ audio:not([controls]) { display: none; height: 0; } /** * Address `[hidden]` styling not present in IE 8/9. * Hide the `template` element in IE, Safari, and Firefox < 22. */ [hidden], template { display: none; } /* Links ========================================================================== */ /** * Remove the gray background color from active links in IE 10. */ a { background: transparent; } /** * Improve readability when focused and also mouse hovered in all browsers. */ a:active, a:hover { outline: 0; } /* Text-level semantics ========================================================================== */ /** * Address styling not present in IE 8/9, Safari 5, and Chrome. */ abbr[title] { border-bottom: 1px dotted; } /** * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ b, strong { font-weight: bold; } /** * Address styling not present in Safari 5 and Chrome. */ dfn { font-style: italic; } /** * Address variable `h1` font-size and margin within `section` and `article` * contexts in Firefox 4+, Safari 5, and Chrome. */ h1 { font-size: 2em; margin: 0.67em 0; } /** * Address styling not present in IE 8/9. */ mark { background: #ff0; color: #000; } /** * Address inconsistent and variable font size in all browsers. */ small { font-size: 80%; } /** * Prevent `sub` and `sup` affecting `line-height` in all browsers. */ sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } /* Embedded content ========================================================================== */ /** * Remove border when inside `a` element in IE 8/9. */ img { border: 0; } /** * Correct overflow displayed oddly in IE 9. */ svg:not(:root) { overflow: hidden; } /* Grouping content ========================================================================== */ /** * Address margin not present in IE 8/9 and Safari 5. */ figure { margin: 1em 40px; } /** * Address differences between Firefox and other browsers. */ hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } /** * Contain overflow in all browsers. */ pre { overflow: auto; } /** * Address odd `em`-unit font size rendering in all browsers. */ code, kbd, pre, samp { font-family: monospace, monospace; font-size: 1em; } /* Forms ========================================================================== */ /** * Known limitation: by default, Chrome and Safari on OS X allow very limited * styling of `select`, unless a `border` property is set. */ /** * 1. Correct color not being inherited. * Known issue: affects color of disabled elements. * 2. Correct font properties not being inherited. * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ button, input, optgroup, select, textarea { color: inherit; /* 1 */ font: inherit; /* 2 */ margin: 0; /* 3 */ } /** * Address `overflow` set to `hidden` in IE 8/9/10. */ button { overflow: visible; } /** * Address inconsistent `text-transform` inheritance for `button` and `select`. * All other form control elements do not inherit `text-transform` values. * Correct `button` style inheritance in Firefox, IE 8+, and Opera * Correct `select` style inheritance in Firefox. */ button, select { text-transform: none; } /** * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` * and `video` controls. * 2. Correct inability to style clickable `input` types in iOS. * 3. Improve usability and consistency of cursor style between image-type * `input` and others. */ button, html input[type="button"], /* 1 */ input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } /** * Re-set default cursor for disabled elements. */ button[disabled], html input[disabled] { cursor: default; } /** * Remove inner padding and border in Firefox 4+. */ button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } /** * Address Firefox 4+ setting `line-height` on `input` using `!important` in * the UA stylesheet. */ input { line-height: normal; } /** * It's recommended that you don't attempt to style these elements. * Firefox's implementation doesn't respect box-sizing, padding, or width. * * 1. Address box sizing set to `content-box` in IE 8/9/10. * 2. Remove excess padding in IE 8/9/10. */ input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } /** * Fix the cursor style for Chrome's increment/decrement buttons. For certain * `font-size` values of the `input`, it causes the cursor style of the * decrement button to change from `default` to `text`. */ input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { height: auto; } /** * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome * (include `-moz` to future-proof). */ input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } /** * Remove inner padding and search cancel button in Safari and Chrome on OS X. * Safari (but not Chrome) clips the cancel button when the search input has * padding (and `textfield` appearance). */ input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } /** * Define consistent border, margin, and padding. */ fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } /** * 1. Correct `color` not being inherited in IE 8/9. * 2. Remove padding so people aren't caught out if they zero out fieldsets. */ legend { border: 0; /* 1 */ padding: 0; /* 2 */ } /** * Remove default vertical scrollbar in IE 8/9. */ textarea { overflow: auto; } /** * Don't inherit the `font-weight` (applied by a rule above). * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. */ optgroup { font-weight: bold; } /* Tables ========================================================================== */ /** * Remove most spacing between table cells. */ table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; } ================================================ FILE: docs/css/reference.css ================================================ section p { margin-bottom: 0; } section h5 { margin-top: 1rem; } section code { padding-left: 0; } .sourcecode { margin-bottom: 1.25rem; padding: 6px 10px; background-color: #f4f4f4; } .sourcecode pre { padding: 0; font-size: 1em; } .sourcecode pre code { background-color: #f4f4f4; } ================================================ FILE: docs/css/samples/api_axis_label.css ================================================ ================================================ FILE: docs/css/samples/api_axis_range.css ================================================ ================================================ FILE: docs/css/samples/api_data_color.css ================================================ ================================================ FILE: docs/css/samples/api_data_name.css ================================================ ================================================ FILE: docs/css/samples/api_flow.css ================================================ ================================================ FILE: docs/css/samples/api_grid_x.css ================================================ ================================================ FILE: docs/css/samples/api_resize.css ================================================ ================================================ FILE: docs/css/samples/axes_label.css ================================================ ================================================ FILE: docs/css/samples/axes_label_position.css ================================================ ================================================ FILE: docs/css/samples/axes_rotated.css ================================================ ================================================ FILE: docs/css/samples/axes_x_localtime.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_count.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_culling.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_fit.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_format.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_rotate.css ================================================ ================================================ FILE: docs/css/samples/axes_x_tick_values.css ================================================ ================================================ FILE: docs/css/samples/axes_y2.css ================================================ ================================================ FILE: docs/css/samples/axes_y_padding.css ================================================ ================================================ FILE: docs/css/samples/axes_y_range.css ================================================ ================================================ FILE: docs/css/samples/axes_y_tick_format.css ================================================ ================================================ FILE: docs/css/samples/categorized.css ================================================ ================================================ FILE: docs/css/samples/chart_area.css ================================================ ================================================ FILE: docs/css/samples/chart_area_stacked.css ================================================ ================================================ FILE: docs/css/samples/chart_bar.css ================================================ ================================================ FILE: docs/css/samples/chart_bar_negative.css ================================================ ================================================ FILE: docs/css/samples/chart_bar_stacked.css ================================================ ================================================ FILE: docs/css/samples/chart_combination.css ================================================ ================================================ FILE: docs/css/samples/chart_donut.css ================================================ ================================================ FILE: docs/css/samples/chart_gauge.css ================================================ ================================================ FILE: docs/css/samples/chart_pie.css ================================================ ================================================ FILE: docs/css/samples/chart_scatter.css ================================================ ================================================ FILE: docs/css/samples/chart_spline.css ================================================ ================================================ FILE: docs/css/samples/chart_stanford.css ================================================ ================================================ FILE: docs/css/samples/chart_step.css ================================================ ================================================ FILE: docs/css/samples/data_color.css ================================================ ================================================ FILE: docs/css/samples/data_columned.css ================================================ ================================================ FILE: docs/css/samples/data_json.css ================================================ ================================================ FILE: docs/css/samples/data_label.css ================================================ ================================================ FILE: docs/css/samples/data_label_format.css ================================================ ================================================ FILE: docs/css/samples/data_load.css ================================================ ================================================ FILE: docs/css/samples/data_name.css ================================================ ================================================ FILE: docs/css/samples/data_order.css ================================================ ================================================ FILE: docs/css/samples/data_rowed.css ================================================ ================================================ FILE: docs/css/samples/data_stringx.css ================================================ ================================================ FILE: docs/css/samples/data_url.css ================================================ ================================================ FILE: docs/css/samples/data_xformat.css ================================================ ================================================ FILE: docs/css/samples/grid_x_lines.css ================================================ ================================================ FILE: docs/css/samples/grid_y_lines.css ================================================ ================================================ FILE: docs/css/samples/interaction_zoom.css ================================================ ================================================ FILE: docs/css/samples/legend_custom.css ================================================ .legend span { width: 33.333333%; display: inline-block; text-align: center; cursor: pointer; color: white; } ================================================ FILE: docs/css/samples/legend_position.css ================================================ ================================================ FILE: docs/css/samples/options_color.css ================================================ ================================================ FILE: docs/css/samples/options_gridline.css ================================================ ================================================ FILE: docs/css/samples/options_legend.css ================================================ ================================================ FILE: docs/css/samples/options_padding.css ================================================ ================================================ FILE: docs/css/samples/options_size.css ================================================ ================================================ FILE: docs/css/samples/options_subchart.css ================================================ ================================================ FILE: docs/css/samples/pie_label_format.css ================================================ ================================================ FILE: docs/css/samples/point_show.css ================================================ ================================================ FILE: docs/css/samples/region.css ================================================ .c3-region.regionY { fill: red; } .c3-region.regionY2 { fill: green; } ================================================ FILE: docs/css/samples/region_timeseries.css ================================================ ================================================ FILE: docs/css/samples/simple_multiple.css ================================================ ================================================ FILE: docs/css/samples/simple_regions.css ================================================ ================================================ FILE: docs/css/samples/simple_xy.css ================================================ ================================================ FILE: docs/css/samples/simple_xy_multiple.css ================================================ ================================================ FILE: docs/css/samples/style_grid.css ================================================ .c3-xgrid-line line { stroke: blue; } .c3-xgrid-line.grid4 line { stroke: pink; } .c3-xgrid-line.grid4 text { fill: pink; } .c3-ygrid-line line { stroke: red; } .c3-ygrid-line.grid800 line { stroke: green; } .c3-ygrid-line.grid800 text { fill: green; } ================================================ FILE: docs/css/samples/style_region.css ================================================ .c3-region-0 { fill: red; } .c3-region.foo { fill: green; } ================================================ FILE: docs/css/samples/timeseries.css ================================================ ================================================ FILE: docs/css/samples/tooltip_format.css ================================================ ================================================ FILE: docs/css/samples/tooltip_grouped.css ================================================ ================================================ FILE: docs/css/samples/tooltip_show.css ================================================ ================================================ FILE: docs/css/samples/transform_area.css ================================================ ================================================ FILE: docs/css/samples/transform_areaspline.css ================================================ ================================================ FILE: docs/css/samples/transform_bar.css ================================================ ================================================ FILE: docs/css/samples/transform_donut.css ================================================ ================================================ FILE: docs/css/samples/transform_line.css ================================================ ================================================ FILE: docs/css/samples/transform_pie.css ================================================ ================================================ FILE: docs/css/samples/transform_scatter.css ================================================ ================================================ FILE: docs/css/samples/transform_spline.css ================================================ ================================================ FILE: docs/css/samples/transition_duration.css ================================================ ================================================ FILE: docs/css/style.css ================================================ body { background-color: #fdfdfd; } footer { text-align: right; } footer hr { margin-bottom: 10px; } footer p { margin-bottom: 10px; } #chart { height: 280px; margin: 0px auto; text-align: center; } #message { font-size: 16px; text-align: center; } #link { margin-top: 18px; float: right; } #link .git-icon { height: 32px; } .antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .container { width: 90%; margin: 0 auto; } .container.sidemenu { position: relative; } .container.sidemenu footer { position: absolute; bottom: 0; left: 0; right: 0; z-index: 100; background-color: #fdfdfd; } @media only screen and (min-width: 40.063em) { .container.sidemenu .column-menu { position: fixed; overflow: auto; top: 60px; bottom: 0; padding-bottom: 60px; width: 280px; } .container.sidemenu .column-content { padding-left: 10px; padding-top: 20px; padding-bottom: 60px; margin-left: 280px; float: none; } } .container.sidemenu .column-menu .side-bar { position: relative; } .container.sidemenu .column-menu .side-bar li > a { display: inline; padding-left: 12px; } .container.sidemenu .column-menu .side-bar .label { top: -2px; padding: 0.22222rem 0.44444rem 0.33333rem; font-size: 0.5rem; margin-bottom: 0; } .container.sidemenu .column-content section .label { top: -4px; padding: 0.22222rem 0.44444rem 0.33333rem; font-size: 0.61rem; } .container hr.large { margin: 48px 0; } .container hr.medium { margin: 24px 0; } .container hr.small { margin: 12px 0; } .container h3.sub { color: #999; margin-bottom: 32px; } h1.title { margin: 16px 0; } .top-bar .name h1 { font-size: 0.94444rem; } .button { transition: none; } h1, h2, h3, h4, h5 { font-weight: 300; } .row { max-width: 80rem; } .chart { margin-top: 42px; } .overview { margin-bottom: 42px; padding: 0 18px; } .features { margin: 42px auto 30px; } .side-bar { } .side-bar h5 { font-weight: bold; } .margin-medium-v { margin-top: 24px; margin-bottom: 24px; } .margin-medium-top { margin-top: 24px !important; } .margin-small-h { margin-left: 12px; margin-right: 12px; } .margin-small-bottom { margin-bottom: 12px !important; } .c3-editor { font-size: 1.1em !important; } .sourcecode { margin-bottom: 1.25rem; } .sourcecode h3 { border-bottom: 1px solid #ddd; padding-bottom: 4px; } .sourcecode pre { border: none; border-radius: 0; padding: 12px 0px; font-size: 1.2em; line-height: 1.428571429; word-break: break-all; word-wrap: break-word; } .sourcecode pre code { background-color: #fdfdfd; padding: 0; font-family: Monaco,Menlo,Consolas,"Courier New",monospace; font-weight: normal; font-size: 0.9em; } .highlight { border-left: 4px solid #ccc; padding: 0 8px; } .highlight pre { padding: 0 8px; line-height: 1.2em; } .section .row { margin-bottom: 32px; } .section > a > h2 { line-height: 1.2em; margin-top: 18px; margin-bottom: 18px; } span.code { display: inline-block; background-color: #eee; padding-left: 4px; padding-right: 4px; font-family: Monaco,Menlo,Consolas,"Courier New",monospace; font-size: 0.9em; } h3 + ul, p + ul { margin-left: 32px; } .gray { color: #999; } li .label { top: -3px; padding: 0.22222rem 0.44444rem 0.33333rem; font-size: 0.61rem; margin: 0 8px; } div.sub-section { padding-top: 4px; padding-left: 10px; } ================================================ FILE: docs/css/tomorrow.css ================================================ /* http://jmblog.github.com/color-themes-for-google-code-highlightjs */ .tomorrow-comment, pre .comment, pre .title { color: #8e908c; } .tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo { /* color: #c82829;*/ } .tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant { color: #f5871f; } .tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute { color: #eab700; } .tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata { color: #718c00; } .tomorrow-aqua, pre .css .hexcolor { color: #3e999f; } .tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title { color: #4271ae; } .tomorrow-purple, pre .keyword, pre .javascript .function { color: #8959a8; } pre code { display: block; background: white; color: #4d4d4c; padding: 0.5em; } pre .coffeescript .javascript, pre .javascript .xml, pre .tex .formula, pre .xml .javascript, pre .xml .vbscript, pre .xml .css, pre .xml .cdata { opacity: 0.5; } ================================================ FILE: docs/data/c3_string_x.csv ================================================ x,download,loading www.siteX.com,100,400 www.siteY.com,200,100 www.siteZ.com,300,200 ================================================ FILE: docs/data/c3_test.csv ================================================ data1,data2,data3 120,80,200 140,50,210 170,100,250 150,70,300 180,120,280 ================================================ FILE: docs/data/c3_test.json ================================================ { "data1": [220, 240, 270, 250, 280], "data2": [180, 150, 300, 70, 120], "data3": [200, 310, 150, 100, 180] } ================================================ FILE: docs/data/c3_test2.csv ================================================ data1,data2,data3 20,180,400 40,150,310 70,120,470 50,170,400 80,200,380 ================================================ FILE: docs/examples.html.haml ================================================ .container .section = partial :index_item_title, locals: { id: 'chart', name: 'Chart' } %div .row = partial :index_item, locals: { id: 'simple_multiple' } = partial :index_item, locals: { id: 'timeseries' } = partial :index_item, locals: { id: 'chart_spline' } .row = partial :index_item, locals: { id: 'simple_xy' } = partial :index_item, locals: { id: 'simple_xy_multiple' } = partial :index_item, locals: { id: 'simple_regions' } .row = partial :index_item, locals: { id: 'chart_step' } = partial :index_item, locals: { id: 'chart_area' } = partial :index_item, locals: { id: 'chart_area_stacked' } .row = partial :index_item, locals: { id: 'chart_bar' } = partial :index_item, locals: { id: 'chart_bar_stacked' } = partial :index_item, locals: { id: 'chart_scatter' } .row = partial :index_item, locals: { id: 'chart_pie' } = partial :index_item, locals: { id: 'chart_donut' } = partial :index_item, locals: { id: 'chart_gauge' } .row = partial :index_item, locals: { id: 'chart_stanford' } = partial :index_item, locals: { id: 'chart_combination' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'axis', name: 'Axis' } %div .row = partial :index_item, locals: { id: 'categorized' } = partial :index_item, locals: { id: 'axes_rotated' } = partial :index_item, locals: { id: 'axes_y2' } .row = partial :index_item, locals: { id: 'axes_x_tick_format' } = partial :index_item, locals: { id: 'axes_x_tick_count' } = partial :index_item, locals: { id: 'axes_x_tick_values' } .row = partial :index_item, locals: { id: 'axes_x_tick_culling' } = partial :index_item, locals: { id: 'axes_x_tick_fit' } = partial :index_item, locals: { id: 'axes_x_localtime' } .row = partial :index_item, locals: { id: 'axes_x_tick_rotate' } = partial :index_item, locals: { id: 'axes_y_tick_format' } = partial :index_item, locals: { id: 'axes_y_padding' } .row = partial :index_item, locals: { id: 'axes_y_range' } = partial :index_item, locals: { id: 'axes_label' } = partial :index_item, locals: { id: 'axes_label_position' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'data', name: 'Data' } %div .row = partial :index_item, locals: { id: 'data_columned' } = partial :index_item, locals: { id: 'data_rowed' } = partial :index_item, locals: { id: 'data_json' } .row = partial :index_item, locals: { id: 'data_url' } = partial :index_item, locals: { id: 'data_stringx' } = partial :index_item, locals: { id: 'data_load' } .row = partial :index_item, locals: { id: 'data_name' } = partial :index_item, locals: { id: 'data_color' } = partial :index_item, locals: { id: 'data_order' } .row = partial :index_item, locals: { id: 'data_label' } = partial :index_item, locals: { id: 'data_label_format' } = partial :index_item, locals: { id: 'data_number_format_l10n' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'grid', name: 'Grid' } %div .row = partial :index_item, locals: { id: 'options_gridline' } = partial :index_item, locals: { id: 'grid_x_lines' } = partial :index_item, locals: { id: 'grid_y_lines' } %hr.medium .section = partial :index_item_title, locals: { id: 'region', name: 'Region' } %div .row = partial :index_item, locals: { id: 'region' } = partial :index_item, locals: { id: 'region_timeseries' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'interaction', name: 'Interaction' } %div .row = partial :index_item, locals: { id: 'options_subchart' } = partial :index_item, locals: { id: 'interaction_zoom' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'legend', name: 'Legend' } %div .row = partial :index_item, locals: { id: 'options_legend' } = partial :index_item, locals: { id: 'legend_position' } = partial :index_item, locals: { id: 'legend_custom' } %hr.medium .section = partial :index_item_title, locals: { id: 'tooltip', name: 'Tooltip' } %div .row = partial :index_item, locals: { id: 'tooltip_show' } = partial :index_item, locals: { id: 'tooltip_grouped' } = partial :index_item, locals: { id: 'tooltip_format' } %hr.medium .section = partial :index_item_title, locals: { id: 'chart_options', name: 'Chart Options' } %div .row = partial :index_item, locals: { id: 'options_size' } = partial :index_item, locals: { id: 'options_padding' } = partial :index_item, locals: { id: 'options_color' } .row = partial :index_item, locals: { id: 'transition_duration' } .large-4.columns .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'line_chart_options', name: 'Line Chart Options' } %div .row = partial :index_item, locals: { id: 'point_show' } .large-4.columns .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'pie_chart_options', name: 'Pie Chart Options' } %div .row = partial :index_item, locals: { id: 'pie_label_format' } .large-4.columns .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'api', name: 'API' } %div .row = partial :index_item, locals: { id: 'api_flow' } = partial :index_item, locals: { id: 'api_data_name' } = partial :index_item, locals: { id: 'api_data_color' } .row = partial :index_item, locals: { id: 'api_axis_label' } = partial :index_item, locals: { id: 'api_axis_range' } = partial :index_item, locals: { id: 'api_resize' } .row = partial :index_item, locals: { id: 'api_grid_x' } .large-4.columns .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'transform', name: 'Transform' } %div .row = partial :index_item, locals: { id: 'transform_line' } = partial :index_item, locals: { id: 'transform_spline' } = partial :index_item, locals: { id: 'transform_bar' } .row = partial :index_item, locals: { id: 'transform_area' } = partial :index_item, locals: { id: 'transform_areaspline' } = partial :index_item, locals: { id: 'transform_scatter' } .row = partial :index_item, locals: { id: 'transform_pie' } = partial :index_item, locals: { id: 'transform_donut' } .large-4.columns %hr.medium .section = partial :index_item_title, locals: { id: 'style', name: 'Style' } %div .row = partial :index_item, locals: { id: 'style_region' } = partial :index_item, locals: { id: 'style_grid' } .large-4.columns = partial :footer = partial :script ================================================ FILE: docs/gettingstarted.html.haml ================================================ .container.sidemenu .row .large-3.medium-4.columns.column-menu .side-bar %ul.side-nav %li GETTING STARTED %li %a( href="#setup" ) 1. Setup %li %a( href="#generate" ) 2. Generate chart %li %a( href="#customize" ) 3. Customize chart %li %a( href="#api" ) 4. Use APIs %li %a( href="#style" ) 5. Customize style %li %a( href="#more" ) 6. And more.. .large-9.medium-8.columns.column-content %section %h2 Getting Started %p In this guide, we are going to show you how to get started with C3. %hr %section %h3 %a( href="#setup" ) 1. Setup %p Download the latest version here: %ul %li https://github.com/c3js/c3/releases/latest %p Installing by Bower/Component is also available with the name c3. %p Then, load the scripts and css: .sourcecode.highlight %pre %code.html.xml :preserve <!-- Load c3.css --> <link href="/path/to/c3.css" rel="stylesheet"> <!-- Load d3.js and c3.js --> <script src="/path/to/d3.v5.min.js" charset="utf-8"></script> <script src="/path/to/c3.min.js"></script> %p C3 depends on D3, so please load D3 too. %hr %section %h3 %a( href="#generate" ) 2. Generate Chart %p C3 generates a chart by calling generate() with the argument object, and an element including the chart will insert into the element specified as a selector in that argument as bindto. %p Prepare the element to bind the chart: .sourcecode.highlight %pre %code.html.xml :preserve <div id="chart"></div> %p And, call generate() with arguments: .sourcecode.highlight %pre %code.javascript :preserve var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } }); %p C3 supports the asynchronous module definition (AMD) API. If you use RequireJS, you can load like this: .sourcecode.highlight %pre %code.javascript :preserve require.config({ baseUrl: '/js', paths: { d3: "http://d3js.org/d3.v5.min" } }); require(["d3", "c3"], function(d3, c3) { c3.generate({ ... }); }); %p Then, you will see the chart: #chart2_1 %br %p Data can be loaded as columned data / rowed data / csv in URL. %p There are serveral options to customize the chart and you can see those here: %ul %li Examples %hr %section %h3 %a( href="#customize" ) 3. Customize Chart %p The chart can be customize by giving some options when generating. We will introduce some of them here. %h4 1. Additional Axis %p Introduce additional axis for data2. Add data.axes and axis.y2.show as follows: .sourcecode.highlight %pre %code.javascript :preserve var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' // ADD } }, axis: { y2: { show: true // ADD } } }); %p Then, the chart will be like this: #chart3_1 %br %h4 2. Show Axis Label %p Show labels for each axis. Add axis.y.label and axis.y2.label as follows: .sourcecode.highlight %pre %code.javascript :preserve var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' } }, axis: { y: { label: { // ADD text: 'Y Label', position: 'outer-middle' } }, y2: { show: true, label: { // ADD text: 'Y2 Label', position: 'outer-middle' } } } }); %p Then, the chart will be like this: #chart3_2 %br %h4 3. Change Chart Type %p Show data2 as Bar chart. Add data.types as follows: .sourcecode.highlight %pre %code.javascript :preserve var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' }, types: { data2: 'bar' // ADD } }, axis: { y: { label: { text: 'Y Label', position: 'outer-middle' } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' } } } }); %p Then, the chart will be like this: #chart3_3 %br %h4 4. Format values %p Format the values of each data. Add axis.y.tick.format as follows: .sourcecode.highlight %pre %code.javascript :preserve var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' }, types: { data2: 'bar' } }, axis: { y: { label: { text: 'Y Label', position: 'outer-middle' }, tick: { format: d3.format("$,") // ADD } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' } } } }); %p Then, the chart will be like this: #chart3_4 %br %p More information about the options, please see Examples. (We'll add the reference soon) %hr %section %h3 %a( href="#api" ) 4. Use APIs %p By using APIs, you can update the chart after it's been rendered. We will introduce some of APIs here. APIs can be called through the object returned from generate(). %h4 1. Load Data %p By using load() API, you can load data and update the chart dynamically as follows: .sourcecode.highlight %pre %code.javascript :preserve // var chart = c3.generate({ ... }); chart.load({ columns: [ ['data1', 300, 100, 250, 150, 300, 150, 500], ['data2', 100, 200, 150, 50, 100, 250] ] }); %p If you push the button "Load" below, this code will run and the chart will be updated. %button.small( onclick="example4_1();" ) Load #chart4_1 %br %h4 2. Unload Data %p By using unload() API, you can unload the data dynamically as follows: .sourcecode.highlight %pre %code.javascript :preserve // var chart = c3.generate({ ... }); chart.unload({ ids: ['data2', 'data3'] }); %p If you push the button "Unload" below, this code will run and the chart will be updated. %button.small( onclick="example4_2();" ) Unload #chart4_2 %br %p Please use unload param in load() API if load and unload need to run simultaneously. Please see this example. %h4 3. Show/Hide Data %p By using show() and hide() API, you can show/hide the data dynamically as follows: .sourcecode.highlight %pre %code.javascript :preserve // var chart = c3.generate({ ... }); chart.hide(['data2', 'data3']); chart.show(['data2', 'data3']); %p If you push the button "Show" and "Hide" below, this code will run and the chart will be updated. %button.small( onclick="example4_3_2();" ) Hide %button.small( onclick="example4_3_1();" ) Show #chart4_3 %br %p The documentation about APIs is poor now, so please check the issues on github. There might be some hints about what you want to do. (We will add the document soon) %hr %section %h3 %a( href="#style" ) 5. Customize Style %p C3 give some classes for each element when generating. So, you can change the style of the elements by using those classes. %h4 1. Line style %p The lines have c3-line-[id] class, so this class can be used to define the style in css as follows: .sourcecode.highlight %pre %code.css :preserve #chart .c3-line-data2 { stroke-width: 5px; } #chart5_1 %br %p Please check the class for each element if you want to change the style. Web Inspector would be useful. (We will add the document for class definition soon) %hr %section %h3 %a( href="#more" ) 6. And More.. %p Please check the examples and the issues on github for more information. Sorry for the poor documentation. We're working on now and please give me some time. Thank you. = partial :footer = partial :script = partial :script_scroll = javascript_include_tag 'gettingstarted.js' ================================================ FILE: docs/img/.gitignore ================================================ ================================================ FILE: docs/index.html.haml ================================================ .container %h1.title C3.js %small D3-based reusable chart library .chart #message %a.button.small( onclick="startDemo();") Start Demo #chart %hr.large %h2.text-center Why C3? .row.text-center.features .medium-4.large-4.columns.supporticons %h3 Comfortable %p C3 makes it easy to generate D3-based charts by wrapping the code required to construct the entire chart. We don't need to write D3 code any more. .medium-4.large-4.columns.supporticons %h3 Customizable %p C3 gives some classes to each element when generating, so you can define a custom style by the class and it's possible to extend the structure directly by D3. .medium-4.large-4.columns.supporticons %h3 Controllable %p C3 provides a variety of APIs and callbacks to access the state of the chart. By using them, you can update the chart even after it's rendered. %h3.text-center.sub C3 enables deeper integration of charts into your application. %br .text-center %a.button( href="/gettingstarted.html" ) Getting Started %hr %h3 Change Log %ul %li v0.7.20 - 2020-08-08 %ul %li Bug fixes. %li v0.7.18 - 2020-06-17 %ul %li Bug fixes. %li v0.7.17 - 2020-06-15 %ul %li Bug fixes. %li v0.7.16 - 2020-06-15 %ul %li Bug fixes. %li v0.7.15 - 2020-02-28 %ul %li Bug fixes. %li v0.7.14 - 2020-02-24 %ul %li Bug fixes. %li v0.7.12 - 2019-10-04 %ul %li Add rollup support. %li Improved subchart handling. %li v0.7.11 - 2019-10-04 %ul %li Bug fixes. %li v0.7.10 - 2019-10-02 %ul %li Bug fixes. %li v0.7.9 - 2019-09-23 %ul %li Experimental support for y/y2 log scale. %li Restore tooltip behavior when grouped. %li Add Region Labels. %li v0.7.8 - 2019-08-25 %ul %li Fix scatter appearance. %li Points in charts can be styled by css. %li A fix for Firefox. %li v0.7.7 - 2019-08-25 %ul %li Fix a bug. %li v0.7.6 - 2019-08-20 %ul %li A bug fix. %li v0.7.5 - 2019-08-19 %ul %li Add normalized stack chart. %li Bug fixes. %li v0.7.4 - 2019-08-09 %ul %li Add padAngle option. %li A bug fix. %li v0.7.3 - 2019-07-29 %ul %li Bug fixes. %li v0.7.2 - 2019-07-06 %ul %li Add stanford.scaleValues option. %li v0.7.1 - 2019-05-29 %ul %li Add an option for tooltip behavior. %li v0.7.0 - 2019-04-10 %ul %li Add stanford diagram. %li v0.6.14 - 2019-03-27 %ul %li Fixed a memory leak. %li Fixed .selected() API. %li v0.6.13 - 2019-03-04 %ul %li Reverted resizing change in 0.6.11. %li v0.6.12 - 2018-12-28 %ul %li Added index to tooltip’s title format callback. %li v0.6.11 - 2018-12-08 %ul %li Fix a bug of resizing. %li v0.6.10 - 2018-12-04 %ul %li Fix a bug of arc chart. %li v0.6.9 - 2018-11-22 %ul %li Fix a bug. %li v0.6.8 - 2018-10-01 %ul %li Add src/* npm package. %li v0.6.7 - 2018-08-08 %ul %li Add the zoom type 'drag'. %li v0.6.6 - 2018-07-24 %ul %li Fix bug of gauge chart. %li v0.6.5 - 2018-07-14 %ul %li Fix bug of hidden legend option. %li v0.6.4 - 2018-07-12 %ul %li Fix a bug of mouse events. %li v0.6.3 - 2018-07-06 %ul %li Fix a bug of gauge. %li v0.6.2 - 2018-06-08 %ul %li Add step-before and step-after for line.step.type option. %li v0.4.23 - 2018-05-18 %ul %li Add axis.x.tick.multilineMax option (0.4 branch). %li v0.6.1 - 2018-05-17 %ul %li Add axis.x.tick.multilineMax option. %li v0.6.0 - 2018-05-14 %ul %li Update D3 dependency to v5. %li v0.5.4 - 2018-04-23 %ul %li Bug fixes. %li v0.5.3 - 2018-04-12 %ul %li Bug fixes. %li v0.5.2 - 2018-03-31 %ul %li Bug fixes. %li v0.5.1 - 2018-03-25 %ul %li Bug fixes. %li v0.5.0 - 2018-03-24 %ul %li Update D3 dependency to version 4. %li v0.4.22 - 2018-03-21 %ul %li Add axis.x.inner option. %li v0.4.21 - 2018-02-15 %ul %li Multi arc gauge chart. %li v0.4.20 - 2018-02-11 %ul %li Fix gauge chart with fullCircle option. %li v0.4.19 - 2018-02-10 %ul %li Do not call resize functions when chart is hidden. %li Switched CI environment. %li Have license in minified bundle. %li Fixed a memory leak. %li v0.4.18 - 2017-09-14 %ul %li point.focus.expand.r takes a function. %li Pie and donuts really handle data.order correctly. %li v0.4.17 - 2017-08-19 %ul %li Added bar.space option. %li v0.4.16 - 2017-08-16 %ul %li Bug fix of bar chart. %li v0.4.15 - 2017-07-20 %ul %li Move some style handling to css. %li v0.4.14 - 2017-06-24 %ul %li Bug fix. %li v0.4.13 - 2017-06-11 %ul %li New option and bug fixes. %li v0.4.12 - 2017-05-28 %ul %li Performance improvement and bug fixes. %li v0.4.11 - 2016-05-01 %ul %li New features, performance improvement and bug fixes. %li v0.4.10 - 2015-03-17 %ul %li New features, performance improvement and bug fixes. %li v0.4.9 - 2015-01-18 %ul %li New features, performance improvement and bug fixes. %li v0.4.8 - 2014-12-14 %ul %li Bug fixes. %li v0.4.7 - 2014-12-02 %ul %li Bug fixes. %li v0.4.6 - 2014-12-01 %ul %li Bug fixes. %li v0.4.5 - 2014-11-30 %ul %li Bug fixes. %li Performance improvement. %li v0.4.4 - 2014-11-21 %ul %li New features (e.g. axis.y/y2.inner, legend.hide options) %li Bug fixes. %li Performance improvement. %li v0.4.3 - 2014-11-18 %ul %li Bug fixes. %li v0.4.2 - 2014-11-17 %ul %li Bug fixes. %li v0.4.1 - 2014-11-16 %ul %li Fixed class suffix. %li v0.4.0 - 2014-11-15 %ul %li A lot of bug fixes and new features %li Introduce unit test (it's not enough though) %li and more... %li v0.3.0 - 2014-08-22 %ul %li Introduced new architecture so that we can extend this library more easily. %li Modified some options such as data.xFormat and data.xLocaltime. %li Splitted source file into small ones. %li v0.2.5 - 2014-08-09 %ul %li Modified to use data.onclick, data.onmouseover and data.onmouseout on pie/donut/gauge instead of its callbacks %li Modified unload interface %li Bug fixes %li and more... %li v0.2.4 - 2014-06-16 %ul %li Added new options such as pie.sort, donut.sort, bar.zerobased, area.zerobased %li Added new API such as category, categories %li Bug fixes %li v0.2.3 - 2014-06-04 %ul %li Renamed callbacks (onend/onenter/onleave) %li Added extension for zoom with reduction (by @danelkhen Thank you!) %li Bug fixes %li v0.2.2 - 2014-06-03 %ul %li Supported ungrouped tooltip %li Added zoom.enable API %li Bug fixes %li v0.2.1 - 2014-06-02 %ul %li Bug fixes %li v0.2.0 - 2014-05-30 %ul %li Supported step line/area, stacked line/area, stacked line/area, gauge chart %li Supported JSON as input %li Added flow API %li etc %li v0.1.42 - 2014-05-18 %h3 Browser Support %p Because of the dependence on D3, C3 supports only modern browsers D3 supports. Please see the description in D3. %p Note: For IE9 and IE10, polyfill is required because c3 uses MutationObserver, which is not supported in those versions. However, it's not required if charts always will be binded to the DOM specified by bindto because MutationObserver is not called in that case. %h3 Dependency %ul %li D3.js ^4.12.0 %p Note: If you need to use D3 v3.x, please use C3 v0.4.22, which is compatible with D3 v3.x. %h3 License %p MIT = partial :footer = partial :script = javascript_include_tag 'index' ================================================ FILE: docs/js/ace/ace.js ================================================ (function(){function s(r){var i=function(e,t){return n("",e,t)},s=e;r&&(e[r]||(e[r]={}),s=e[r]);if(!s.define||!s.define.packaged)t.original=s.define,s.define=t,s.define.packaged=!0;if(!s.require||!s.require.packaged)n.original=s.require,s.require=i,s.require.packaged=!0}var ACE_NAMESPACE = "ace",e=function(){return this}();if(!ACE_NAMESPACE&&typeof requirejs!="undefined")return;var t=function(e,n,r){if(typeof e!="string"){t.original?t.original.apply(window,arguments):(console.error("dropping module because define wasn't a string."),console.trace());return}arguments.length==2&&(r=n),t.modules||(t.modules={},t.payloads={}),t.payloads[e]=r,t.modules[e]=null},n=function(e,t,r){if(Object.prototype.toString.call(t)==="[object Array]"){var s=[];for(var o=0,u=t.length;o1&&u(t,"")>-1&&(a=RegExp(this.source,r.replace.call(o(this),"g","")),r.replace.call(e.slice(t.index),a,function(){for(var e=1;et.index&&this.lastIndex--}return t},s||(RegExp.prototype.test=function(e){var t=r.exec.call(this,e);return t&&this.global&&!t[0].length&&this.lastIndex>t.index&&this.lastIndex--,!!t})}),ace.define("ace/lib/es5-shim",["require","exports","module"],function(e,t,n){function r(){}function w(e){try{return Object.defineProperty(e,"sentinel",{}),"sentinel"in e}catch(t){}}function H(e){return e=+e,e!==e?e=0:e!==0&&e!==1/0&&e!==-1/0&&(e=(e>0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n=0?parseFloat((i.match(/(?:MSIE |Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]):parseFloat((i.match(/(?:Trident\/[0-9]+[\.0-9]+;.*rv:)([0-9]+[\.0-9]+)/)||[])[1]),t.isOldIE=t.isIE&&t.isIE<9,t.isGecko=t.isMozilla=(window.Controllers||window.controllers)&&window.navigator.product==="Gecko",t.isOldGecko=t.isGecko&&parseInt((i.match(/rv\:(\d+)/)||[])[1],10)<4,t.isOpera=window.opera&&Object.prototype.toString.call(window.opera)=="[object Opera]",t.isWebKit=parseFloat(i.split("WebKit/")[1])||undefined,t.isChrome=parseFloat(i.split(" Chrome/")[1])||undefined,t.isAIR=i.indexOf("AdobeAIR")>=0,t.isIPad=i.indexOf("iPad")>=0,t.isTouchPad=i.indexOf("TouchPad")>=0,t.isChromeOS=i.indexOf(" CrOS ")>=0}),ace.define("ace/lib/event",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t,n){var o=s(t);if(!i.isMac&&u){if(u[91]||u[92])o|=8;if(u.altGr){if((3&o)==3)return;u.altGr=0}if(n===18||n===17){var f="location"in t?t.location:t.keyLocation;if(n===17&&f===1)a=t.timeStamp;else if(n===18&&o===3&&f===2){var l=-a;a=t.timeStamp,l+=a,l<3&&(u.altGr=!0)}}}if(n in r.MODIFIER_KEYS){switch(r.MODIFIER_KEYS[n]){case"Alt":o=2;break;case"Shift":o=4;break;case"Ctrl":o=1;break;default:o=8}n=-1}o&8&&(n===91||n===93)&&(n=-1);if(!o&&n===13){var f="location"in t?t.location:t.keyLocation;if(f===3){e(t,o,-n);if(t.defaultPrevented)return}}if(i.isChromeOS&&o&8){e(t,o,n);if(t.defaultPrevented)return;o&=-9}return!!o||n in r.FUNCTION_KEYS||n in r.PRINTABLE_KEYS?e(t,o,n):!1}var r=e("./keys"),i=e("./useragent");t.addListener=function(e,t,n){if(e.addEventListener)return e.addEventListener(t,n,!1);if(e.attachEvent){var r=function(){n.call(e,window.event)};n._wrapper=r,e.attachEvent("on"+t,r)}},t.removeListener=function(e,t,n){if(e.removeEventListener)return e.removeEventListener(t,n,!1);e.detachEvent&&e.detachEvent("on"+t,n._wrapper||n)},t.stopEvent=function(e){return t.stopPropagation(e),t.preventDefault(e),!1},t.stopPropagation=function(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0},t.preventDefault=function(e){e.preventDefault?e.preventDefault():e.returnValue=!1},t.getButton=function(e){return e.type=="dblclick"?0:e.type=="contextmenu"||i.isMac&&e.ctrlKey&&!e.altKey&&!e.shiftKey?2:e.preventDefault?e.button:{1:0,2:2,4:1}[e.button]},t.capture=function(e,n,r){function i(e){n&&n(e),r&&r(e),t.removeListener(document,"mousemove",n,!0),t.removeListener(document,"mouseup",i,!0),t.removeListener(document,"dragstart",i,!0)}return t.addListener(document,"mousemove",n,!0),t.addListener(document,"mouseup",i,!0),t.addListener(document,"dragstart",i,!0),i},t.addMouseWheelListener=function(e,n){"onmousewheel"in e?t.addListener(e,"mousewheel",function(e){var t=8;e.wheelDeltaX!==undefined?(e.wheelX=-e.wheelDeltaX/t,e.wheelY=-e.wheelDeltaY/t):(e.wheelX=0,e.wheelY=-e.wheelDelta/t),n(e)}):"onwheel"in e?t.addListener(e,"wheel",function(e){var t=.35;switch(e.deltaMode){case e.DOM_DELTA_PIXEL:e.wheelX=e.deltaX*t||0,e.wheelY=e.deltaY*t||0;break;case e.DOM_DELTA_LINE:case e.DOM_DELTA_PAGE:e.wheelX=(e.deltaX||0)*5,e.wheelY=(e.deltaY||0)*5}n(e)}):t.addListener(e,"DOMMouseScroll",function(e){e.axis&&e.axis==e.HORIZONTAL_AXIS?(e.wheelX=(e.detail||0)*5,e.wheelY=0):(e.wheelX=0,e.wheelY=(e.detail||0)*5),n(e)})},t.addMultiMouseDownListener=function(e,n,r,s){var o=0,u,a,f,l={2:"dblclick",3:"tripleclick",4:"quadclick"};t.addListener(e,"mousedown",function(e){t.getButton(e)!==0?o=0:e.detail>1?(o++,o>4&&(o=1)):o=1;if(i.isIE){var c=Math.abs(e.clientX-u)>5||Math.abs(e.clientY-a)>5;if(!f||c)o=1;f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),o==1&&(u=e.clientX,a=e.clientY)}e._clicks=o,r[s]("mousedown",e);if(o>4)o=0;else if(o>1)return r[s](l[o],e)}),i.isOldIE&&t.addListener(e,"dblclick",function(e){o=2,f&&clearTimeout(f),f=setTimeout(function(){f=null},n[o-1]||600),r[s]("mousedown",e),r[s](l[o],e)})};var s=!i.isMac||!i.isOpera||"KeyboardEvent"in window?function(e){return 0|(e.ctrlKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.metaKey?8:0)}:function(e){return 0|(e.metaKey?1:0)|(e.altKey?2:0)|(e.shiftKey?4:0)|(e.ctrlKey?8:0)};t.getModifierString=function(e){return r.KEY_MODS[s(e)]};var u=null,a=0;t.addCommandKeyListener=function(e,n){var r=t.addListener;if(i.isOldGecko||i.isOpera&&!("KeyboardEvent"in window)){var s=null;r(e,"keydown",function(e){s=e.keyCode}),r(e,"keypress",function(e){return o(n,e,s)})}else{var a=null;r(e,"keydown",function(e){u[e.keyCode]=!0;var t=o(n,e,e.keyCode);return a=e.defaultPrevented,t}),r(e,"keypress",function(e){a&&(e.ctrlKey||e.altKey||e.shiftKey||e.metaKey)&&(t.stopEvent(e),a=null)}),r(e,"keyup",function(e){u[e.keyCode]=null}),u||(u=Object.create(null),r(window,"focus",function(e){u=Object.create(null)}))}};if(window.postMessage&&!i.isOldIE){var f=1;t.nextTick=function(e,n){n=n||window;var r="zero-timeout-message-"+f;t.addListener(n,"message",function i(s){s.data==r&&(t.stopPropagation(s),t.removeListener(n,"message",i),e())}),n.postMessage(r,"*")}}t.nextFrame=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||window.oRequestAnimationFrame,t.nextFrame?t.nextFrame=t.nextFrame.bind(window):t.nextFrame=function(e){setTimeout(e,17)}}),ace.define("ace/lib/lang",["require","exports","module"],function(e,t,n){"use strict";t.last=function(e){return e[e.length-1]},t.stringReverse=function(e){return e.split("").reverse().join("")},t.stringRepeat=function(e,t){var n="";while(t>0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n1),e.preventDefault()},this.startSelect=function(e,t){e=e||this.editor.renderer.screenToTextCoordinates(this.x,this.y);var n=this.editor;this.mousedownEvent.getShiftKey()?n.selection.selectToPosition(e):t||n.selection.moveToPosition(e),t||this.select(),n.renderer.scroller.setCapture&&n.renderer.scroller.setCapture(),n.setStyle("ace_selecting"),this.setState("select")},this.select=function(){var e,t=this.editor,n=t.renderer.screenToTextCoordinates(this.x,this.y);if(this.$clickSelection){var r=this.$clickSelection.comparePoint(n);if(r==-1)e=this.$clickSelection.end;else if(r==1)e=this.$clickSelection.start;else{var i=f(this.$clickSelection,n);n=i.cursor,e=i.anchor}t.selection.setSelectionAnchor(e.row,e.column)}t.selection.selectToPosition(n),t.renderer.scrollCursorIntoView()},this.extendSelectionBy=function(e){var t,n=this.editor,r=n.renderer.screenToTextCoordinates(this.x,this.y),i=n.selection[e](r.row,r.column);if(this.$clickSelection){var s=this.$clickSelection.comparePoint(i.start),o=this.$clickSelection.comparePoint(i.end);if(s==-1&&o<=0){t=this.$clickSelection.end;if(i.end.row!=r.row||i.end.column!=r.column)r=i.start}else if(o==1&&s>=0){t=this.$clickSelection.start;if(i.start.row!=r.row||i.start.column!=r.column)r=i.end}else if(s==-1&&o==1)r=i.end,t=i.start;else{var u=f(this.$clickSelection,r);r=u.cursor,t=u.anchor}n.selection.setSelectionAnchor(t.row,t.column)}n.selection.selectToPosition(r),n.renderer.scrollCursorIntoView()},this.selectEnd=this.selectAllEnd=this.selectByWordsEnd=this.selectByLinesEnd=function(){this.$clickSelection=null,this.editor.unsetStyle("ace_selecting"),this.editor.renderer.scroller.releaseCapture&&this.editor.renderer.scroller.releaseCapture()},this.focusWait=function(){var e=a(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y),t=Date.now();(e>o||t-this.mousedownEvent.time>this.$focusTimout)&&this.startSelect(this.mousedownEvent.getDocumentPosition())},this.onDoubleClick=function(e){var t=e.getDocumentPosition(),n=this.editor,r=n.session,i=r.getBracketRange(t);i?(i.isEmpty()&&(i.start.column--,i.end.column++),this.setState("select")):(i=n.selection.getWordRange(t.row,t.column),this.setState("selectByWords")),this.$clickSelection=i,this.select()},this.onTripleClick=function(e){var t=e.getDocumentPosition(),n=this.editor;this.setState("selectByLines");var r=n.getSelectionRange();r.isMultiLine()&&r.contains(t.row,t.column)?(this.$clickSelection=n.selection.getLineRange(r.start.row),this.$clickSelection.end=n.selection.getLineRange(r.end.row).end):this.$clickSelection=n.selection.getLineRange(t.row),this.select()},this.onQuadClick=function(e){var t=this.editor;t.selectAll(),this.$clickSelection=t.getSelectionRange(),this.setState("selectAll")},this.onMouseWheel=function(e){if(e.getAccelKey())return;e.getShiftKey()&&e.wheelY&&!e.wheelX&&(e.wheelX=e.wheelY,e.wheelY=0);var t=e.domEvent.timeStamp,n=t-(this.$lastScrollTime||0),r=this.editor,i=r.renderer.isScrollableBy(e.wheelX*e.speed,e.wheelY*e.speed);if(i||n<200)return this.$lastScrollTime=t,r.renderer.scrollBy(e.wheelX*e.speed,e.wheelY*e.speed),e.stop()}}).call(u.prototype),t.DefaultHandlers=u}),ace.define("ace/tooltip",["require","exports","module","ace/lib/oop","ace/lib/dom"],function(e,t,n){"use strict";function s(e){this.isOpen=!1,this.$element=null,this.$parentNode=e}var r=e("./lib/oop"),i=e("./lib/dom");(function(){this.$init=function(){return this.$element=i.createElement("div"),this.$element.className="ace_tooltip",this.$element.style.display="none",this.$parentNode.appendChild(this.$element),this.$element},this.getElement=function(){return this.$element||this.$init()},this.setText=function(e){i.setInnerText(this.getElement(),e)},this.setHtml=function(e){this.getElement().innerHTML=e},this.setPosition=function(e,t){this.getElement().style.left=e+"px",this.getElement().style.top=t+"px"},this.setClassName=function(e){i.addCssClass(this.getElement(),e)},this.show=function(e,t,n){e!=null&&this.setText(e),t!=null&&n!=null&&this.setPosition(t,n),this.isOpen||(this.getElement().style.display="block",this.isOpen=!0)},this.hide=function(){this.isOpen&&(this.getElement().style.display="none",this.isOpen=!1)},this.getHeight=function(){return this.getElement().offsetHeight},this.getWidth=function(){return this.getElement().offsetWidth}}).call(s.prototype),t.Tooltip=s}),ace.define("ace/mouse/default_gutter_handler",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/event","ace/tooltip"],function(e,t,n){"use strict";function u(e){function l(){var r=u.getDocumentPosition().row,s=n.$annotations[r];if(!s)return c();var o=t.session.getLength();if(r==o){var a=t.renderer.pixelToScreenCoordinates(0,u.y).row,l=u.$pos;if(a>t.session.documentToScreenRow(l.row,l.column))return c()}if(f==s)return;f=s.text.join("
"),i.setHtml(f),i.show(),t.on("mousewheel",c);if(e.$tooltipFollowsMouse)h(u);else{var p=n.$cells[t.session.documentToScreenRow(r,0)].element,d=p.getBoundingClientRect(),v=i.getElement().style;v.left=d.right+"px",v.top=d.bottom+"px"}}function c(){o&&(o=clearTimeout(o)),f&&(i.hide(),f=null,t.removeEventListener("mousewheel",c))}function h(e){i.setPosition(e.x,e.y)}var t=e.editor,n=t.renderer.$gutterLayer,i=new a(t.container);e.editor.setDefaultHandler("guttermousedown",function(r){if(!t.isFocused()||r.getButton()!=0)return;var i=n.getRegion(r);if(i=="foldWidgets")return;var s=r.getDocumentPosition().row,o=t.session.selection;if(r.getShiftKey())o.selectTo(s,0);else{if(r.domEvent.detail==2)return t.selectAll(),r.preventDefault();e.$clickSelection=t.selection.getLineRange(s)}return e.setState("selectByLines"),e.captureMouse(r),r.preventDefault()});var o,u,f;e.editor.setDefaultHandler("guttermousemove",function(t){var n=t.domEvent.target||t.domEvent.srcElement;if(r.hasCssClass(n,"ace_fold-widget"))return c();f&&e.$tooltipFollowsMouse&&h(t),u=t;if(o)return;o=setTimeout(function(){o=null,u&&!e.isMousePressed?l():c()},50)}),s.addListener(t.renderer.$gutter,"mouseout",function(e){u=null;if(!f||o)return;o=setTimeout(function(){o=null,c()},50)}),t.on("changeSession",c)}function a(e){o.call(this,e)}var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/event"),o=e("../tooltip").Tooltip;i.inherits(a,o),function(){this.setPosition=function(e,t){var n=window.innerWidth||document.documentElement.clientWidth,r=window.innerHeight||document.documentElement.clientHeight,i=this.getWidth(),s=this.getHeight();e+=15,t+=15,e+i>n&&(e-=e+i-n),t+s>r&&(t-=20+s),o.prototype.setPosition.call(this,e,t)}}.call(a.prototype),t.GutterHandler=u}),ace.define("ace/mouse/mouse_event",["require","exports","module","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=t.MouseEvent=function(e,t){this.domEvent=e,this.editor=t,this.x=this.clientX=e.clientX,this.y=this.clientY=e.clientY,this.$pos=null,this.$inSelection=null,this.propagationStopped=!1,this.defaultPrevented=!1};(function(){this.stopPropagation=function(){r.stopPropagation(this.domEvent),this.propagationStopped=!0},this.preventDefault=function(){r.preventDefault(this.domEvent),this.defaultPrevented=!0},this.stop=function(){this.stopPropagation(),this.preventDefault()},this.getDocumentPosition=function(){return this.$pos?this.$pos:(this.$pos=this.editor.renderer.screenToTextCoordinates(this.clientX,this.clientY),this.$pos)},this.inSelection=function(){if(this.$inSelection!==null)return this.$inSelection;var e=this.editor,t=e.getSelectionRange();if(t.isEmpty())this.$inSelection=!1;else{var n=this.getDocumentPosition();this.$inSelection=t.contains(n.row,n.column)}return this.$inSelection},this.getButton=function(){return r.getButton(this.domEvent)},this.getShiftKey=function(){return this.domEvent.shiftKey},this.getAccelKey=i.isMac?function(){return this.domEvent.metaKey}:function(){return this.domEvent.ctrlKey}}).call(s.prototype)}),ace.define("ace/mouse/dragdrop_handler",["require","exports","module","ace/lib/dom","ace/lib/event","ace/lib/useragent"],function(e,t,n){"use strict";function f(e){function T(e,n){var r=Date.now(),i=!n||e.row!=n.row,s=!n||e.column!=n.column;if(!S||i||s)t.$blockScrolling+=1,t.moveCursorToPosition(e),t.$blockScrolling-=1,S=r,x={x:p,y:d};else{var o=l(x.x,x.y,p,d);o>a?S=null:r-S>=u&&(t.renderer.scrollCursorIntoView(),S=null)}}function N(e,n){var r=Date.now(),i=t.renderer.layerConfig.lineHeight,s=t.renderer.layerConfig.characterWidth,u=t.renderer.scroller.getBoundingClientRect(),a={x:{left:p-u.left,right:u.right-p},y:{top:d-u.top,bottom:u.bottom-d}},f=Math.min(a.x.left,a.x.right),l=Math.min(a.y.top,a.y.bottom),c={row:e.row,column:e.column};f/s<=2&&(c.column+=a.x.left=o&&t.renderer.scrollCursorIntoView(c):E=r:E=null}function C(){var e=g;g=t.renderer.screenToTextCoordinates(p,d),T(g,e),N(g,e)}function k(){m=t.selection.toOrientedRange(),h=t.session.addMarker(m,"ace_selection",t.getSelectionStyle()),t.clearSelection(),t.isFocused()&&t.renderer.$cursorLayer.setBlinking(!1),clearInterval(v),C(),v=setInterval(C,20),y=0,i.addListener(document,"mousemove",O)}function L(){clearInterval(v),t.session.removeMarker(h),h=null,t.$blockScrolling+=1,t.selection.fromOrientedRange(m),t.$blockScrolling-=1,t.isFocused()&&!w&&t.renderer.$cursorLayer.setBlinking(!t.getReadOnly()),m=null,g=null,y=0,E=null,S=null,i.removeListener(document,"mousemove",O)}function O(){A==null&&(A=setTimeout(function(){A!=null&&h&&L()},20))}function M(e){var t=e.types;return!t||Array.prototype.some.call(t,function(e){return e=="text/plain"||e=="Text"})}function _(e){var t=["copy","copymove","all","uninitialized"],n=["move","copymove","linkmove","all","uninitialized"],r=s.isMac?e.altKey:e.ctrlKey,i="uninitialized";try{i=e.dataTransfer.effectAllowed.toLowerCase()}catch(e){}var o="none";return r&&t.indexOf(i)>=0?o="copy":n.indexOf(i)>=0?o="move":t.indexOf(i)>=0&&(o="copy"),o}var t=e.editor,n=r.createElement("img");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",s.isOpera&&(n.style.cssText="width:1px;height:1px;position:fixed;top:0;left:0;z-index:2147483647;opacity:0;");var f=["dragWait","dragWaitEnd","startDrag","dragReadyEnd","onMouseDrag"];f.forEach(function(t){e[t]=this[t]},this),t.addEventListener("mousedown",this.onMouseDown.bind(e));var c=t.container,h,p,d,v,m,g,y=0,b,w,E,S,x;this.onDragStart=function(e){if(this.cancelDrag||!c.draggable){var r=this;return setTimeout(function(){r.startSelect(),r.captureMouse(e)},0),e.preventDefault()}m=t.getSelectionRange();var i=e.dataTransfer;i.effectAllowed=t.getReadOnly()?"copy":"copyMove",s.isOpera&&(t.container.appendChild(n),n.scrollTop=0),i.setDragImage&&i.setDragImage(n,0,0),s.isOpera&&t.container.removeChild(n),i.clearData(),i.setData("Text",t.session.getTextRange()),w=!0,this.setState("drag")},this.onDragEnd=function(e){c.draggable=!1,w=!1,this.setState(null);if(!t.getReadOnly()){var n=e.dataTransfer.dropEffect;!b&&n=="move"&&t.session.remove(t.getSelectionRange()),t.renderer.$cursorLayer.setBlinking(!0)}this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle("")},this.onDragEnter=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||k(),y++,e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragOver=function(e){if(t.getReadOnly()||!M(e.dataTransfer))return;return p=e.clientX,d=e.clientY,h||(k(),y++),A!==null&&(A=null),e.dataTransfer.dropEffect=b=_(e),i.preventDefault(e)},this.onDragLeave=function(e){y--;if(y<=0&&h)return L(),b=null,i.preventDefault(e)},this.onDrop=function(e){if(!g)return;var n=e.dataTransfer;if(w)switch(b){case"move":m.contains(g.row,g.column)?m={start:g,end:g}:m=t.moveText(m,g);break;case"copy":m=t.moveText(m,g,!0)}else{var r=n.getData("Text");m={start:g,end:t.session.insert(g,r)},t.focus(),b=null}return L(),i.preventDefault(e)},i.addListener(c,"dragstart",this.onDragStart.bind(e)),i.addListener(c,"dragend",this.onDragEnd.bind(e)),i.addListener(c,"dragenter",this.onDragEnter.bind(e)),i.addListener(c,"dragover",this.onDragOver.bind(e)),i.addListener(c,"dragleave",this.onDragLeave.bind(e)),i.addListener(c,"drop",this.onDrop.bind(e));var A=null}function l(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}var r=e("../lib/dom"),i=e("../lib/event"),s=e("../lib/useragent"),o=200,u=200,a=5;(function(){this.dragWait=function(){var e=Date.now()-this.mousedownEvent.time;e>this.editor.getDragDelay()&&this.startDrag()},this.dragWaitEnd=function(){var e=this.editor.container;e.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()),this.selectEnd()},this.dragReadyEnd=function(e){this.editor.renderer.$cursorLayer.setBlinking(!this.editor.getReadOnly()),this.editor.unsetStyle("ace_dragging"),this.editor.renderer.setCursorStyle(""),this.dragWaitEnd()},this.startDrag=function(){this.cancelDrag=!1;var e=this.editor,t=e.container;t.draggable=!0,e.renderer.$cursorLayer.setBlinking(!1),e.setStyle("ace_dragging");var n=s.isWin?"default":"move";e.renderer.setCursorStyle(n),this.setState("dragReady")},this.onMouseDrag=function(e){var t=this.editor.container;if(s.isIE&&this.state=="dragReady"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>3&&t.dragDrop()}if(this.state==="dragWait"){var n=l(this.mousedownEvent.x,this.mousedownEvent.y,this.x,this.y);n>0&&(t.draggable=!1,this.startSelect(this.mousedownEvent.getDocumentPosition()))}},this.onMouseDown=function(e){if(!this.$dragEnabled)return;this.mousedownEvent=e;var t=this.editor,n=e.inSelection(),r=e.getButton(),i=e.domEvent.detail||1;if(i===1&&r===0&&n){if(e.editor.inMultiSelectMode&&(e.getAccelKey()||e.getShiftKey()))return;this.mousedownEvent.time=Date.now();var o=e.domEvent.target||e.domEvent.srcElement;"unselectable"in o&&(o.unselectable="on");if(t.getDragDelay()){if(s.isWebKit){this.cancelDrag=!0;var u=t.container;u.draggable=!0}this.setState("dragWait")}else this.startDrag();this.captureMouse(e,this.onMouseDrag.bind(this)),e.defaultPrevented=!0}}}).call(f.prototype),t.DragdropHandler=f}),ace.define("ace/lib/net",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("./dom");t.get=function(e,t){var n=new XMLHttpRequest;n.open("GET",e,!0),n.onreadystatechange=function(){n.readyState===4&&t(n.responseText)},n.send(null)},t.loadScript=function(e,t){var n=r.getDocumentHead(),i=document.createElement("script");i.src=e,n.appendChild(i),i.onload=i.onreadystatechange=function(e,n){if(n||!i.readyState||i.readyState=="loaded"||i.readyState=="complete")i=i.onload=i.onreadystatechange=null,n||t()}},t.qualifyURL=function(e){var t=document.createElement("a");return t.href=e,t.href}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o1&&(i=n[n.length-2]);var o=a[t+"Path"];return o==null?o=a.basePath:r=="/"&&(t=r=""),o&&o.slice(-1)!="/"&&(o+="/"),o+t+r+i+this.get("suffix")},t.setModuleUrl=function(e,t){return a.$moduleUrls[e]=t},t.$loading={},t.loadModule=function(n,r){var i,o;Array.isArray(n)&&(o=n[0],n=n[1]);try{i=e(n)}catch(u){}if(i&&!t.$loading[n])return r&&r(i);t.$loading[n]||(t.$loading[n]=[]),t.$loading[n].push(r);if(t.$loading[n].length>1)return;var a=function(){e([n],function(e){t._emit("load.module",{name:n,module:e});var r=t.$loading[n];t.$loading[n]=null,r.forEach(function(t){t&&t(e)})})};if(!t.get("packaged"))return a();s.loadScript(t.moduleUrl(n,o),a)},t.init=f;var c={setOptions:function(e){Object.keys(e).forEach(function(t){this.setOption(t,e[t])},this)},getOptions:function(e){var t={};return e?Array.isArray(e)||(t=e,e=Object.keys(t)):e=Object.keys(this.$options),e.forEach(function(e){t[e]=this.getOption(e)},this),t},setOption:function(e,t){if(this["$"+e]===t)return;var n=this.$options[e];if(!n)return typeof console!="undefined"&&console.warn&&console.warn('misspelled option "'+e+'"'),undefined;if(n.forwardTo)return this[n.forwardTo]&&this[n.forwardTo].setOption(e,t);n.handlesSet||(this["$"+e]=t),n&&n.set&&n.set.call(this,t)},getOption:function(e){var t=this.$options[e];return t?t.forwardTo?this[t.forwardTo]&&this[t.forwardTo].getOption(e):t&&t.get?t.get.call(this):this["$"+e]:(typeof console!="undefined"&&console.warn&&console.warn('misspelled option "'+e+'"'),undefined)}},h={};t.defineOptions=function(e,t,n){return e.$options||(h[t]=e.$options={}),Object.keys(n).forEach(function(t){var r=n[t];typeof r=="string"&&(r={forwardTo:r}),r.name||(r.name=t),e.$options[r.name]=r,"initialValue"in r&&(e["$"+r.name]=r.initialValue)}),i.implement(e,c),this},t.resetOptions=function(e){Object.keys(e.$options).forEach(function(t){var n=e.$options[t];"value"in n&&e.setOption(t,n.value)})},t.setDefaultValue=function(e,n,r){var i=h[e]||(h[e]={});i[n]&&(i.forwardTo?t.setDefaultValue(i.forwardTo,n,r):i[n].value=r)},t.setDefaultValues=function(e,n){Object.keys(n).forEach(function(r){t.setDefaultValue(e,r,n[r])})}}),ace.define("ace/mouse/mouse_handler",["require","exports","module","ace/lib/event","ace/lib/useragent","ace/mouse/default_handlers","ace/mouse/default_gutter_handler","ace/mouse/mouse_event","ace/mouse/dragdrop_handler","ace/config"],function(e,t,n){"use strict";var r=e("../lib/event"),i=e("../lib/useragent"),s=e("./default_handlers").DefaultHandlers,o=e("./default_gutter_handler").GutterHandler,u=e("./mouse_event").MouseEvent,a=e("./dragdrop_handler").DragdropHandler,f=e("../config"),l=function(e){var t=this;this.editor=e,new s(this),new o(this),new a(this);var n=function(t){!e.isFocused()&&e.textInput&&e.textInput.moveToMouse(t),e.focus()},u=e.renderer.getMouseEventTarget();r.addListener(u,"click",this.onMouseEvent.bind(this,"click")),r.addListener(u,"mousemove",this.onMouseMove.bind(this,"mousemove")),r.addMultiMouseDownListener(u,[400,300,250],this,"onMouseEvent"),e.renderer.scrollBarV&&(r.addMultiMouseDownListener(e.renderer.scrollBarV.inner,[400,300,250],this,"onMouseEvent"),r.addMultiMouseDownListener(e.renderer.scrollBarH.inner,[400,300,250],this,"onMouseEvent"),i.isIE&&(r.addListener(e.renderer.scrollBarV.element,"mousedown",n),r.addListener(e.renderer.scrollBarH.element,"mousemove",n))),r.addMouseWheelListener(e.container,this.onMouseWheel.bind(this,"mousewheel"));var f=e.renderer.$gutter;r.addListener(f,"mousedown",this.onMouseEvent.bind(this,"guttermousedown")),r.addListener(f,"click",this.onMouseEvent.bind(this,"gutterclick")),r.addListener(f,"dblclick",this.onMouseEvent.bind(this,"gutterdblclick")),r.addListener(f,"mousemove",this.onMouseEvent.bind(this,"guttermousemove")),r.addListener(u,"mousedown",n),r.addListener(f,"mousedown",function(t){return e.focus(),r.preventDefault(t)}),e.on("mousemove",function(n){if(t.state||t.$dragDelay||!t.$dragEnabled)return;var r=e.renderer.screenToTextCoordinates(n.x,n.y),i=e.session.selection.getRange(),s=e.renderer;!i.isEmpty()&&i.insideStart(r.row,r.column)?s.setCursorStyle("default"):s.setCursorStyle("")})};(function(){this.onMouseEvent=function(e,t){this.editor._emit(e,new u(t,this.editor))},this.onMouseMove=function(e,t){var n=this.editor._eventRegistry&&this.editor._eventRegistry.mousemove;if(!n||!n.length)return;this.editor._emit(e,new u(t,this.editor))},this.onMouseWheel=function(e,t){var n=new u(t,this.editor);n.speed=this.$scrollSpeed*2,n.wheelX=t.wheelX,n.wheelY=t.wheelY,this.editor._emit(e,n)},this.setState=function(e){this.state=e},this.captureMouse=function(e,t){this.x=e.x,this.y=e.y,this.isMousePressed=!0;var n=this.editor.renderer;n.$keepTextAreaAtCursor&&(n.$keepTextAreaAtCursor=null);var s=this,o=function(e){if(!e)return;if(i.isWebKit&&!e.which&&s.releaseMouse)return s.releaseMouse();s.x=e.clientX,s.y=e.clientY,t&&t(e),s.mouseEvent=new u(e,s.editor),s.$mouseMoved=!0},a=function(e){clearInterval(l),f(),s[s.state+"End"]&&s[s.state+"End"](e),s.state="",n.$keepTextAreaAtCursor==null&&(n.$keepTextAreaAtCursor=!0,n.$moveTextAreaToCursor()),s.isMousePressed=!1,s.$onCaptureMouseMove=s.releaseMouse=null,e&&s.onMouseEvent("mouseup",e)},f=function(){s[s.state]&&s[s.state](),s.$mouseMoved=!1};if(i.isOldIE&&e.domEvent.type=="dblclick")return setTimeout(function(){a(e)});s.$onCaptureMouseMove=o,s.releaseMouse=r.capture(this.editor.container,o,a);var l=setInterval(f,20)},this.releaseMouse=null,this.cancelContextMenu=function(){var e=function(t){if(t&&t.domEvent&&t.domEvent.type!="contextmenu")return;this.editor.off("nativecontextmenu",e),t&&t.domEvent&&r.stopEvent(t.domEvent)}.bind(this);setTimeout(e,10),this.editor.on("nativecontextmenu",e)}}).call(l.prototype),f.defineOptions(l.prototype,"mouseHandler",{scrollSpeed:{initialValue:2},dragDelay:{initialValue:i.isMac?150:0},dragEnabled:{initialValue:!0},focusTimout:{initialValue:0},tooltipFollowsMouse:{initialValue:!0}}),t.MouseHandler=l}),ace.define("ace/mouse/fold_handler",["require","exports","module"],function(e,t,n){"use strict";function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e.session,i=r.getFoldAt(n.row,n.column,1);i&&(t.getAccelKey()?r.removeFold(i):r.expandFold(i),t.stop())}),e.on("gutterclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session;i.foldWidgets&&i.foldWidgets[r]&&e.session.onFoldWidgetClick(r,t),e.isFocused()||e.focus(),t.stop()}}),e.on("gutterdblclick",function(t){var n=e.renderer.$gutterLayer.getRegion(t);if(n=="foldWidgets"){var r=t.getDocumentPosition().row,i=e.session,s=i.getParentFoldRangeData(r,!0),o=s.range||s.firstRange;if(o){r=o.start.row;var u=i.getFoldAt(r,i.getLine(r).length,1);u?i.removeFold(u):(i.addFold("...",o),e.renderer.scrollCursorIntoView({row:o.start.row,column:0}))}t.stop()}})}t.FoldHandler=r}),ace.define("ace/keyboard/keybinding",["require","exports","module","ace/lib/keys","ace/lib/event"],function(e,t,n){"use strict";var r=e("../lib/keys"),i=e("../lib/event"),s=function(e){this.$editor=e,this.$data={editor:e},this.$handlers=[],this.setDefaultHandler(e.commands)};(function(){this.setDefaultHandler=function(e){this.removeKeyboardHandler(this.$defaultHandler),this.$defaultHandler=e,this.addKeyboardHandler(e,0)},this.setKeyboardHandler=function(e){var t=this.$handlers;if(t[t.length-1]==e)return;while(t[t.length-1]&&t[t.length-1]!=this.$defaultHandler)this.removeKeyboardHandler(t[t.length-1]);this.addKeyboardHandler(e,1)},this.addKeyboardHandler=function(e,t){if(!e)return;typeof e=="function"&&!e.handleKeyboard&&(e.handleKeyboard=e);var n=this.$handlers.indexOf(e);n!=-1&&this.$handlers.splice(n,1),t==undefined?this.$handlers.push(e):this.$handlers.splice(t,0,e),n==-1&&e.attach&&e.attach(this.$editor)},this.removeKeyboardHandler=function(e){var t=this.$handlers.indexOf(e);return t==-1?!1:(this.$handlers.splice(t,1),e.detach&&e.detach(this.$editor),!0)},this.getKeyboardHandler=function(){return this.$handlers[this.$handlers.length-1]},this.getStatusText=function(){var e=this.$data,t=e.editor;return this.$handlers.map(function(n){return n.getStatusText&&n.getStatusText(t,e)||""}).filter(Boolean).join(" ")},this.$callKeyboardHandlers=function(e,t,n,r){var s,o=!1,u=this.$editor.commands;for(var a=this.$handlers.length;a--;){s=this.$handlers[a].handleKeyboard(this.$data,e,t,n,r);if(!s||!s.command)continue;s.command=="null"?o=!0:o=u.exec(s.command,this.$editor,s.args,r),o&&r&&e!=-1&&s.passEvent!=1&&s.command.passEvent!=1&&i.stopEvent(r);if(o)break}return o},this.onCommandKey=function(e,t,n){var i=r.keyCodeToString(n);this.$callKeyboardHandlers(t,i,n,e)},this.onTextInput=function(e){var t=this.$callKeyboardHandlers(-1,e);t||this.$editor.commands.exec("insertstring",this.$editor,e)}}).call(s.prototype),t.KeyBinding=s}),ace.define("ace/range",["require","exports","module"],function(e,t,n){"use strict";var r=function(e,t){return e.row-t.row||e.column-t.column},i=function(e,t,n,r){this.start={row:e,column:t},this.end={row:n,column:r}};(function(){this.isEqual=function(e){return this.start.row===e.start.row&&this.end.row===e.end.row&&this.start.column===e.start.column&&this.end.column===e.end.column},this.toString=function(){return"Range: ["+this.start.row+"/"+this.start.column+"] -> ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.rowt.row||e.row==t.row&&e.column>t.column},this.getRange=function(){var e=this.anchor,t=this.lead;return this.isEmpty()?o.fromPoints(t,t):this.isBackwards()?o.fromPoints(t,e):o.fromPoints(e,t)},this.clearSelection=function(){this.$isEmpty||(this.$isEmpty=!0,this._emit("changeSelection"))},this.selectAll=function(){var e=this.doc.getLength()-1;this.setSelectionAnchor(0,0),this.moveCursorTo(e,this.doc.getLine(e).length)},this.setRange=this.setSelectionRange=function(e,t){t?(this.setSelectionAnchor(e.end.row,e.end.column),this.selectTo(e.start.row,e.start.column)):(this.setSelectionAnchor(e.start.row,e.start.column),this.selectTo(e.end.row,e.end.column)),this.getRange().isEmpty()&&(this.$isEmpty=!0),this.$desiredColumn=null},this.$moveSelection=function(e){var t=this.lead;this.$isEmpty&&this.setSelectionAnchor(t.row,t.column),e.call(this)},this.selectTo=function(e,t){this.$moveSelection(function(){this.moveCursorTo(e,t)})},this.selectToPosition=function(e){this.$moveSelection(function(){this.moveCursorToPosition(e)})},this.moveTo=function(e,t){this.clearSelection(),this.moveCursorTo(e,t)},this.moveToPosition=function(e){this.clearSelection(),this.moveCursorToPosition(e)},this.selectUp=function(){this.$moveSelection(this.moveCursorUp)},this.selectDown=function(){this.$moveSelection(this.moveCursorDown)},this.selectRight=function(){this.$moveSelection(this.moveCursorRight)},this.selectLeft=function(){this.$moveSelection(this.moveCursorLeft)},this.selectLineStart=function(){this.$moveSelection(this.moveCursorLineStart)},this.selectLineEnd=function(){this.$moveSelection(this.moveCursorLineEnd)},this.selectFileEnd=function(){this.$moveSelection(this.moveCursorFileEnd)},this.selectFileStart=function(){this.$moveSelection(this.moveCursorFileStart)},this.selectWordRight=function(){this.$moveSelection(this.moveCursorWordRight)},this.selectWordLeft=function(){this.$moveSelection(this.moveCursorWordLeft)},this.getWordRange=function(e,t){if(typeof t=="undefined"){var n=e||this.lead;e=n.row,t=n.column}return this.session.getWordRange(e,t)},this.selectWord=function(){this.setSelectionRange(this.getWordRange())},this.selectAWord=function(){var e=this.getCursor(),t=this.session.getAWordRange(e.row,e.column);this.setSelectionRange(t)},this.getLineRange=function(e,t){var n=typeof e=="number"?e:this.lead.row,r,i=this.session.getFoldLine(n);return i?(n=i.start.row,r=i.end.row):r=n,t===!0?new o(n,0,r,this.session.getLine(r).length):new o(n,0,r+1,0)},this.selectLine=function(){this.setSelectionRange(this.getLineRange())},this.moveCursorUp=function(){this.moveCursorBy(-1,0)},this.moveCursorDown=function(){this.moveCursorBy(1,0)},this.moveCursorLeft=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,-1))this.moveCursorTo(t.start.row,t.start.column);else if(e.column===0)e.row>0&&this.moveCursorTo(e.row-1,this.doc.getLine(e.row-1).length);else{var n=this.session.getTabSize();this.session.isTabStop(e)&&this.doc.getLine(e.row).slice(e.column-n,e.column).split(" ").length-1==n?this.moveCursorBy(0,-n):this.moveCursorBy(0,-1)}},this.moveCursorRight=function(){var e=this.lead.getPosition(),t;if(t=this.session.getFoldAt(e.row,e.column,1))this.moveCursorTo(t.end.row,t.end.column);else if(this.lead.column==this.doc.getLine(this.lead.row).length)this.lead.row0&&(t.column=r)}}this.moveCursorTo(t.row,t.column)},this.moveCursorFileEnd=function(){var e=this.doc.getLength()-1,t=this.doc.getLine(e).length;this.moveCursorTo(e,t)},this.moveCursorFileStart=function(){this.moveCursorTo(0,0)},this.moveCursorLongWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i;this.session.nonTokenRe.lastIndex=0,this.session.tokenRe.lastIndex=0;var s=this.session.getFoldAt(e,t,1);if(s){this.moveCursorTo(s.end.row,s.end.column);return}if(i=this.session.nonTokenRe.exec(r))t+=this.session.nonTokenRe.lastIndex,this.session.nonTokenRe.lastIndex=0,r=n.substring(t);if(t>=n.length){this.moveCursorTo(e,n.length),this.moveCursorRight(),e0&&this.moveCursorWordLeft();return}if(o=this.session.tokenRe.exec(s))t-=this.session.tokenRe.lastIndex,this.session.tokenRe.lastIndex=0;this.moveCursorTo(e,t)},this.$shortWordEndIndex=function(e){var t,n=0,r,i=/\s/,s=this.session.tokenRe;s.lastIndex=0;if(t=this.session.tokenRe.exec(e))n=this.session.tokenRe.lastIndex;else{while((r=e[n])&&i.test(r))n++;if(n<1){s.lastIndex=0;while((r=e[n])&&!s.test(r)){s.lastIndex=0,n++;if(i.test(r)){if(n>2){n--;break}while((r=e[n])&&i.test(r))n++;if(n>2)break}}}}return s.lastIndex=0,n},this.moveCursorShortWordRight=function(){var e=this.lead.row,t=this.lead.column,n=this.doc.getLine(e),r=n.substring(t),i=this.session.getFoldAt(e,t,1);if(i)return this.moveCursorTo(i.end.row,i.end.column);if(t==n.length){var s=this.doc.getLength();do e++,r=this.doc.getLine(e);while(e0&&/^\s*$/.test(r));t=r.length,/\s+$/.test(r)||(r="")}var s=i.stringReverse(r),o=this.$shortWordEndIndex(s);return this.moveCursorTo(e,t-o)},this.moveCursorWordRight=function(){this.session.$selectLongWords?this.moveCursorLongWordRight():this.moveCursorShortWordRight()},this.moveCursorWordLeft=function(){this.session.$selectLongWords?this.moveCursorLongWordLeft():this.moveCursorShortWordLeft()},this.moveCursorBy=function(e,t){var n=this.session.documentToScreenPosition(this.lead.row,this.lead.column);t===0&&(this.$desiredColumn?n.column=this.$desiredColumn:this.$desiredColumn=n.column);var r=this.session.screenToDocumentPosition(n.row+e,n.column);e!==0&&t===0&&r.row===this.lead.row&&r.column===this.lead.column&&this.session.lineWidgets&&this.session.lineWidgets[r.row]&&r.row++,this.moveCursorTo(r.row,r.column+t,t===0)},this.moveCursorToPosition=function(e){this.moveCursorTo(e.row,e.column)},this.moveCursorTo=function(e,t,n){var r=this.session.getFoldAt(e,t,1);r&&(e=r.start.row,t=r.start.column),this.$keepDesiredColumnOnChange=!0,this.lead.setPosition(e,t),this.$keepDesiredColumnOnChange=!1,n||(this.$desiredColumn=null)},this.moveCursorToScreen=function(e,t,n){var r=this.session.screenToDocumentPosition(e,t);this.moveCursorTo(r.row,r.column,n)},this.detach=function(){this.lead.detach(),this.anchor.detach(),this.session=this.doc=null},this.fromOrientedRange=function(e){this.setSelectionRange(e,e.cursor==e.start),this.$desiredColumn=e.desiredColumn||this.$desiredColumn},this.toOrientedRange=function(e){var t=this.getRange();return e?(e.start.column=t.start.column,e.start.row=t.start.row,e.end.column=t.end.column,e.end.row=t.end.row):e=t,e.cursor=this.isBackwards()?e.start:e.end,e.desiredColumn=this.$desiredColumn,e},this.getRangeOfMovements=function(e){var t=this.getCursor();try{e.call(null,this);var n=this.getCursor();return o.fromPoints(t,n)}catch(r){return o.fromPoints(t,t)}finally{this.moveCursorToPosition(t)}},this.toJSON=function(){if(this.rangeCount)var e=this.ranges.map(function(e){var t=e.clone();return t.isBackwards=e.cursor==e.start,t});else{var e=this.getRange();e.isBackwards=this.isBackwards()}return e},this.fromJSON=function(e){if(e.start==undefined){if(this.rangeList){this.toSingleRange(e[0]);for(var t=e.length;t--;){var n=o.fromPoints(e[t].start,e[t].end);e.isBackwards&&(n.cursor=n.start),this.addRange(n,!0)}return}e=e[0]}this.rangeList&&this.toSingleRange(e),this.setSelectionRange(e,e.isBackwards)},this.isEqual=function(e){if((e.length||this.rangeCount)&&e.length!=this.rangeCount)return!1;if(!e.length||!this.ranges)return this.getRange().isEqual(e);for(var t=this.ranges.length;t--;)if(!this.ranges[t].isEqual(e[t]))return!1;return!0}}).call(u.prototype),t.Selection=u}),ace.define("ace/tokenizer",["require","exports","module"],function(e,t,n){"use strict";var r=2e3,i=function(e){this.states=e,this.regExps={},this.matchMappings={};for(var t in this.states){var n=this.states[t],r=[],i=0,s=this.matchMappings[t]={defaultToken:"text"},o="g",u=[];for(var a=0;a1?f.onMatch=this.$applyToken:f.onMatch=f.token),c>1&&(/\\\d/.test(f.regex)?l=f.regex.replace(/\\([0-9]+)/g,function(e,t){return"\\"+(parseInt(t,10)+i+1)}):(c=1,l=this.removeCapturingGroups(f.regex)),!f.splitRegex&&typeof f.token!="string"&&u.push(f)),s[i]=a,i+=c,r.push(l),f.onMatch||(f.onMatch=null)}r.length||(s[0]=0,r.push("$")),u.forEach(function(e){e.splitRegex=this.createSplitterRegexp(e.regex,o)},this),this.regExps[t]=new RegExp("("+r.join(")|(")+")|($)",o)}};(function(){this.$setMaxTokenCount=function(e){r=e|0},this.$applyToken=function(e){var t=this.splitRegex.exec(e).slice(1),n=this.token.apply(this,t);if(typeof n=="string")return[{type:n,value:e}];var r=[];for(var i=0,s=n.length;il){var g=e.substring(l,m-v.length);h.type==p?h.value+=g:(h.type&&f.push(h),h={type:p,value:g})}for(var y=0;yr){c>2*e.length&&this.reportError("infinite loop with in ace tokenizer",{startState:t,line:e});while(l1&&n[0]!==i&&n.unshift("#tmp",i),{tokens:f,state:n.length?n:i}},this.reportError=function(e,t){var n=new Error(e);n.data=t,typeof console=="object"&&console.error&&console.error(n),setTimeout(function(){throw n})}}).call(i.prototype),t.Tokenizer=i}),ace.define("ace/mode/text_highlight_rules",["require","exports","module","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../lib/lang"),i=function(){this.$rules={start:[{token:"empty_line",regex:"^$"},{defaultToken:"text"}]}};(function(){this.addRules=function(e,t){if(!t){for(var n in e)this.$rules[n]=e[n];return}for(var n in e){var r=e[n];for(var i=0;i=this.$rowTokens.length){this.$row+=1,e||(e=this.$session.getLength());if(this.$row>=e)return this.$row=e-1,null;this.$rowTokens=this.$session.getTokens(this.$row),this.$tokenIndex=0}return this.$rowTokens[this.$tokenIndex]},this.getCurrentToken=function(){return this.$rowTokens[this.$tokenIndex]},this.getCurrentTokenRow=function(){return this.$row},this.getCurrentTokenColumn=function(){var e=this.$rowTokens,t=this.$tokenIndex,n=e[t].start;if(n!==undefined)return n;n=0;while(t>0)t-=1,n+=e[t].value.length;return n}}).call(r.prototype),t.TokenIterator=r}),ace.define("ace/mode/text",["require","exports","module","ace/tokenizer","ace/mode/text_highlight_rules","ace/mode/behaviour","ace/unicode","ace/lib/lang","ace/token_iterator","ace/range"],function(e,t,n){"use strict";var r=e("../tokenizer").Tokenizer,i=e("./text_highlight_rules").TextHighlightRules,s=e("./behaviour").Behaviour,o=e("../unicode"),u=e("../lib/lang"),a=e("../token_iterator").TokenIterator,f=e("../range").Range,l=function(){this.HighlightRules=i,this.$behaviour=new s};(function(){this.tokenRe=new RegExp("^["+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]+","g"),this.nonTokenRe=new RegExp("^(?:[^"+o.packages.L+o.packages.Mn+o.packages.Mc+o.packages.Nd+o.packages.Pc+"\\$_]|\\s])+","g"),this.getTokenizer=function(){return this.$tokenizer||(this.$highlightRules=this.$highlightRules||new this.HighlightRules,this.$tokenizer=new r(this.$highlightRules.getRules())),this.$tokenizer},this.lineCommentStart="",this.blockComment="",this.toggleCommentLines=function(e,t,n,r){function w(e){for(var t=n;t<=r;t++)e(i.getLine(t),t)}var i=t.doc,s=!0,o=!0,a=Infinity,f=t.getTabSize(),l=!1;if(!this.lineCommentStart){if(!this.blockComment)return!1;var c=this.blockComment.start,h=this.blockComment.end,p=new RegExp("^(\\s*)(?:"+u.escapeRegExp(c)+")"),d=new RegExp("(?:"+u.escapeRegExp(h)+")\\s*$"),v=function(e,t){if(g(e,t))return;if(!s||/\S/.test(e))i.insertInLine({row:t,column:e.length},h),i.insertInLine({row:t,column:a},c)},m=function(e,t){var n;(n=e.match(d))&&i.removeInLine(t,e.length-n[0].length,e.length),(n=e.match(p))&&i.removeInLine(t,n[1].length,n[0].length)},g=function(e,n){if(p.test(e))return!0;var r=t.getTokens(n);for(var i=0;i2?r%f!=f-1:r%f==0}}var E=Infinity;w(function(e,t){var n=e.search(/\S/);n!==-1?(ne.length&&(E=e.length)}),a==Infinity&&(a=E,s=!1,o=!1),l&&a%f!=0&&(a=Math.floor(a/f)*f),w(o?m:v)},this.toggleBlockComment=function(e,t,n,r){var i=this.blockComment;if(!i)return;!i.start&&i[0]&&(i=i[0]);var s=new a(t,r.row,r.column),o=s.getCurrentToken(),u=t.selection,l=t.selection.toOrientedRange(),c,h;if(o&&/comment/.test(o.type)){var p,d;while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.start);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;p=new f(m,g,m,g+i.start.length);break}o=s.stepBackward()}var s=new a(t,r.row,r.column),o=s.getCurrentToken();while(o&&/comment/.test(o.type)){var v=o.value.indexOf(i.end);if(v!=-1){var m=s.getCurrentTokenRow(),g=s.getCurrentTokenColumn()+v;d=new f(m,g,m,g+i.end.length);break}o=s.stepForward()}d&&t.remove(d),p&&(t.remove(p),c=p.start.row,h=-i.start.length)}else h=i.start.length,c=n.start.row,t.insert(n.end,i.end),t.insert(n.start,i.start);l.start.row==c&&(l.start.column+=h),l.end.row==c&&(l.end.column+=h),t.selection.fromOrientedRange(l)},this.getNextLineIndent=function(e,t,n){return this.$getIndent(t)},this.checkOutdent=function(e,t,n){return!1},this.autoOutdent=function(e,t,n){},this.$getIndent=function(e){return e.match(/^\s*/)[0]},this.createWorker=function(e){return null},this.createModeDelegates=function(e){this.$embeds=[],this.$modes={};for(var t in e)e[t]&&(this.$embeds.push(t),this.$modes[t]=new e[t]);var n=["toggleBlockComment","toggleCommentLines","getNextLineIndent","checkOutdent","autoOutdent","transformAction","getCompletions"];for(var t=0;tthis.row)return;if(n.start.row==this.row&&n.start.column>this.column)return;var r=this.row,i=this.column,s=n.start,o=n.end;if(t.action==="insertText")if(s.row===r&&s.column<=i){if(s.column!==i||!this.$insertRight)s.row===o.row?i+=o.column-s.column:(i-=s.column,r+=o.row-s.row)}else s.row!==o.row&&s.row=i?i=s.column:i=Math.max(0,i-(o.column-s.column)):s.row!==o.row&&s.row=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,o=e("./anchor").Anchor,u=function(e){this.$lines=[],e.length===0?this.$lines=[""]:Array.isArray(e)?this._insertLines(0,e):this.insert({row:0,column:0},e)};(function(){r.implement(this,i),this.setValue=function(e){var t=this.getLength();this.remove(new s(0,0,t,this.getLine(t-1).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new o(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){if(e.start.row==e.end.row)return this.getLine(e.start.row).substring(e.start.column,e.end.column);var t=this.getLines(e.start.row,e.end.row);t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;return e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column)),t.join(this.getNewLineCharacter())},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):e.row<0&&(e.row=0),e},this.insert=function(e,t){if(!t||t.length===0)return e;e=this.$clipPosition(e),this.getLength()<=1&&this.$detectNewLine(t);var n=this.$split(t),r=n.splice(0,1)[0],i=n.length==0?null:n.splice(n.length-1,1)[0];return e=this.insertInLine(e,r),i!==null&&(e=this.insertNewLine(e),e=this._insertLines(e.row,n),e=this.insertInLine(e,i||"")),e},this.insertLines=function(e,t){return e>=this.getLength()?this.insert({row:e,column:0},"\n"+t.join("\n")):this._insertLines(Math.max(e,0),t)},this._insertLines=function(e,t){if(t.length==0)return{row:e,column:0};while(t.length>61440){var n=this._insertLines(e,t.slice(0,61440));t=t.slice(61440),e=n.row}var r=[e,0];r.push.apply(r,t),this.$lines.splice.apply(this.$lines,r);var i=new s(e,0,e+t.length,0),o={action:"insertLines",range:i,lines:t};return this._signal("change",{data:o}),i.end},this.insertNewLine=function(e){e=this.$clipPosition(e);var t=this.$lines[e.row]||"";this.$lines[e.row]=t.substring(0,e.column),this.$lines.splice(e.row+1,0,t.substring(e.column,t.length));var n={row:e.row+1,column:0},r={action:"insertText",range:s.fromPoints(e,n),text:this.getNewLineCharacter()};return this._signal("change",{data:r}),n},this.insertInLine=function(e,t){if(t.length==0)return e;var n=this.$lines[e.row]||"";this.$lines[e.row]=n.substring(0,e.column)+t+n.substring(e.column);var r={row:e.row,column:e.column+t.length},i={action:"insertText",range:s.fromPoints(e,r),text:t};return this._signal("change",{data:i}),r},this.remove=function(e){e instanceof s||(e=s.fromPoints(e.start,e.end)),e.start=this.$clipPosition(e.start),e.end=this.$clipPosition(e.end);if(e.isEmpty())return e.start;var t=e.start.row,n=e.end.row;if(e.isMultiLine()){var r=e.start.column==0?t:t+1,i=n-1;e.end.column>0&&this.removeInLine(n,0,e.end.column),i>=r&&this._removeLines(r,i),r!=t&&(this.removeInLine(t,e.start.column,this.getLine(t).length),this.removeNewLine(e.start.row))}else this.removeInLine(t,e.start.column,e.end.column);return e.start},this.removeInLine=function(e,t,n){if(t==n)return;var r=new s(e,t,e,n),i=this.getLine(e),o=i.substring(t,n),u=i.substring(0,t)+i.substring(n,i.length);this.$lines.splice(e,1,u);var a={action:"removeText",range:r,text:o};return this._signal("change",{data:a}),r.start},this.removeLines=function(e,t){return e<0||t>=this.getLength()?this.remove(new s(e,0,t+1,0)):this._removeLines(e,t)},this._removeLines=function(e,t){var n=new s(e,0,t+1,0),r=this.$lines.splice(e,t-e+1),i={action:"removeLines",range:n,nl:this.getNewLineCharacter(),lines:r};return this._signal("change",{data:i}),r},this.removeNewLine=function(e){var t=this.getLine(e),n=this.getLine(e+1),r=new s(e,t.length,e+1,0),i=t+n;this.$lines.splice(e,2,i);var o={action:"removeText",range:r,text:this.getNewLineCharacter()};this._signal("change",{data:o})},this.replace=function(e,t){e instanceof s||(e=s.fromPoints(e.start,e.end));if(t.length==0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);if(t)var n=this.insert(e.start,t);else n=e.start;return n},this.applyDeltas=function(e){for(var t=0;t=0;t--){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this._removeLines(r.start.row,r.end.row-1):n.action=="insertText"?this.remove(r):n.action=="removeLines"?this._insertLines(r.start.row,n.lines):n.action=="removeText"&&this.insert(r.start,n.text)}},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i20){n.running=setTimeout(n.$worker,20);break}}n.currentLine=t,s<=r&&n.fireUpdateEvent(s,r)}};(function(){r.implement(this,i),this.setTokenizer=function(e){this.tokenizer=e,this.lines=[],this.states=[],this.start(0)},this.setDocument=function(e){this.doc=e,this.lines=[],this.states=[],this.stop()},this.fireUpdateEvent=function(e,t){var n={first:e,last:t};this._signal("update",{data:n})},this.start=function(e){this.currentLine=Math.min(e||0,this.currentLine,this.doc.getLength()),this.lines.splice(this.currentLine,this.lines.length),this.states.splice(this.currentLine,this.states.length),this.stop(),this.running=setTimeout(this.$worker,700)},this.scheduleStart=function(){this.running||(this.running=setTimeout(this.$worker,700))},this.$updateOnChange=function(e){var t=e.range,n=t.start.row,r=t.end.row-n;if(r===0)this.lines[n]=null;else if(e.action=="removeText"||e.action=="removeLines")this.lines.splice(n,r+1,null),this.states.splice(n,r+1,null);else{var i=Array(r+1);i.unshift(n,1),this.lines.splice.apply(this.lines,i),this.states.splice.apply(this.states,i)}this.currentLine=Math.min(n,this.currentLine,this.doc.getLength()),this.stop()},this.stop=function(){this.running&&clearTimeout(this.running),this.running=!1},this.getTokens=function(e){return this.lines[e]||this.$tokenizeRow(e)},this.getState=function(e){return this.currentLine==e&&this.$tokenizeRow(e),this.states[e]||"start"},this.$tokenizeRow=function(e){var t=this.doc.getLine(e),n=this.states[e-1],r=this.tokenizer.getLineTokens(t,n,e);return this.states[e]+""!=r.state+""?(this.states[e]=r.state,this.lines[e+1]=null,this.currentLine>e+1&&(this.currentLine=e+1)):this.currentLine==e&&(this.currentLine=e+1),this.lines[e]=r.tokens}}).call(s.prototype),t.BackgroundTokenizer=s}),ace.define("ace/search_highlight",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(e,t,n){this.setRegexp(e),this.clazz=t,this.type=n||"text"};(function(){this.MAX_RANGES=500,this.setRegexp=function(e){if(this.regExp+""==e+"")return;this.regExp=e,this.cache=[]},this.update=function(e,t,n,i){if(!this.regExp)return;var o=i.firstRow,u=i.lastRow;for(var a=o;a<=u;a++){var f=this.cache[a];f==null&&(f=r.getMatchOffsets(n.getLine(a),this.regExp),f.length>this.MAX_RANGES&&(f=f.slice(0,this.MAX_RANGES)),f=f.map(function(e){return new s(a,e.offset,a,e.offset+e.length)}),this.cache[a]=f.length?f:"");for(var l=f.length;l--;)t.drawSingleLineMarker(e,f[l].toScreenRange(n),this.clazz,i)}}}).call(o.prototype),t.SearchHighlight=o}),ace.define("ace/edit_session/fold_line",["require","exports","module","ace/range"],function(e,t,n){"use strict";function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.folds=[t];var n=t[t.length-1];this.range=new r(t[0].start.row,t[0].start.column,n.end.row,n.end.column),this.start=this.range.start,this.end=this.range.end,this.folds.forEach(function(e){e.setFoldLine(this)},this)}var r=e("../range").Range;(function(){this.shiftRow=function(e){this.start.row+=e,this.end.row+=e,this.folds.forEach(function(t){t.start.row+=e,t.end.row+=e})},this.addFold=function(e){if(e.sameRow){if(e.start.rowthis.endRow)throw new Error("Can't add a fold to this FoldLine as it has no connection");this.folds.push(e),this.folds.sort(function(e,t){return-e.range.compareEnd(t.start.row,t.start.column)}),this.range.compareEnd(e.start.row,e.start.column)>0?(this.end.row=e.end.row,this.end.column=e.end.column):this.range.compareStart(e.end.row,e.end.column)<0&&(this.start.row=e.start.row,this.start.column=e.start.column)}else if(e.start.row==this.end.row)this.folds.push(e),this.end.row=e.end.row,this.end.column=e.end.column;else{if(e.end.row!=this.start.row)throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");this.folds.unshift(e),this.start.row=e.start.row,this.start.column=e.start.column}e.foldLine=this},this.containsRow=function(e){return e>=this.start.row&&e<=this.end.row},this.walk=function(e,t,n){var r=0,i=this.folds,s,o,u,a=!0;t==null&&(t=this.end.row,n=this.end.column);for(var f=0;f0)continue;var a=i(e,o.start);return u===0?t&&a!==0?-s-2:s:a>0||a===0&&!t?s:-s-1}return-s-1},this.add=function(e){var t=!e.isEmpty(),n=this.pointIndex(e.start,t);n<0&&(n=-n-1);var r=this.pointIndex(e.end,t,n);return r<0?r=-r-1:r++,this.ranges.splice(n,r-n,e)},this.addList=function(e){var t=[];for(var n=e.length;n--;)t.push.call(t,this.add(e[n]));return t},this.substractPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges.splice(t,1)},this.merge=function(){var e=[],t=this.ranges;t=t.sort(function(e,t){return i(e.start,t.start)});var n=t[0],r;for(var s=1;s=0},this.containsPoint=function(e){return this.pointIndex(e)>=0},this.rangeAtPoint=function(e){var t=this.pointIndex(e);if(t>=0)return this.ranges[t]},this.clipRows=function(e,t){var n=this.ranges;if(n[0].start.row>t||n[n.length-1].start.rowi)break;c.start.row==i&&c.start.column>=n.column&&(c.start.column!=n.column||!this.$insertRight)&&(c.start.column+=u,c.start.row+=o);if(c.end.row==i&&c.end.column>=n.column){if(c.end.column==n.column&&this.$insertRight)continue;c.end.column==n.column&&u>0&&fc.start.column&&c.end.column==a[f+1].start.column&&(c.end.column-=u),c.end.column+=u,c.end.row+=o}}if(o!=0&&f=e)return i;if(i.end.row>e)return null}return null},this.getNextFoldLine=function(e,t){var n=this.$foldData,r=0;t&&(r=n.indexOf(t)),r==-1&&(r=0);for(r;r=e)return i}return null},this.getFoldedRowCount=function(e,t){var n=this.$foldData,r=t-e+1;for(var i=0;i=t){u=e?r-=t-u:r=0);break}o>=e&&(u>=e?r-=o-u:r-=o-e+1)}return r},this.$addFoldLine=function(e){return this.$foldData.push(e),this.$foldData.sort(function(e,t){return e.start.row-t.start.row}),e},this.addFold=function(e,t){var n=this.$foldData,r=!1,o;e instanceof s?o=e:(o=new s(t,e),o.collapseChildren=t.collapseChildren),this.$clipRangeToDocument(o.range);var u=o.start.row,a=o.start.column,f=o.end.row,l=o.end.column;if(u0&&(this.removeFolds(p),p.forEach(function(e){o.addSubFold(e)}));for(var d=0;d0&&this.foldAll(e.start.row+1,e.end.row,e.collapseChildren-1),e.subFolds=[]},this.expandFolds=function(e){e.forEach(function(e){this.expandFold(e)},this)},this.unfold=function(e,t){var n,i;e==null?(n=new r(0,0,this.getLength(),0),t=!0):typeof e=="number"?n=new r(e,0,e,this.getLine(e).length):"row"in e?n=r.fromPoints(e,e):n=e,i=this.getFoldsInRangeList(n);if(t)this.removeFolds(i);else{var s=i;while(s.length)this.expandFolds(s),s=this.getFoldsInRangeList(n)}if(i.length)return i},this.isRowFolded=function(e,t){return!!this.getFoldLine(e,t)},this.getRowFoldEnd=function(e,t){var n=this.getFoldLine(e,t);return n?n.end.row:e},this.getRowFoldStart=function(e,t){var n=this.getFoldLine(e,t);return n?n.start.row:e},this.getFoldDisplayLine=function(e,t,n,r,i){r==null&&(r=e.start.row),i==null&&(i=0),t==null&&(t=e.end.row),n==null&&(n=this.getLine(t).length);var s=this.doc,o="";return e.walk(function(e,t,n,u){if(t=e){i=s.end.row;try{var o=this.addFold("...",s);o&&(o.collapseChildren=n)}catch(u){}}}},this.$foldStyles={manual:1,markbegin:1,markbeginend:1},this.$foldStyle="markbegin",this.setFoldStyle=function(e){if(!this.$foldStyles[e])throw new Error("invalid fold style: "+e+"["+Object.keys(this.$foldStyles).join(", ")+"]");if(this.$foldStyle==e)return;this.$foldStyle=e,e=="manual"&&this.unfold();var t=this.$foldMode;this.$setFolding(null),this.$setFolding(t)},this.$setFolding=function(e){if(this.$foldMode==e)return;this.$foldMode=e,this.removeListener("change",this.$updateFoldWidgets),this._emit("changeAnnotation");if(!e||this.$foldStyle=="manual"){this.foldWidgets=null;return}this.foldWidgets=[],this.getFoldWidget=e.getFoldWidget.bind(e,this,this.$foldStyle),this.getFoldWidgetRange=e.getFoldWidgetRange.bind(e,this,this.$foldStyle),this.$updateFoldWidgets=this.updateFoldWidgets.bind(this),this.on("change",this.$updateFoldWidgets)},this.getParentFoldRangeData=function(e,t){var n=this.foldWidgets;if(!n||t&&n[e])return{};var r=e-1,i;while(r>=0){var s=n[r];s==null&&(s=n[r]=this.getFoldWidget(r));if(s=="start"){var o=this.getFoldWidgetRange(r);i||(i=o);if(o&&o.end.row>=e)break}r--}return{range:r!==-1&&o,firstRange:i}},this.onFoldWidgetClick=function(e,t){t=t.domEvent;var n={children:t.shiftKey,all:t.ctrlKey||t.metaKey,siblings:t.altKey},r=this.$toggleFoldWidget(e,n);if(!r){var i=t.target||t.srcElement;i&&/ace_fold-widget/.test(i.className)&&(i.className+=" ace_invalid")}},this.$toggleFoldWidget=function(e,t){if(!this.getFoldWidget)return;var n=this.getFoldWidget(e),r=this.getLine(e),i=n==="end"?-1:1,s=this.getFoldAt(e,i===-1?0:r.length,i);if(s){t.children||t.all?this.removeFold(s):this.expandFold(s);return}var o=this.getFoldWidgetRange(e,!0);if(o&&!o.isMultiLine()){s=this.getFoldAt(o.start.row,o.start.column,1);if(s&&o.isEqual(s.range)){this.removeFold(s);return}}if(t.siblings){var u=this.getParentFoldRangeData(e);if(u.range)var a=u.range.start.row+1,f=u.range.end.row;this.foldAll(a,f,t.all?1e4:0)}else t.children?(f=o?o.end.row:this.getLength(),this.foldAll(e+1,o.end.row,t.all?1e4:0)):o&&(t.all&&(o.collapseChildren=1e4),this.addFold("...",o));return o},this.toggleFoldWidget=function(e){var t=this.selection.getCursor().row;t=this.getRowFoldStart(t);var n=this.$toggleFoldWidget(t,{});if(n)return;var r=this.getParentFoldRangeData(t,!0);n=r.range||r.firstRange;if(n){t=n.start.row;var i=this.getFoldAt(t,this.getLine(t).length,1);i?this.removeFold(i):this.addFold("...",n)}},this.updateFoldWidgets=function(e){var t=e.data,n=t.range,r=n.start.row,i=n.end.row-r;if(i===0)this.foldWidgets[r]=null;else if(t.action=="removeText"||t.action=="removeLines")this.foldWidgets.splice(r,i+1,null);else{var s=Array(i+1);s.unshift(r,1),this.foldWidgets.splice.apply(this.foldWidgets,s)}}}var r=e("../range").Range,i=e("./fold_line").FoldLine,s=e("./fold").Fold,o=e("../token_iterator").TokenIterator;t.Folding=u}),ace.define("ace/edit_session/bracket_match",["require","exports","module","ace/token_iterator","ace/range"],function(e,t,n){"use strict";function s(){this.findMatchingBracket=function(e,t){if(e.column==0)return null;var n=t||this.getLine(e.row).charAt(e.column-1);if(n=="")return null;var r=n.match(/([\(\[\{])|([\)\]\}])/);return r?r[1]?this.$findClosingBracket(r[1],e):this.$findOpeningBracket(r[2],e):null},this.getBracketRange=function(e){var t=this.getLine(e.row),n=!0,r,s=t.charAt(e.column-1),o=s&&s.match(/([\(\[\{])|([\)\]\}])/);o||(s=t.charAt(e.column),e={row:e.row,column:e.column+1},o=s&&s.match(/([\(\[\{])|([\)\]\}])/),n=!1);if(!o)return null;if(o[1]){var u=this.$findClosingBracket(o[1],e);if(!u)return null;r=i.fromPoints(e,u),n||(r.end.column++,r.start.column--),r.cursor=r.end}else{var u=this.$findOpeningBracket(o[2],e);if(!u)return null;r=i.fromPoints(u,e),n||(r.start.column++,r.end.column--),r.cursor=r.start}return r},this.$brackets={")":"(","(":")","]":"[","[":"]","{":"}","}":"{"},this.$findOpeningBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("rparen",".paren").replace(/\b(?:end|start|begin)\b/,"")+")+"));var a=t.column-o.getCurrentTokenColumn()-2,f=u.value;for(;;){while(a>=0){var l=f.charAt(a);if(l==i){s-=1;if(s==0)return{row:o.getCurrentTokenRow(),column:a+o.getCurrentTokenColumn()}}else l==e&&(s+=1);a-=1}do u=o.stepBackward();while(u&&!n.test(u.type));if(u==null)break;f=u.value,a=f.length-1}return null},this.$findClosingBracket=function(e,t,n){var i=this.$brackets[e],s=1,o=new r(this,t.row,t.column),u=o.getCurrentToken();u||(u=o.stepForward());if(!u)return;n||(n=new RegExp("(\\.?"+u.type.replace(".","\\.").replace("lparen",".paren").replace(/\b(?:end|start|begin)\b/,"")+")+"));var a=t.column-o.getCurrentTokenColumn();for(;;){var f=u.value,l=f.length;while(a=4352&&e<=4447||e>=4515&&e<=4519||e>=4602&&e<=4607||e>=9001&&e<=9002||e>=11904&&e<=11929||e>=11931&&e<=12019||e>=12032&&e<=12245||e>=12272&&e<=12283||e>=12288&&e<=12350||e>=12353&&e<=12438||e>=12441&&e<=12543||e>=12549&&e<=12589||e>=12593&&e<=12686||e>=12688&&e<=12730||e>=12736&&e<=12771||e>=12784&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=13054||e>=13056&&e<=19903||e>=19968&&e<=42124||e>=42128&&e<=42182||e>=43360&&e<=43388||e>=44032&&e<=55203||e>=55216&&e<=55238||e>=55243&&e<=55291||e>=63744&&e<=64255||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=65281&&e<=65376||e>=65504&&e<=65510}r.implement(this,o),this.setDocument=function(e){this.doc&&this.doc.removeListener("change",this.$onChange),this.doc=e,e.on("change",this.$onChange),this.bgTokenizer&&this.bgTokenizer.setDocument(this.getDocument()),this.resetCaches()},this.getDocument=function(){return this.doc},this.$resetRowCache=function(e){if(!e){this.$docRowCache=[],this.$screenRowCache=[];return}var t=this.$docRowCache.length,n=this.$getRowCacheIndex(this.$docRowCache,e)+1;t>n&&(this.$docRowCache.splice(n,t),this.$screenRowCache.splice(n,t))},this.$getRowCacheIndex=function(e,t){var n=0,r=e.length-1;while(n<=r){var i=n+r>>1,s=e[i];if(t>s)n=i+1;else{if(!(t=t)break}return r=n[s],r?(r.index=s,r.start=i-r.value.length,r):null},this.setUndoManager=function(e){this.$undoManager=e,this.$deltas=[],this.$deltasDoc=[],this.$deltasFold=[],this.$informUndoManager&&this.$informUndoManager.cancel();if(e){var t=this;this.$syncInformUndoManager=function(){t.$informUndoManager.cancel(),t.$deltasFold.length&&(t.$deltas.push({group:"fold",deltas:t.$deltasFold}),t.$deltasFold=[]),t.$deltasDoc.length&&(t.$deltas.push({group:"doc",deltas:t.$deltasDoc}),t.$deltasDoc=[]),t.$deltas.length>0&&e.execute({action:"aceupdate",args:[t.$deltas,t],merge:t.mergeUndoDeltas}),t.mergeUndoDeltas=!1,t.$deltas=[]},this.$informUndoManager=i.delayedCall(this.$syncInformUndoManager)}},this.markUndoGroup=function(){this.$syncInformUndoManager&&this.$syncInformUndoManager()},this.$defaultUndoManager={undo:function(){},redo:function(){},reset:function(){}},this.getUndoManager=function(){return this.$undoManager||this.$defaultUndoManager},this.getTabString=function(){return this.getUseSoftTabs()?i.stringRepeat(" ",this.getTabSize()):" "},this.setUseSoftTabs=function(e){this.setOption("useSoftTabs",e)},this.getUseSoftTabs=function(){return this.$useSoftTabs&&!this.$mode.$indentWithTabs},this.setTabSize=function(e){this.setOption("tabSize",e)},this.getTabSize=function(){return this.$tabSize},this.isTabStop=function(e){return this.$useSoftTabs&&e.column%this.$tabSize===0},this.$overwrite=!1,this.setOverwrite=function(e){this.setOption("overwrite",e)},this.getOverwrite=function(){return this.$overwrite},this.toggleOverwrite=function(){this.setOverwrite(!this.$overwrite)},this.addGutterDecoration=function(e,t){this.$decorations[e]||(this.$decorations[e]=""),this.$decorations[e]+=" "+t,this._signal("changeBreakpoint",{})},this.removeGutterDecoration=function(e,t){this.$decorations[e]=(this.$decorations[e]||"").replace(" "+t,""),this._signal("changeBreakpoint",{})},this.getBreakpoints=function(){return this.$breakpoints},this.setBreakpoints=function(e){this.$breakpoints=[];for(var t=0;t0&&(r=!!n.charAt(t-1).match(this.tokenRe)),r||(r=!!n.charAt(t).match(this.tokenRe));if(r)var i=this.tokenRe;else if(/^\s+$/.test(n.slice(t-1,t+1)))var i=/\s/;else var i=this.nonTokenRe;var s=t;if(s>0){do s--;while(s>=0&&n.charAt(s).match(i));s++}var o=t;while(oe&&(e=t.screenWidth)}),this.lineWidgetWidth=e},this.$computeWidth=function(e){if(this.$modified||e){this.$modified=!1;if(this.$useWrapMode)return this.screenWidth=this.$wrapLimit;var t=this.doc.getAllLines(),n=this.$rowLengthCache,r=0,i=0,s=this.$foldData[i],o=s?s.start.row:Infinity,u=t.length;for(var a=0;ao){a=s.end.row+1;if(a>=u)break;s=this.$foldData[i++],o=s?s.start.row:Infinity}n[a]==null&&(n[a]=this.$getStringScreenWidth(t[a])[0]),n[a]>r&&(r=n[a])}this.screenWidth=r}},this.getLine=function(e){return this.doc.getLine(e)},this.getLines=function(e,t){return this.doc.getLines(e,t)},this.getLength=function(){return this.doc.getLength()},this.getTextRange=function(e){return this.doc.getTextRange(e||this.selection.getRange())},this.insert=function(e,t){return this.doc.insert(e,t)},this.remove=function(e){return this.doc.remove(e)},this.undoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=e.length-1;r!=-1;r--){var i=e[r];i.group=="doc"?(this.doc.revertDeltas(i.deltas),n=this.$getUndoSelection(i.deltas,!0,n)):i.deltas.forEach(function(e){this.addFolds(e.folds)},this)}return this.$fromUndo=!1,n&&this.$undoSelect&&!t&&this.selection.setSelectionRange(n),n},this.redoChanges=function(e,t){if(!e.length)return;this.$fromUndo=!0;var n=null;for(var r=0;re.end.column&&(s.start.column+=u),s.end.row==e.end.row&&s.end.column>e.end.column&&(s.end.column+=u)),o&&s.start.row>=e.end.row&&(s.start.row+=o,s.end.row+=o)}s.end=this.insert(s.start,r);if(i.length){var a=e.start,l=s.start,o=l.row-a.row,u=l.column-a.column;this.addFolds(i.map(function(e){return e=e.clone(),e.start.row==a.row&&(e.start.column+=u),e.end.row==a.row&&(e.end.column+=u),e.start.row+=o,e.end.row+=o,e}))}return s},this.indentRows=function(e,t,n){n=n.replace(/\t/g,this.getTabString());for(var r=e;r<=t;r++)this.insert({row:r,column:0},n)},this.outdentRows=function(e){var t=e.collapseRows(),n=new f(0,0,0,0),r=this.getTabSize();for(var i=t.start.row;i<=t.end.row;++i){var s=this.getLine(i);n.start.row=i,n.end.row=i;for(var o=0;o0){var r=this.getRowFoldEnd(t+n);if(r>this.doc.getLength()-1)return 0;var i=r-t}else{e=this.$clipRowToDocument(e),t=this.$clipRowToDocument(t);var i=t-e+1}var s=new f(e,0,t,Number.MAX_VALUE),o=this.getFoldsInRange(s).map(function(e){return e=e.clone(),e.start.row+=i,e.end.row+=i,e}),u=n==0?this.doc.getLines(e,t):this.doc.removeLines(e,t);return this.doc.insertLines(e+i,u),o.length&&this.addFolds(o),i},this.moveLinesUp=function(e,t){return this.$moveLines(e,t,-1)},this.moveLinesDown=function(e,t){return this.$moveLines(e,t,1)},this.duplicateLines=function(e,t){return this.$moveLines(e,t,0)},this.$clipRowToDocument=function(e){return Math.max(0,Math.min(e,this.doc.getLength()-1))},this.$clipColumnToRow=function(e,t){return t<0?0:Math.min(this.doc.getLine(e).length,t)},this.$clipPositionToDocument=function(e,t){t=Math.max(0,t);if(e<0)e=0,t=0;else{var n=this.doc.getLength();e>=n?(e=n-1,t=this.doc.getLine(n-1).length):t=Math.min(this.doc.getLine(e).length,t)}return{row:e,column:t}},this.$clipRangeToDocument=function(e){e.start.row<0?(e.start.row=0,e.start.column=0):e.start.column=this.$clipColumnToRow(e.start.row,e.start.column);var t=this.doc.getLength()-1;return e.end.row>t?(e.end.row=t,e.end.column=this.doc.getLine(t).length):e.end.column=this.$clipColumnToRow(e.end.row,e.end.column),e},this.$wrapLimit=80,this.$useWrapMode=!1,this.$wrapLimitRange={min:null,max:null},this.setUseWrapMode=function(e){if(e!=this.$useWrapMode){this.$useWrapMode=e,this.$modified=!0,this.$resetRowCache(0);if(e){var t=this.getLength();this.$wrapData=Array(t),this.$updateWrapData(0,t-1)}this._signal("changeWrapMode")}},this.getUseWrapMode=function(){return this.$useWrapMode},this.setWrapLimitRange=function(e,t){if(this.$wrapLimitRange.min!==e||this.$wrapLimitRange.max!==t)this.$wrapLimitRange={min:e,max:t},this.$modified=!0,this._signal("changeWrapMode")},this.adjustWrapLimit=function(e,t){var n=this.$wrapLimitRange;n.max<0&&(n={min:t,max:t});var r=this.$constrainWrapLimit(e,n.min,n.max);return r!=this.$wrapLimit&&r>1?(this.$wrapLimit=r,this.$modified=!0,this.$useWrapMode&&(this.$updateWrapData(0,this.getLength()-1),this.$resetRowCache(0),this._signal("changeWrapLimit")),!0):!1},this.$constrainWrapLimit=function(e,t,n){return t&&(e=Math.max(t,e)),n&&(e=Math.min(n,e)),e},this.getWrapLimit=function(){return this.$wrapLimit},this.setWrapLimit=function(e){this.setWrapLimitRange(e,e)},this.getWrapLimitRange=function(){return{min:this.$wrapLimitRange.min,max:this.$wrapLimitRange.max}},this.$updateInternalDataOnChange=function(e){var t=this.$useWrapMode,n,r=e.data.action,i=e.data.range.start.row,s=e.data.range.end.row,o=e.data.range.start,u=e.data.range.end,a=null;r.indexOf("Lines")!=-1?(r=="insertLines"?s=i+e.data.lines.length:s=i,n=e.data.lines?e.data.lines.length:s-i):n=s-i,this.$updating=!0;if(n!=0)if(r.indexOf("remove")!=-1){this[t?"$wrapData":"$rowLengthCache"].splice(i,n);var f=this.$foldData;a=this.getFoldsInRange(e.data.range),this.removeFolds(a);var l=this.getFoldLine(u.row),c=0;if(l){l.addRemoveChars(u.row,u.column,o.column-u.column),l.shiftRow(-n);var h=this.getFoldLine(i);h&&h!==l&&(h.merge(l),l=h),c=f.indexOf(l)+1}for(c;c=u.row&&l.shiftRow(-n)}s=i}else{var p=Array(n);p.unshift(i,0);var d=t?this.$wrapData:this.$rowLengthCache;d.splice.apply(d,p);var f=this.$foldData,l=this.getFoldLine(i),c=0;if(l){var v=l.range.compareInside(o.row,o.column);v==0?(l=l.split(o.row,o.column),l&&(l.shiftRow(n),l.addRemoveChars(s,0,u.column-o.column))):v==-1&&(l.addRemoveChars(i,0,u.column-o.column),l.shiftRow(n)),c=f.indexOf(l)+1}for(c;c=i&&l.shiftRow(n)}}else{n=Math.abs(e.data.range.start.column-e.data.range.end.column),r.indexOf("remove")!=-1&&(a=this.getFoldsInRange(e.data.range),this.removeFolds(a),n=-n);var l=this.getFoldLine(i);l&&l.addRemoveChars(i,o.column,n)}return t&&this.$wrapData.length!=this.doc.getLength()&&console.error("doc.getLength() and $wrapData.length have to be the same!"),this.$updating=!1,t?this.$updateWrapData(i,s):this.$updateRowLengthCache(i,s),a},this.$updateRowLengthCache=function(e,t,n){this.$rowLengthCache[e]=null,this.$rowLengthCache[t]=null},this.$updateWrapData=function(e,t){var r=this.doc.getAllLines(),i=this.getTabSize(),s=this.$wrapData,o=this.$wrapLimit,a,f,l=e;t=Math.min(t,r.length-1);while(l<=t)f=this.getFoldLine(l,f),f?(a=[],f.walk(function(e,t,i,s){var o;if(e!=null){o=this.$getDisplayTokens(e,a.length),o[0]=n;for(var f=1;fr){var h=o+r;if(e[h-1]>=p&&e[h]>=p){c(h);continue}if(e[h]==n||e[h]==u){for(h;h!=o-1;h--)if(e[h]==n)break;if(h>o){c(h);continue}h=o+r;for(h;h>2)),o-1);while(h>d&&e[h]d&&e[h]d&&e[h]==l)h--}else while(h>d&&e[h]d){c(++h);continue}h=o+r,e[h]==t&&h--,c(h)}return i},this.$getDisplayTokens=function(n,r){var i=[],s;r=r||0;for(var o=0;o39&&u<48||u>57&&u<64?i.push(l):u>=4352&&m(u)?i.push(e,t):i.push(e)}return i},this.$getStringScreenWidth=function(e,t,n){if(t==0)return[0,0];t==null&&(t=Infinity),n=n||0;var r,i;for(i=0;i=4352&&m(r)?n+=2:n+=1;if(n>t)break}return[n,i]},this.lineWidgets=null,this.getRowLength=function(e){if(this.lineWidgets)var t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0;else t=0;return!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.getRowLineCount=function(e){return!this.$useWrapMode||!this.$wrapData[e]?1:this.$wrapData[e].length+1},this.getScreenLastRowColumn=function(e){var t=this.screenToDocumentPosition(e,Number.MAX_VALUE);return this.documentToScreenColumn(t.row,t.column)},this.getDocumentLastRowColumn=function(e,t){var n=this.documentToScreenRow(e,t);return this.getScreenLastRowColumn(n)},this.getDocumentLastRowColumnPosition=function(e,t){var n=this.documentToScreenRow(e,t);return this.screenToDocumentPosition(n,Number.MAX_VALUE/10)},this.getRowSplitData=function(e){return this.$useWrapMode?this.$wrapData[e]:undefined},this.getScreenTabSize=function(e){return this.$tabSize-e%this.$tabSize},this.screenToDocumentRow=function(e,t){return this.screenToDocumentPosition(e,t).row},this.screenToDocumentColumn=function(e,t){return this.screenToDocumentPosition(e,t).column},this.screenToDocumentPosition=function(e,t){if(e<0)return{row:0,column:0};var n,r=0,i=0,s,o=0,u=0,a=this.$screenRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var o=a[f],r=this.$docRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getLength()-1,p=this.getNextFoldLine(r),d=p?p.start.row:Infinity;while(o<=e){u=this.getRowLength(r);if(o+u>e||r>=h)break;o+=u,r++,r>d&&(r=p.end.row+1,p=this.getNextFoldLine(r,p),d=p?p.start.row:Infinity),c&&(this.$docRowCache.push(r),this.$screenRowCache.push(o))}if(p&&p.start.row<=r)n=this.getFoldDisplayLine(p),r=p.start.row;else{if(o+u<=e||r>h)return{row:h,column:this.getLine(h).length};n=this.getLine(r),p=null}if(this.$useWrapMode){var v=this.$wrapData[r];if(v){var m=Math.floor(e-o);s=v[m],m>0&&v.length&&(i=v[m-1]||v[v.length-1],n=n.substring(i))}}return i+=this.$getStringScreenWidth(n,t)[1],this.$useWrapMode&&i>=s&&(i=s-1),p?p.idxToPosition(i):{row:r,column:i}},this.documentToScreenPosition=function(e,t){if(typeof t=="undefined")var n=this.$clipPositionToDocument(e.row,e.column);else n=this.$clipPositionToDocument(e,t);e=n.row,t=n.column;var r=0,i=null,s=null;s=this.getFoldAt(e,t,1),s&&(e=s.start.row,t=s.start.column);var o,u=0,a=this.$docRowCache,f=this.$getRowCacheIndex(a,e),l=a.length;if(l&&f>=0)var u=a[f],r=this.$screenRowCache[f],c=e>a[l-1];else var c=!l;var h=this.getNextFoldLine(u),p=h?h.start.row:Infinity;while(u=p){o=h.end.row+1;if(o>e)break;h=this.getNextFoldLine(o,h),p=h?h.start.row:Infinity}else o=u+1;r+=this.getRowLength(u),u=o,c&&(this.$docRowCache.push(u),this.$screenRowCache.push(r))}var d="";h&&u>=p?(d=this.getFoldDisplayLine(h,e,t),i=h.start.row):(d=this.getLine(e).substring(0,t),i=e);if(this.$useWrapMode){var v=this.$wrapData[i];if(v){var m=0;while(d.length>=v[m])r++,m++;d=d.substring(v[m-1]||0,d.length)}}return{row:r,column:this.$getStringScreenWidth(d)[0]}},this.documentToScreenColumn=function(e,t){return this.documentToScreenPosition(e,t).column},this.documentToScreenRow=function(e,t){return this.documentToScreenPosition(e,t).row},this.getScreenLength=function(){var e=0,t=null;if(!this.$useWrapMode){e=this.getLength();var n=this.$foldData;for(var r=0;ro&&(s=t.end.row+1,t=this.$foldData[r++],o=t?t.start.row:Infinity)}}return this.lineWidgets&&(e+=this.$getWidgetScreenLength()),e},this.$setFontMetrics=function(e){},this.destroy=function(){this.bgTokenizer&&(this.bgTokenizer.setDocument(null),this.bgTokenizer=null),this.$stopWorker()}}).call(p.prototype),e("./edit_session/folding").Folding.call(p.prototype),e("./edit_session/bracket_match").BracketMatch.call(p.prototype),s.defineOptions(p.prototype,"session",{wrap:{set:function(e){!e||e=="off"?e=!1:e=="free"?e=!0:e=="printMargin"?e=-1:typeof e=="string"&&(e=parseInt(e,10)||!1);if(this.$wrap==e)return;if(!e)this.setUseWrapMode(!1);else{var t=typeof e=="number"?e:null;this.setWrapLimitRange(t,t),this.setUseWrapMode(!0)}this.$wrap=e},get:function(){return this.getUseWrapMode()?this.$wrap==-1?"printMargin":this.getWrapLimitRange().min?this.$wrap:"free":"off"},handlesSet:!0},wrapMethod:{set:function(e){e=e=="auto"?this.$mode.type!="text":e!="text",e!=this.$wrapAsCode&&(this.$wrapAsCode=e,this.$useWrapMode&&(this.$modified=!0,this.$resetRowCache(0),this.$updateWrapData(0,this.getLength()-1)))},initialValue:"auto"},firstLineNumber:{set:function(){this._signal("changeBreakpoint")},initialValue:1},useWorker:{set:function(e){this.$useWorker=e,this.$stopWorker(),e&&this.$startWorker()},initialValue:!0},useSoftTabs:{initialValue:!0},tabSize:{set:function(e){if(isNaN(e)||this.$tabSize===e)return;this.$modified=!0,this.$rowLengthCache=[],this.$tabSize=e,this._signal("changeTabSize")},initialValue:4,handlesSet:!0},overwrite:{set:function(e){this._signal("changeOverwrite")},initialValue:!1},newLineMode:{set:function(e){this.doc.setNewLineMode(e)},get:function(){return this.doc.getNewLineMode()},handlesSet:!0},mode:{set:function(e){this.setMode(e)},get:function(){return this.$modeId}}}),t.EditSession=p}),ace.define("ace/search",["require","exports","module","ace/lib/lang","ace/lib/oop","ace/range"],function(e,t,n){"use strict";var r=e("./lib/lang"),i=e("./lib/oop"),s=e("./range").Range,o=function(){this.$options={}};(function(){this.set=function(e){return i.mixin(this.$options,e),this},this.getOptions=function(){return r.copyObject(this.$options)},this.setOptions=function(e){this.$options=e},this.find=function(e){var t=this.$matchIterator(e,this.$options);if(!t)return!1;var n=null;return t.forEach(function(e,t,r){if(!e.start){var i=e.offset+(r||0);n=new s(t,i,t,i+e.length)}else n=e;return!0}),n},this.findAll=function(e){var t=this.$options;if(!t.needle)return[];this.$assembleRegExp(t);var n=t.range,i=n?e.getLines(n.start.row,n.end.row):e.doc.getAllLines(),o=[],u=t.re;if(t.$isMultiLine){var a=u.length,f=i.length-a,l;e:for(var c=u.offset||0;c<=f;c++){for(var h=0;hv)continue;o.push(l=new s(c,v,c+a-1,m)),a>2&&(c=c+a-2)}}else for(var g=0;gE&&o[h].end.row==n.end.row)h--;o=o.slice(g,h+1);for(g=0,h=o.length;g=0;u--)if(o(s[u],t,i))return!0};else var f=function(e,t,i){var s=r.getMatchOffsets(e,n);for(var u=0;u=o;r--)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=u,o=s.row;r>=o;r--)if(n(e.getLine(r),r))return}:function(n){var r=s.row,i=e.getLine(r).substr(s.column);if(n(i,r,s.column))return;for(r+=1;r<=u;r++)if(n(e.getLine(r),r))return;if(t.wrap==0)return;for(r=o,u=s.row;r<=u;r++)if(n(e.getLine(r),r))return};return{forEach:a}}}).call(o.prototype),t.Search=o}),ace.define("ace/keyboard/hash_handler",["require","exports","module","ace/lib/keys","ace/lib/useragent"],function(e,t,n){"use strict";function o(e,t){this.platform=t||(i.isMac?"mac":"win"),this.commands={},this.commandKeyBinding={},this.addCommands(e),this.$singleCommand=!0}function u(e,t){o.call(this,e,t),this.$singleCommand=!1}var r=e("../lib/keys"),i=e("../lib/useragent"),s=r.KEY_MODS;u.prototype=o.prototype,function(){this.addCommand=function(e){this.commands[e.name]&&this.removeCommand(e),this.commands[e.name]=e,e.bindKey&&this._buildKeyHash(e)},this.removeCommand=function(e,t){var n=e&&(typeof e=="string"?e:e.name);e=this.commands[n],t||delete this.commands[n];var r=this.commandKeyBinding;for(var i in r){var s=r[i];if(s==e)delete r[i];else if(Array.isArray(s)){var o=s.indexOf(e);o!=-1&&(s.splice(o,1),s.length==1&&(r[i]=s[0]))}}},this.bindKey=function(e,t,n){typeof e=="object"&&(e=e[this.platform]);if(!e)return;if(typeof t=="function")return this.addCommand({exec:t,bindKey:e,name:t.name||e});e.split("|").forEach(function(e){var r="";if(e.indexOf(" ")!=-1){var i=e.split(/\s+/);e=i.pop(),i.forEach(function(e){var t=this.parseKeys(e),n=s[t.hashId]+t.key;r+=(r?" ":"")+n,this._addCommandToBinding(r,"chainKeys")},this),r+=" "}var o=this.parseKeys(e),u=s[o.hashId]+o.key;this._addCommandToBinding(r+u,t,n)},this)},this._addCommandToBinding=function(e,t,n){var r=this.commandKeyBinding,i;t?!r[e]||this.$singleCommand?r[e]=t:(Array.isArray(r[e])?(i=r[e].indexOf(t))!=-1&&r[e].splice(i,1):r[e]=[r[e]],n||t.isDefault?r[e].unshift(t):r[e].push(t)):delete r[e]},this.addCommands=function(e){e&&Object.keys(e).forEach(function(t){var n=e[t];if(!n)return;if(typeof n=="string")return this.bindKey(n,t);typeof n=="function"&&(n={exec:n});if(typeof n!="object")return;n.name||(n.name=t),this.addCommand(n)},this)},this.removeCommands=function(e){Object.keys(e).forEach(function(t){this.removeCommand(e[t])},this)},this.bindKeys=function(e){Object.keys(e).forEach(function(t){this.bindKey(t,e[t])},this)},this._buildKeyHash=function(e){this.bindKey(e.bindKey,e)},this.parseKeys=function(e){var t=e.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(e){return e}),n=t.pop(),i=r[n];if(r.FUNCTION_KEYS[i])n=r.FUNCTION_KEYS[i].toLowerCase();else{if(!t.length)return{key:n,hashId:-1};if(t.length==1&&t[0]=="shift")return{key:n.toUpperCase(),hashId:-1}}var s=0;for(var o=t.length;o--;){var u=r.KEY_MODS[t[o]];if(u==null)return typeof console!="undefined"&&console.error("invalid modifier "+t[o]+" in "+e),!1;s|=u}return{key:n,hashId:s}},this.findKeyCommand=function(t,n){var r=s[t]+n;return this.commandKeyBinding[r]},this.handleKeyboard=function(e,t,n,r){var i=s[t]+n,o=this.commandKeyBinding[i];e.$keyChain&&(e.$keyChain+=" "+i,o=this.commandKeyBinding[e.$keyChain]||o);if(o)if(o=="chainKeys"||o[o.length-1]=="chainKeys")return e.$keyChain=e.$keyChain||i,{command:"null"};return e.$keyChain&&r>0&&(e.$keyChain=""),{command:o}}}.call(o.prototype),t.HashHandler=o,t.MultiHashHandler=u}),ace.define("ace/commands/command_manager",["require","exports","module","ace/lib/oop","ace/keyboard/hash_handler","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../keyboard/hash_handler").MultiHashHandler,s=e("../lib/event_emitter").EventEmitter,o=function(e,t){i.call(this,t,e),this.byName=this.commands,this.setDefaultHandler("exec",function(e){return e.command.exec(e.editor,e.args||{})})};r.inherits(o,i),function(){r.implement(this,s),this.exec=function(e,t,n){if(Array.isArray(e)){for(var r=e.length;r--;)if(this.exec(e[r],t,n))return!0;return!1}typeof e=="string"&&(e=this.commands[e]);if(!e)return!1;if(t&&t.$readOnly&&!e.readOnly)return!1;var i={editor:t,command:e,args:n};return i.returnValue=this._emit("exec",i),this._signal("afterExec",i),i.returnValue===!1?!1:!0},this.toggleRecording=function(e){if(this.$inReplay)return;return e&&e._emit("changeStatus"),this.recording?(this.macro.pop(),this.removeEventListener("exec",this.$addCommandToMacro),this.macro.length||(this.macro=this.oldMacro),this.recording=!1):(this.$addCommandToMacro||(this.$addCommandToMacro=function(e){this.macro.push([e.command,e.args])}.bind(this)),this.oldMacro=this.macro,this.macro=[],this.on("exec",this.$addCommandToMacro),this.recording=!0)},this.replay=function(e){if(this.$inReplay||!this.macro)return;if(this.recording)return this.toggleRecording(e);try{this.$inReplay=!0,this.macro.forEach(function(t){typeof t=="string"?this.exec(t,e):this.exec(t[0],e,t[1])},this)}finally{this.$inReplay=!1}},this.trimMacro=function(e){return e.map(function(e){return typeof e[0]!="string"&&(e[0]=e[0].name),e[1]||(e=e[0]),e})}}.call(o.prototype),t.CommandManager=o}),ace.define("ace/commands/default_commands",["require","exports","module","ace/lib/lang","ace/config","ace/range"],function(e,t,n){"use strict";function o(e,t){return{win:e,mac:t}}var r=e("../lib/lang"),i=e("../config"),s=e("../range").Range;t.commands=[{name:"showSettingsMenu",bindKey:o("Ctrl-,","Command-,"),exec:function(e){i.loadModule("ace/ext/settings_menu",function(t){t.init(e),e.showSettingsMenu()})},readOnly:!0},{name:"goToNextError",bindKey:o("Alt-E","Ctrl-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,1)})},scrollIntoView:"animate",readOnly:!0},{name:"goToPreviousError",bindKey:o("Alt-Shift-E","Ctrl-Shift-E"),exec:function(e){i.loadModule("ace/ext/error_marker",function(t){t.showErrorMarker(e,-1)})},scrollIntoView:"animate",readOnly:!0},{name:"selectall",bindKey:o("Ctrl-A","Command-A"),exec:function(e){e.selectAll()},readOnly:!0},{name:"centerselection",bindKey:o(null,"Ctrl-L"),exec:function(e){e.centerSelection()},readOnly:!0},{name:"gotoline",bindKey:o("Ctrl-L","Command-L"),exec:function(e){var t=parseInt(prompt("Enter line number:"),10);isNaN(t)||e.gotoLine(t)},readOnly:!0},{name:"fold",bindKey:o("Alt-L|Ctrl-F1","Command-Alt-L|Command-F1"),exec:function(e){e.session.toggleFold(!1)},scrollIntoView:"center",readOnly:!0},{name:"unfold",bindKey:o("Alt-Shift-L|Ctrl-Shift-F1","Command-Alt-Shift-L|Command-Shift-F1"),exec:function(e){e.session.toggleFold(!0)},scrollIntoView:"center",readOnly:!0},{name:"toggleFoldWidget",bindKey:o("F2","F2"),exec:function(e){e.session.toggleFoldWidget()},scrollIntoView:"center",readOnly:!0},{name:"toggleParentFoldWidget",bindKey:o("Alt-F2","Alt-F2"),exec:function(e){e.session.toggleFoldWidget(!0)},scrollIntoView:"center",readOnly:!0},{name:"foldall",bindKey:o("Ctrl-Alt-0","Ctrl-Command-Option-0"),exec:function(e){e.session.foldAll()},scrollIntoView:"center",readOnly:!0},{name:"foldOther",bindKey:o("Alt-0","Command-Option-0"),exec:function(e){e.session.foldAll(),e.session.unfold(e.selection.getAllRanges())},scrollIntoView:"center",readOnly:!0},{name:"unfoldall",bindKey:o("Alt-Shift-0","Command-Option-Shift-0"),exec:function(e){e.session.unfold()},scrollIntoView:"center",readOnly:!0},{name:"findnext",bindKey:o("Ctrl-K","Command-G"),exec:function(e){e.findNext()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"findprevious",bindKey:o("Ctrl-Shift-K","Command-Shift-G"),exec:function(e){e.findPrevious()},multiSelectAction:"forEach",scrollIntoView:"center",readOnly:!0},{name:"selectOrFindNext",bindKey:o("Alt-K","Ctrl-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findNext()},readOnly:!0},{name:"selectOrFindPrevious",bindKey:o("Alt-Shift-K","Ctrl-Shift-G"),exec:function(e){e.selection.isEmpty()?e.selection.selectWord():e.findPrevious()},readOnly:!0},{name:"find",bindKey:o("Ctrl-F","Command-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e)})},readOnly:!0},{name:"overwrite",bindKey:"Insert",exec:function(e){e.toggleOverwrite()},readOnly:!0},{name:"selecttostart",bindKey:o("Ctrl-Shift-Home","Command-Shift-Up"),exec:function(e){e.getSelection().selectFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotostart",bindKey:o("Ctrl-Home","Command-Home|Command-Up"),exec:function(e){e.navigateFileStart()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectup",bindKey:o("Shift-Up","Shift-Up"),exec:function(e){e.getSelection().selectUp()},multiSelectAction:"forEach",readOnly:!0},{name:"golineup",bindKey:o("Up","Up|Ctrl-P"),exec:function(e,t){e.navigateUp(t.times)},multiSelectAction:"forEach",readOnly:!0},{name:"selecttoend",bindKey:o("Ctrl-Shift-End","Command-Shift-Down"),exec:function(e){e.getSelection().selectFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"gotoend",bindKey:o("Ctrl-End","Command-End|Command-Down"),exec:function(e){e.navigateFileEnd()},multiSelectAction:"forEach",readOnly:!0,scrollIntoView:"animate",aceCommandGroup:"fileJump"},{name:"selectdown",bindKey:o("Shift-Down","Shift-Down"),exec:function(e){e.getSelection().selectDown()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"golinedown",bindKey:o("Down","Down|Ctrl-N"),exec:function(e,t){e.navigateDown(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordleft",bindKey:o("Ctrl-Shift-Left","Option-Shift-Left"),exec:function(e){e.getSelection().selectWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordleft",bindKey:o("Ctrl-Left","Option-Left"),exec:function(e){e.navigateWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolinestart",bindKey:o("Alt-Shift-Left","Command-Shift-Left"),exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolinestart",bindKey:o("Alt-Left|Home","Command-Left|Home|Ctrl-A"),exec:function(e){e.navigateLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectleft",bindKey:o("Shift-Left","Shift-Left"),exec:function(e){e.getSelection().selectLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoleft",bindKey:o("Left","Left|Ctrl-B"),exec:function(e,t){e.navigateLeft(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectwordright",bindKey:o("Ctrl-Shift-Right","Option-Shift-Right"),exec:function(e){e.getSelection().selectWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotowordright",bindKey:o("Ctrl-Right","Option-Right"),exec:function(e){e.navigateWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selecttolineend",bindKey:o("Alt-Shift-Right","Command-Shift-Right"),exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotolineend",bindKey:o("Alt-Right|End","Command-Right|End|Ctrl-E"),exec:function(e){e.navigateLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectright",bindKey:o("Shift-Right","Shift-Right"),exec:function(e){e.getSelection().selectRight()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"gotoright",bindKey:o("Right","Right|Ctrl-F"),exec:function(e,t){e.navigateRight(t.times)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectpagedown",bindKey:"Shift-PageDown",exec:function(e){e.selectPageDown()},readOnly:!0},{name:"pagedown",bindKey:o(null,"Option-PageDown"),exec:function(e){e.scrollPageDown()},readOnly:!0},{name:"gotopagedown",bindKey:o("PageDown","PageDown|Ctrl-V"),exec:function(e){e.gotoPageDown()},readOnly:!0},{name:"selectpageup",bindKey:"Shift-PageUp",exec:function(e){e.selectPageUp()},readOnly:!0},{name:"pageup",bindKey:o(null,"Option-PageUp"),exec:function(e){e.scrollPageUp()},readOnly:!0},{name:"gotopageup",bindKey:"PageUp",exec:function(e){e.gotoPageUp()},readOnly:!0},{name:"scrollup",bindKey:o("Ctrl-Up",null),exec:function(e){e.renderer.scrollBy(0,-2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"scrolldown",bindKey:o("Ctrl-Down",null),exec:function(e){e.renderer.scrollBy(0,2*e.renderer.layerConfig.lineHeight)},readOnly:!0},{name:"selectlinestart",bindKey:"Shift-Home",exec:function(e){e.getSelection().selectLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"selectlineend",bindKey:"Shift-End",exec:function(e){e.getSelection().selectLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"togglerecording",bindKey:o("Ctrl-Alt-E","Command-Option-E"),exec:function(e){e.commands.toggleRecording(e)},readOnly:!0},{name:"replaymacro",bindKey:o("Ctrl-Shift-E","Command-Shift-E"),exec:function(e){e.commands.replay(e)},readOnly:!0},{name:"jumptomatching",bindKey:o("Ctrl-P","Ctrl-P"),exec:function(e){e.jumpToMatching()},multiSelectAction:"forEach",readOnly:!0},{name:"selecttomatching",bindKey:o("Ctrl-Shift-P","Ctrl-Shift-P"),exec:function(e){e.jumpToMatching(!0)},multiSelectAction:"forEach",readOnly:!0},{name:"passKeysToBrowser",bindKey:o("null","null"),exec:function(){},passEvent:!0,readOnly:!0},{name:"cut",exec:function(e){var t=e.getSelectionRange();e._emit("cut",t),e.selection.isEmpty()||(e.session.remove(t),e.clearSelection())},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"removeline",bindKey:o("Ctrl-D","Command-D"),exec:function(e){e.removeLines()},scrollIntoView:"cursor",multiSelectAction:"forEachLine"},{name:"duplicateSelection",bindKey:o("Ctrl-Shift-D","Command-Shift-D"),exec:function(e){e.duplicateSelection()},scrollIntoView:"cursor",multiSelectAction:"forEach"},{name:"sortlines",bindKey:o("Ctrl-Alt-S","Command-Alt-S"),exec:function(e){e.sortLines()},scrollIntoView:"selection",multiSelectAction:"forEachLine"},{name:"togglecomment",bindKey:o("Ctrl-/","Command-/"),exec:function(e){e.toggleCommentLines()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"toggleBlockComment",bindKey:o("Ctrl-Shift-/","Command-Shift-/"),exec:function(e){e.toggleBlockComment()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"modifyNumberUp",bindKey:o("Ctrl-Shift-Up","Alt-Shift-Up"),exec:function(e){e.modifyNumber(1)},multiSelectAction:"forEach"},{name:"modifyNumberDown",bindKey:o("Ctrl-Shift-Down","Alt-Shift-Down"),exec:function(e){e.modifyNumber(-1)},multiSelectAction:"forEach"},{name:"replace",bindKey:o("Ctrl-H","Command-Option-F"),exec:function(e){i.loadModule("ace/ext/searchbox",function(t){t.Search(e,!0)})}},{name:"undo",bindKey:o("Ctrl-Z","Command-Z"),exec:function(e){e.undo()}},{name:"redo",bindKey:o("Ctrl-Shift-Z|Ctrl-Y","Command-Shift-Z|Command-Y"),exec:function(e){e.redo()}},{name:"copylinesup",bindKey:o("Alt-Shift-Up","Command-Option-Up"),exec:function(e){e.copyLinesUp()},scrollIntoView:"cursor"},{name:"movelinesup",bindKey:o("Alt-Up","Option-Up"),exec:function(e){e.moveLinesUp()},scrollIntoView:"cursor"},{name:"copylinesdown",bindKey:o("Alt-Shift-Down","Command-Option-Down"),exec:function(e){e.copyLinesDown()},scrollIntoView:"cursor"},{name:"movelinesdown",bindKey:o("Alt-Down","Option-Down"),exec:function(e){e.moveLinesDown()},scrollIntoView:"cursor"},{name:"del",bindKey:o("Delete","Delete|Ctrl-D|Shift-Delete"),exec:function(e){e.remove("right")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"backspace",bindKey:o("Shift-Backspace|Backspace","Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"),exec:function(e){e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"cut_or_delete",bindKey:o("Shift-Delete",null),exec:function(e){if(!e.selection.isEmpty())return!1;e.remove("left")},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolinestart",bindKey:o("Alt-Backspace","Command-Backspace"),exec:function(e){e.removeToLineStart()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removetolineend",bindKey:o("Alt-Delete","Ctrl-K"),exec:function(e){e.removeToLineEnd()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordleft",bindKey:o("Ctrl-Backspace","Alt-Backspace|Ctrl-Alt-Backspace"),exec:function(e){e.removeWordLeft()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"removewordright",bindKey:o("Ctrl-Delete","Alt-Delete"),exec:function(e){e.removeWordRight()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"outdent",bindKey:o("Shift-Tab","Shift-Tab"),exec:function(e){e.blockOutdent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"indent",bindKey:o("Tab","Tab"),exec:function(e){e.indent()},multiSelectAction:"forEach",scrollIntoView:"selectionPart"},{name:"blockoutdent",bindKey:o("Ctrl-[","Ctrl-["),exec:function(e){e.blockOutdent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"blockindent",bindKey:o("Ctrl-]","Ctrl-]"),exec:function(e){e.blockIndent()},multiSelectAction:"forEachLine",scrollIntoView:"selectionPart"},{name:"insertstring",exec:function(e,t){e.insert(t)},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"inserttext",exec:function(e,t){e.insert(r.stringRepeat(t.text||"",t.times||1))},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"splitline",bindKey:o(null,"Ctrl-O"),exec:function(e){e.splitLine()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"transposeletters",bindKey:o("Ctrl-T","Ctrl-T"),exec:function(e){e.transposeLetters()},multiSelectAction:function(e){e.transposeSelections(1)},scrollIntoView:"cursor"},{name:"touppercase",bindKey:o("Ctrl-U","Ctrl-U"),exec:function(e){e.toUpperCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"tolowercase",bindKey:o("Ctrl-Shift-U","Ctrl-Shift-U"),exec:function(e){e.toLowerCase()},multiSelectAction:"forEach",scrollIntoView:"cursor"},{name:"expandtoline",bindKey:o("Ctrl-Shift-L","Command-Shift-L"),exec:function(e){var t=e.selection.getRange();t.start.column=t.end.column=0,t.end.row++,e.selection.setRange(t,!1)},multiSelectAction:"forEach",scrollIntoView:"cursor",readOnly:!0},{name:"joinlines",bindKey:o(null,null),exec:function(e){var t=e.selection.isBackwards(),n=t?e.selection.getSelectionLead():e.selection.getSelectionAnchor(),i=t?e.selection.getSelectionAnchor():e.selection.getSelectionLead(),o=e.session.doc.getLine(n.row).length,u=e.session.doc.getTextRange(e.selection.getRange()),a=u.replace(/\n\s*/," ").length,f=e.session.doc.getLine(n.row);for(var l=n.row+1;l<=i.row+1;l++){var c=r.stringTrimLeft(r.stringTrimRight(e.session.doc.getLine(l)));c.length!==0&&(c=" "+c),f+=c}i.row+10?(e.selection.moveCursorTo(n.row,n.column),e.selection.selectTo(n.row,n.column+a)):(o=e.session.doc.getLine(n.row).length>o?o+1:o,e.selection.moveCursorTo(n.row,o))},multiSelectAction:"forEach",readOnly:!0},{name:"invertSelection",bindKey:o(null,null),exec:function(e){var t=e.session.doc.getLength()-1,n=e.session.doc.getLine(t).length,r=e.selection.rangeList.ranges,i=[];r.length<1&&(r=[e.selection.getRange()]);for(var o=0;o=r.lastRow||n.end.row<=r.firstRow)&&this.renderer.scrollSelectionIntoView(this.selection.anchor,this.selection.lead);break;default:}t.scrollIntoView=="animate"&&this.renderer.animateScrolling(this.curOp.scrollTop)}this.prevOp=this.curOp,this.curOp=null}},this.$mergeableCommands=["backspace","del","insertstring"],this.$historyTracker=function(e){if(!this.$mergeUndoDeltas)return;var t=this.prevOp,n=this.$mergeableCommands,r=t.command&&e.command.name==t.command.name;if(e.command.name=="insertstring"){var i=e.args;this.mergeNextCommand===undefined&&(this.mergeNextCommand=!0),r=r&&this.mergeNextCommand&&(!/\s/.test(i)||/\s/.test(t.args)),this.mergeNextCommand=!0}else r=r&&n.indexOf(e.command.name)!==-1;this.$mergeUndoDeltas!="always"&&Date.now()-this.sequenceStartTime>2e3&&(r=!1),r?this.session.mergeUndoDeltas=!0:n.indexOf(e.command.name)!==-1&&(this.sequenceStartTime=Date.now())},this.setKeyboardHandler=function(e,t){if(e&&typeof e=="string"){this.$keybindingId=e;var n=this;g.loadModule(["keybinding",e],function(r){n.$keybindingId==e&&n.keyBinding.setKeyboardHandler(r&&r.handler),t&&t()})}else this.$keybindingId=null,this.keyBinding.setKeyboardHandler(e),t&&t()},this.getKeyboardHandler=function(){return this.keyBinding.getKeyboardHandler()},this.setSession=function(e){if(this.session==e)return;var t=this.session;if(t){this.session.removeEventListener("change",this.$onDocumentChange),this.session.removeEventListener("changeMode",this.$onChangeMode),this.session.removeEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.session.removeEventListener("changeTabSize",this.$onChangeTabSize),this.session.removeEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.session.removeEventListener("changeWrapMode",this.$onChangeWrapMode),this.session.removeEventListener("onChangeFold",this.$onChangeFold),this.session.removeEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.session.removeEventListener("changeBackMarker",this.$onChangeBackMarker),this.session.removeEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.session.removeEventListener("changeAnnotation",this.$onChangeAnnotation),this.session.removeEventListener("changeOverwrite",this.$onCursorChange),this.session.removeEventListener("changeScrollTop",this.$onScrollTopChange),this.session.removeEventListener("changeScrollLeft",this.$onScrollLeftChange);var n=this.session.getSelection();n.removeEventListener("changeCursor",this.$onCursorChange),n.removeEventListener("changeSelection",this.$onSelectionChange)}this.session=e,e?(this.$onDocumentChange=this.onDocumentChange.bind(this),e.addEventListener("change",this.$onDocumentChange),this.renderer.setSession(e),this.$onChangeMode=this.onChangeMode.bind(this),e.addEventListener("changeMode",this.$onChangeMode),this.$onTokenizerUpdate=this.onTokenizerUpdate.bind(this),e.addEventListener("tokenizerUpdate",this.$onTokenizerUpdate),this.$onChangeTabSize=this.renderer.onChangeTabSize.bind(this.renderer),e.addEventListener("changeTabSize",this.$onChangeTabSize),this.$onChangeWrapLimit=this.onChangeWrapLimit.bind(this),e.addEventListener("changeWrapLimit",this.$onChangeWrapLimit),this.$onChangeWrapMode=this.onChangeWrapMode.bind(this),e.addEventListener("changeWrapMode",this.$onChangeWrapMode),this.$onChangeFold=this.onChangeFold.bind(this),e.addEventListener("changeFold",this.$onChangeFold),this.$onChangeFrontMarker=this.onChangeFrontMarker.bind(this),this.session.addEventListener("changeFrontMarker",this.$onChangeFrontMarker),this.$onChangeBackMarker=this.onChangeBackMarker.bind(this),this.session.addEventListener("changeBackMarker",this.$onChangeBackMarker),this.$onChangeBreakpoint=this.onChangeBreakpoint.bind(this),this.session.addEventListener("changeBreakpoint",this.$onChangeBreakpoint),this.$onChangeAnnotation=this.onChangeAnnotation.bind(this),this.session.addEventListener("changeAnnotation",this.$onChangeAnnotation),this.$onCursorChange=this.onCursorChange.bind(this),this.session.addEventListener("changeOverwrite",this.$onCursorChange),this.$onScrollTopChange=this.onScrollTopChange.bind(this),this.session.addEventListener("changeScrollTop",this.$onScrollTopChange),this.$onScrollLeftChange=this.onScrollLeftChange.bind(this),this.session.addEventListener("changeScrollLeft",this.$onScrollLeftChange),this.selection=e.getSelection(),this.selection.addEventListener("changeCursor",this.$onCursorChange),this.$onSelectionChange=this.onSelectionChange.bind(this),this.selection.addEventListener("changeSelection",this.$onSelectionChange),this.onChangeMode(),this.$blockScrolling+=1,this.onCursorChange(),this.$blockScrolling-=1,this.onScrollTopChange(),this.onScrollLeftChange(),this.onSelectionChange(),this.onChangeFrontMarker(),this.onChangeBackMarker(),this.onChangeBreakpoint(),this.onChangeAnnotation(),this.session.getUseWrapMode()&&this.renderer.adjustWrapLimit(),this.renderer.updateFull()):(this.selection=null,this.renderer.setSession(e)),this._signal("changeSession",{session:e,oldSession:t}),t&&t._signal("changeEditor",{oldEditor:this}),e&&e._signal("changeEditor",{editor:this})},this.getSession=function(){return this.session},this.setValue=function(e,t){return this.session.doc.setValue(e),t?t==1?this.navigateFileEnd():t==-1&&this.navigateFileStart():this.selectAll(),e},this.getValue=function(){return this.session.getValue()},this.getSelection=function(){return this.selection},this.resize=function(e){this.renderer.onResize(e)},this.setTheme=function(e,t){this.renderer.setTheme(e,t)},this.getTheme=function(){return this.renderer.getTheme()},this.setStyle=function(e){this.renderer.setStyle(e)},this.unsetStyle=function(e){this.renderer.unsetStyle(e)},this.getFontSize=function(){return this.getOption("fontSize")||i.computedStyle(this.container,"fontSize")},this.setFontSize=function(e){this.setOption("fontSize",e)},this.$highlightBrackets=function(){this.session.$bracketHighlight&&(this.session.removeMarker(this.session.$bracketHighlight),this.session.$bracketHighlight=null);if(this.$highlightPending)return;var e=this;this.$highlightPending=!0,setTimeout(function(){e.$highlightPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=t.findMatchingBracket(e.getCursorPosition());if(n)var r=new p(n.row,n.column,n.row,n.column+1);else if(t.$mode.getMatching)var r=t.$mode.getMatching(e.session);r&&(t.$bracketHighlight=t.addMarker(r,"ace_bracket","text"))},50)},this.$highlightTags=function(){if(this.$highlightTagPending)return;var e=this;this.$highlightTagPending=!0,setTimeout(function(){e.$highlightTagPending=!1;var t=e.session;if(!t||!t.bgTokenizer)return;var n=e.getCursorPosition(),r=new y(e.session,n.row,n.column),i=r.getCurrentToken();if(!i||i.type.indexOf("tag-name")===-1){t.removeMarker(t.$tagHighlight),t.$tagHighlight=null;return}var s=i.value,o=0,u=r.stepBackward();if(u.value=="<"){do u=i,i=r.stepForward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="=0)}else{do i=u,u=r.stepBackward(),i&&i.value===s&&i.type.indexOf("tag-name")!==-1&&(u.value==="<"?o++:u.value==="1)&&(t=!1)}if(e.$highlightLineMarker&&!t)e.removeMarker(e.$highlightLineMarker.id),e.$highlightLineMarker=null;else if(!e.$highlightLineMarker&&t){var n=new p(t.row,t.column,t.row,Infinity);n.id=e.addMarker(n,"ace_active-line","screenLine"),e.$highlightLineMarker=n}else t&&(e.$highlightLineMarker.start.row=t.row,e.$highlightLineMarker.end.row=t.row,e.$highlightLineMarker.start.column=t.column,e._signal("changeBackMarker"))},this.onSelectionChange=function(e){var t=this.session;t.$selectionMarker&&t.removeMarker(t.$selectionMarker),t.$selectionMarker=null;if(!this.selection.isEmpty()){var n=this.selection.getRange(),r=this.getSelectionStyle();t.$selectionMarker=t.addMarker(n,"ace_selection",r)}else this.$updateHighlightActiveLine();var i=this.$highlightSelectedWord&&this.$getSelectionHighLightRegexp();this.session.highlight(i),this._signal("changeSelection")},this.$getSelectionHighLightRegexp=function(){var e=this.session,t=this.getSelectionRange();if(t.isEmpty()||t.isMultiLine())return;var n=t.start.column-1,r=t.end.column+1,i=e.getLine(t.start.row),s=i.length,o=i.substring(Math.max(n,0),Math.min(r,s));if(n>=0&&/^[\w\d]/.test(o)||r<=s&&/[\w\d]$/.test(o))return;o=i.substring(t.start.column,t.end.column);if(!/^[\w\d]+$/.test(o))return;var u=this.$search.$assembleRegExp({wholeWord:!0,caseSensitive:!0,needle:o});return u},this.onChangeFrontMarker=function(){this.renderer.updateFrontMarkers()},this.onChangeBackMarker=function(){this.renderer.updateBackMarkers()},this.onChangeBreakpoint=function(){this.renderer.updateBreakpoints()},this.onChangeAnnotation=function(){this.renderer.setAnnotations(this.session.getAnnotations())},this.onChangeMode=function(e){this.renderer.updateText(),this._emit("changeMode",e)},this.onChangeWrapLimit=function(){this.renderer.updateFull()},this.onChangeWrapMode=function(){this.renderer.onResize(!0)},this.onChangeFold=function(){this.$updateHighlightActiveLine(),this.renderer.updateFull()},this.getSelectedText=function(){return this.session.getTextRange(this.getSelectionRange())},this.getCopyText=function(){var e=this.getSelectedText();return this._signal("copy",e),e},this.onCopy=function(){this.commands.exec("copy",this)},this.onCut=function(){this.commands.exec("cut",this)},this.onPaste=function(e){if(this.$readOnly)return;var t={text:e};this._signal("paste",t),this.insert(t.text,!0)},this.execCommand=function(e,t){return this.commands.exec(e,this,t)},this.insert=function(e,t){var n=this.session,r=n.getMode(),i=this.getCursorPosition();if(this.getBehavioursEnabled()&&!t){var s=r.transformAction(n.getState(i.row),"insertion",this,n,e);s&&(e!==s.text&&(this.session.mergeUndoDeltas=!1,this.$mergeNextCommand=!1),e=s.text)}e==" "&&(e=this.session.getTabString());if(!this.selection.isEmpty()){var o=this.getSelectionRange();i=this.session.remove(o),this.clearSelection()}else if(this.session.getOverwrite()){var o=new p.fromPoints(i,i);o.end.column+=e.length,this.session.remove(o)}if(e=="\n"||e=="\r\n"){var u=n.getLine(i.row);if(i.column>u.search(/\S|$/)){var a=u.substr(i.column).search(/\S|$/);n.doc.removeInLine(i.row,i.column,i.column+a)}}this.clearSelection();var f=i.column,l=n.getState(i.row),u=n.getLine(i.row),c=r.checkOutdent(l,u,e),h=n.insert(i,e);s&&s.selection&&(s.selection.length==2?this.selection.setSelectionRange(new p(i.row,f+s.selection[0],i.row,f+s.selection[1])):this.selection.setSelectionRange(new p(i.row+s.selection[0],s.selection[1],i.row+s.selection[2],s.selection[3])));if(n.getDocument().isNewLine(e)){var d=r.getNextLineIndent(l,u.slice(0,i.column),n.getTabString());n.insert({row:i.row+1,column:0},d)}c&&r.autoOutdent(l,n,i.row)},this.onTextInput=function(e){this.keyBinding.onTextInput(e)},this.onCommandKey=function(e,t,n){this.keyBinding.onCommandKey(e,t,n)},this.setOverwrite=function(e){this.session.setOverwrite(e)},this.getOverwrite=function(){return this.session.getOverwrite()},this.toggleOverwrite=function(){this.session.toggleOverwrite()},this.setScrollSpeed=function(e){this.setOption("scrollSpeed",e)},this.getScrollSpeed=function(){return this.getOption("scrollSpeed")},this.setDragDelay=function(e){this.setOption("dragDelay",e)},this.getDragDelay=function(){return this.getOption("dragDelay")},this.setSelectionStyle=function(e){this.setOption("selectionStyle",e)},this.getSelectionStyle=function(){return this.getOption("selectionStyle")},this.setHighlightActiveLine=function(e){this.setOption("highlightActiveLine",e)},this.getHighlightActiveLine=function(){return this.getOption("highlightActiveLine")},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.setHighlightSelectedWord=function(e){this.setOption("highlightSelectedWord",e)},this.getHighlightSelectedWord=function(){return this.$highlightSelectedWord},this.setAnimatedScroll=function(e){this.renderer.setAnimatedScroll(e)},this.getAnimatedScroll=function(){return this.renderer.getAnimatedScroll()},this.setShowInvisibles=function(e){this.renderer.setShowInvisibles(e)},this.getShowInvisibles=function(){return this.renderer.getShowInvisibles()},this.setDisplayIndentGuides=function(e){this.renderer.setDisplayIndentGuides(e)},this.getDisplayIndentGuides=function(){return this.renderer.getDisplayIndentGuides()},this.setShowPrintMargin=function(e){this.renderer.setShowPrintMargin(e)},this.getShowPrintMargin=function(){return this.renderer.getShowPrintMargin()},this.setPrintMarginColumn=function(e){this.renderer.setPrintMarginColumn(e)},this.getPrintMarginColumn=function(){return this.renderer.getPrintMarginColumn()},this.setReadOnly=function(e){this.setOption("readOnly",e)},this.getReadOnly=function(){return this.getOption("readOnly")},this.setBehavioursEnabled=function(e){this.setOption("behavioursEnabled",e)},this.getBehavioursEnabled=function(){return this.getOption("behavioursEnabled")},this.setWrapBehavioursEnabled=function(e){this.setOption("wrapBehavioursEnabled",e)},this.getWrapBehavioursEnabled=function(){return this.getOption("wrapBehavioursEnabled")},this.setShowFoldWidgets=function(e){this.setOption("showFoldWidgets",e)},this.getShowFoldWidgets=function(){return this.getOption("showFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.remove=function(e){this.selection.isEmpty()&&(e=="left"?this.selection.selectLeft():this.selection.selectRight());var t=this.getSelectionRange();if(this.getBehavioursEnabled()){var n=this.session,r=n.getState(t.start.row),i=n.getMode().transformAction(r,"deletion",this,n,t);if(t.end.column===0){var s=n.getTextRange(t);if(s[s.length-1]=="\n"){var o=n.getLine(t.end.row);/^\s+$/.test(o)&&(t.end.column=o.length)}}i&&(t=i)}this.session.remove(t),this.clearSelection()},this.removeWordRight=function(){this.selection.isEmpty()&&this.selection.selectWordRight(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeWordLeft=function(){this.selection.isEmpty()&&this.selection.selectWordLeft(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineStart=function(){this.selection.isEmpty()&&this.selection.selectLineStart(),this.session.remove(this.getSelectionRange()),this.clearSelection()},this.removeToLineEnd=function(){this.selection.isEmpty()&&this.selection.selectLineEnd();var e=this.getSelectionRange();e.start.column==e.end.column&&e.start.row==e.end.row&&(e.end.column=0,e.end.row++),this.session.remove(e),this.clearSelection()},this.splitLine=function(){this.selection.isEmpty()||(this.session.remove(this.getSelectionRange()),this.clearSelection());var e=this.getCursorPosition();this.insert("\n"),this.moveCursorToPosition(e)},this.transposeLetters=function(){if(!this.selection.isEmpty())return;var e=this.getCursorPosition(),t=e.column;if(t===0)return;var n=this.session.getLine(e.row),r,i;tt.toLowerCase()?1:0});var r=new p(0,0,0,0);for(var i=e.first;i<=e.last;i++){var s=t.getLine(i);r.start.row=i,r.end.row=i,r.end.column=s.length,t.replace(r,n[i-e.first])}},this.toggleCommentLines=function(){var e=this.session.getState(this.getCursorPosition().row),t=this.$getSelectedRows();this.session.getMode().toggleCommentLines(e,this.session,t.first,t.last)},this.toggleBlockComment=function(){var e=this.getCursorPosition(),t=this.session.getState(e.row),n=this.getSelectionRange();this.session.getMode().toggleBlockComment(t,this.session,n,e)},this.getNumberAt=function(e,t){var n=/[\-]?[0-9]+(?:\.[0-9]+)?/g;n.lastIndex=0;var r=this.session.getLine(e);while(n.lastIndex=t){var s={value:i[0],start:i.index,end:i.index+i[0].length};return s}}return null},this.modifyNumber=function(e){var t=this.selection.getCursor().row,n=this.selection.getCursor().column,r=new p(t,n-1,t,n),i=this.session.getTextRange(r);if(!isNaN(parseFloat(i))&&isFinite(i)){var s=this.getNumberAt(t,n);if(s){var o=s.value.indexOf(".")>=0?s.start+s.value.indexOf(".")+1:s.end,u=s.start+s.value.length-o,a=parseFloat(s.value);a*=Math.pow(10,u),o!==s.end&&n=o)s[u].moveBy(i,0),u--}t.fromOrientedRange(t.ranges[0]),t.rangeList.attach(this.session)}},this.$getSelectedRows=function(){var e=this.getSelectionRange().collapseRows();return{first:this.session.getRowFoldStart(e.start.row),last:this.session.getRowFoldEnd(e.end.row)}},this.onCompositionStart=function(e){this.renderer.showComposition(this.getCursorPosition())},this.onCompositionUpdate=function(e){this.renderer.setCompositionText(e)},this.onCompositionEnd=function(){this.renderer.hideComposition()},this.getFirstVisibleRow=function(){return this.renderer.getFirstVisibleRow()},this.getLastVisibleRow=function(){return this.renderer.getLastVisibleRow()},this.isRowVisible=function(e){return e>=this.getFirstVisibleRow()&&e<=this.getLastVisibleRow()},this.isRowFullyVisible=function(e){return e>=this.renderer.getFirstFullyVisibleRow()&&e<=this.renderer.getLastFullyVisibleRow()},this.$getVisibleRowCount=function(){return this.renderer.getScrollBottomRow()-this.renderer.getScrollTopRow()+1},this.$moveByPage=function(e,t){var n=this.renderer,r=this.renderer.layerConfig,i=e*Math.floor(r.height/r.lineHeight);this.$blockScrolling++,t===!0?this.selection.$moveSelection(function(){this.moveCursorBy(i,0)}):t===!1&&(this.selection.moveCursorBy(i,0),this.selection.clearSelection()),this.$blockScrolling--;var s=n.scrollTop;n.scrollBy(0,i*r.lineHeight),t!=null&&n.scrollCursorIntoView(null,.5),n.animateScrolling(s)},this.selectPageDown=function(){this.$moveByPage(1,!0)},this.selectPageUp=function(){this.$moveByPage(-1,!0)},this.gotoPageDown=function(){this.$moveByPage(1,!1)},this.gotoPageUp=function(){this.$moveByPage(-1,!1)},this.scrollPageDown=function(){this.$moveByPage(1)},this.scrollPageUp=function(){this.$moveByPage(-1)},this.scrollToRow=function(e){this.renderer.scrollToRow(e)},this.scrollToLine=function(e,t,n,r){this.renderer.scrollToLine(e,t,n,r)},this.centerSelection=function(){var e=this.getSelectionRange(),t={row:Math.floor(e.start.row+(e.end.row-e.start.row)/2),column:Math.floor(e.start.column+(e.end.column-e.start.column)/2)};this.renderer.alignCursor(t,.5)},this.getCursorPosition=function(){return this.selection.getCursor()},this.getCursorPositionScreen=function(){return this.session.documentToScreenPosition(this.getCursorPosition())},this.getSelectionRange=function(){return this.selection.getRange()},this.selectAll=function(){this.$blockScrolling+=1,this.selection.selectAll(),this.$blockScrolling-=1},this.clearSelection=function(){this.selection.clearSelection()},this.moveCursorTo=function(e,t){this.selection.moveCursorTo(e,t)},this.moveCursorToPosition=function(e){this.selection.moveCursorToPosition(e)},this.jumpToMatching=function(e,t){var n=this.getCursorPosition(),r=new y(this.session,n.row,n.column),i=r.getCurrentToken(),s=i||r.stepForward();if(!s)return;var o,u=!1,a={},f=n.column-s.start,l,c={")":"(","(":"(","]":"[","[":"[","{":"{","}":"{"};do{if(s.value.match(/[{}()\[\]]/g))for(;f=0;--s)this.$tryReplace(n[s],e)&&r++;return this.selection.setSelectionRange(i),this.$blockScrolling-=1,r},this.$tryReplace=function(e,t){var n=this.session.getTextRange(e);return t=this.$search.replace(n,t),t!==null?(e.end=this.session.replace(e,t),e):null},this.getLastSearchOptions=function(){return this.$search.getOptions()},this.find=function(e,t,n){t||(t={}),typeof e=="string"||e instanceof RegExp?t.needle=e:typeof e=="object"&&r.mixin(t,e);var i=this.selection.getRange();t.needle==null&&(e=this.session.getTextRange(i)||this.$search.$options.needle,e||(i=this.session.getWordRange(i.start.row,i.start.column),e=this.session.getTextRange(i)),this.$search.set({needle:e})),this.$search.set(t),t.start||this.$search.set({start:i});var s=this.$search.find(this.session);if(t.preventScroll)return s;if(s)return this.revealRange(s,n),s;t.backwards?i.start=i.end:i.end=i.start,this.selection.setRange(i)},this.findNext=function(e,t){this.find({skipCurrent:!0,backwards:!1},e,t)},this.findPrevious=function(e,t){this.find(e,{skipCurrent:!0,backwards:!0},t)},this.revealRange=function(e,t){this.$blockScrolling+=1,this.session.unfold(e),this.selection.setSelectionRange(e),this.$blockScrolling-=1;var n=this.renderer.scrollTop;this.renderer.scrollSelectionIntoView(e.start,e.end,.5),t!==!1&&this.renderer.animateScrolling(n)},this.undo=function(){this.$blockScrolling++,this.session.getUndoManager().undo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.redo=function(){this.$blockScrolling++,this.session.getUndoManager().redo(),this.$blockScrolling--,this.renderer.scrollCursorIntoView(null,.5)},this.destroy=function(){this.renderer.destroy(),this._signal("destroy",this),this.session&&this.session.destroy()},this.setAutoScrollEditorIntoView=function(e){if(!e)return;var t,n=this,r=!1;this.$scrollAnchor||(this.$scrollAnchor=document.createElement("div"));var i=this.$scrollAnchor;i.style.cssText="position:absolute",this.container.insertBefore(i,this.container.firstChild);var s=this.on("changeSelection",function(){r=!0}),o=this.renderer.on("beforeRender",function(){r&&(t=n.renderer.container.getBoundingClientRect())}),u=this.renderer.on("afterRender",function(){if(r&&t&&(n.isFocused()||n.searchBox&&n.searchBox.isFocused())){var e=n.renderer,s=e.$cursorLayer.$pixelPos,o=e.layerConfig,u=s.top-o.offset;s.top>=0&&u+t.top<0?r=!0:s.topwindow.innerHeight?r=!1:r=null,r!=null&&(i.style.top=u+"px",i.style.left=s.left+"px",i.style.height=o.lineHeight+"px",i.scrollIntoView(r)),r=t=null}});this.setAutoScrollEditorIntoView=function(e){if(e)return;delete this.setAutoScrollEditorIntoView,this.removeEventListener("changeSelection",s),this.renderer.removeEventListener("afterRender",u),this.renderer.removeEventListener("beforeRender",o)}},this.$resetCursorStyle=function(){var e=this.$cursorStyle||"ace",t=this.renderer.$cursorLayer;if(!t)return;t.setSmoothBlinking(/smooth/.test(e)),t.isBlinking=!this.$readOnly&&e!="wide",i.setCssClass(t.element,"ace_slim-cursors",/slim/.test(e))}}).call(b.prototype),g.defineOptions(b.prototype,"editor",{selectionStyle:{set:function(e){this.onSelectionChange(),this._signal("changeSelectionStyle",{data:e})},initialValue:"line"},highlightActiveLine:{set:function(){this.$updateHighlightActiveLine()},initialValue:!0},highlightSelectedWord:{set:function(e){this.$onSelectionChange()},initialValue:!0},readOnly:{set:function(e){this.$resetCursorStyle()},initialValue:!1},cursorStyle:{set:function(e){this.$resetCursorStyle()},values:["ace","slim","smooth","wide"],initialValue:"ace"},mergeUndoDeltas:{values:[!1,!0,"always"],initialValue:!0},behavioursEnabled:{initialValue:!0},wrapBehavioursEnabled:{initialValue:!0},autoScrollEditorIntoView:{set:function(e){this.setAutoScrollEditorIntoView(e)}},hScrollBarAlwaysVisible:"renderer",vScrollBarAlwaysVisible:"renderer",highlightGutterLine:"renderer",animatedScroll:"renderer",showInvisibles:"renderer",showPrintMargin:"renderer",printMarginColumn:"renderer",printMargin:"renderer",fadeFoldWidgets:"renderer",showFoldWidgets:"renderer",showLineNumbers:"renderer",showGutter:"renderer",displayIndentGuides:"renderer",fontSize:"renderer",fontFamily:"renderer",maxLines:"renderer",minLines:"renderer",scrollPastEnd:"renderer",fixedWidthGutter:"renderer",theme:"renderer",scrollSpeed:"$mouseHandler",dragDelay:"$mouseHandler",dragEnabled:"$mouseHandler",focusTimout:"$mouseHandler",tooltipFollowsMouse:"$mouseHandler",firstLineNumber:"session",overwrite:"session",newLineMode:"session",useWorker:"session",useSoftTabs:"session",tabSize:"session",wrap:"session",foldStyle:"session",mode:"session"}),t.Editor=b}),ace.define("ace/undomanager",["require","exports","module"],function(e,t,n){"use strict";var r=function(){this.reset()};(function(){this.execute=function(e){var t=e.args[0];this.$doc=e.args[1],e.merge&&this.hasUndo()&&(this.dirtyCounter--,t=this.$undoStack.pop().concat(t)),this.$undoStack.push(t),this.$redoStack=[],this.dirtyCounter<0&&(this.dirtyCounter=NaN),this.dirtyCounter++},this.undo=function(e){var t=this.$undoStack.pop(),n=null;return t&&(n=this.$doc.undoChanges(t,e),this.$redoStack.push(t),this.dirtyCounter--),n},this.redo=function(e){var t=this.$redoStack.pop(),n=null;return t&&(n=this.$doc.redoChanges(t,e),this.$undoStack.push(t),this.dirtyCounter++),n},this.reset=function(){this.$undoStack=[],this.$redoStack=[],this.dirtyCounter=0},this.hasUndo=function(){return this.$undoStack.length>0},this.hasRedo=function(){return this.$redoStack.length>0},this.markClean=function(){this.dirtyCounter=0},this.isClean=function(){return this.dirtyCounter===0}}).call(r.prototype),t.UndoManager=r}),ace.define("ace/layer/gutter",["require","exports","module","ace/lib/dom","ace/lib/oop","ace/lib/lang","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/dom"),i=e("../lib/oop"),s=e("../lib/lang"),o=e("../lib/event_emitter").EventEmitter,u=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_gutter-layer",e.appendChild(this.element),this.setShowFoldWidgets(this.$showFoldWidgets),this.gutterWidth=0,this.$annotations=[],this.$updateAnnotations=this.$updateAnnotations.bind(this),this.$cells=[]};(function(){i.implement(this,o),this.setSession=function(e){this.session&&this.session.removeEventListener("change",this.$updateAnnotations),this.session=e,e&&e.on("change",this.$updateAnnotations)},this.addGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.addGutterDecoration"),this.session.addGutterDecoration(e,t)},this.removeGutterDecoration=function(e,t){window.console&&console.warn&&console.warn("deprecated use session.removeGutterDecoration"),this.session.removeGutterDecoration(e,t)},this.setAnnotations=function(e){this.$annotations=[];for(var t=0;to&&(v=s.end.row+1,s=t.getNextFoldLine(v,s),o=s?s.start.row:Infinity);if(v>i){while(this.$cells.length>d+1)p=this.$cells.pop(),this.element.removeChild(p.element);break}p=this.$cells[++d],p||(p={element:null,textNode:null,foldWidget:null},p.element=r.createElement("div"),p.textNode=document.createTextNode(""),p.element.appendChild(p.textNode),this.element.appendChild(p.element),this.$cells[d]=p);var m="ace_gutter-cell ";a[v]&&(m+=a[v]),f[v]&&(m+=f[v]),this.$annotations[v]&&(m+=this.$annotations[v].className),p.element.className!=m&&(p.element.className=m);var g=t.getRowLength(v)*e.lineHeight+"px";g!=p.element.style.height&&(p.element.style.height=g);if(u){var y=u[v];y==null&&(y=u[v]=t.getFoldWidget(v))}if(y){p.foldWidget||(p.foldWidget=r.createElement("span"),p.element.appendChild(p.foldWidget));var m="ace_fold-widget ace_"+y;y=="start"&&v==o&&vn.right-t.right)return"foldWidgets"}}).call(u.prototype),t.Gutter=u}),ace.define("ace/layer/marker",["require","exports","module","ace/range","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../range").Range,i=e("../lib/dom"),s=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_marker-layer",e.appendChild(this.element)};(function(){this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setMarkers=function(e){this.markers=e},this.update=function(e){var e=e||this.config;if(!e)return;this.config=e;var t=[];for(var n in this.markers){var r=this.markers[n];if(!r.range){r.update(t,this,this.session,e);continue}var i=r.range.clipRows(e.firstRow,e.lastRow);if(i.isEmpty())continue;i=i.toScreenRange(this.session);if(r.renderer){var s=this.$getTop(i.start.row,e),o=this.$padding+i.start.column*e.characterWidth;r.renderer(t,i,o,s,e)}else r.type=="fullLine"?this.drawFullLineMarker(t,i,r.clazz,e):r.type=="screenLine"?this.drawScreenLineMarker(t,i,r.clazz,e):i.isMultiLine()?r.type=="text"?this.drawTextMarker(t,i,r.clazz,e):this.drawMultiLineMarker(t,i,r.clazz,e):this.drawSingleLineMarker(t,i,r.clazz+" ace_start",e)}this.element.innerHTML=t.join("")},this.$getTop=function(e,t){return(e-t.firstRowScreen)*t.lineHeight},this.drawTextMarker=function(e,t,n,i,s){var o=t.start.row,u=new r(o,t.start.column,o,this.session.getScreenLastRowColumn(o));this.drawSingleLineMarker(e,u,n+" ace_start",i,1,s),o=t.end.row,u=new r(o,0,o,t.end.column),this.drawSingleLineMarker(e,u,n,i,0,s);for(o=t.start.row+1;o"),u=this.$getTop(t.end.row,r);var f=t.end.column*r.characterWidth;e.push("
"),o=(t.end.row-t.start.row-1)*r.lineHeight;if(o<0)return;u=this.$getTop(t.start.row+1,r),e.push("
")},this.drawSingleLineMarker=function(e,t,n,r,i,s){var o=r.lineHeight,u=(t.end.column+(i||0)-t.start.column)*r.characterWidth,a=this.$getTop(t.start.row,r),f=this.$padding+t.start.column*r.characterWidth;e.push("
")},this.drawFullLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;t.start.row!=t.end.row&&(o+=this.$getTop(t.end.row,r)-s),e.push("
")},this.drawScreenLineMarker=function(e,t,n,r,i){var s=this.$getTop(t.start.row,r),o=r.lineHeight;e.push("
")}}).call(s.prototype),t.Marker=s}),ace.define("ace/layer/text",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=function(e){this.element=i.createElement("div"),this.element.className="ace_layer ace_text-layer",e.appendChild(this.element),this.$updateEolChar=this.$updateEolChar.bind(this)};(function(){r.implement(this,u),this.EOF_CHAR="\u00b6",this.EOL_CHAR_LF="\u00ac",this.EOL_CHAR_CRLF="\u00a4",this.EOL_CHAR=this.EOL_CHAR_LF,this.TAB_CHAR="\u2192",this.SPACE_CHAR="\u00b7",this.$padding=0,this.$updateEolChar=function(){var e=this.session.doc.getNewLineCharacter()=="\n"?this.EOL_CHAR_LF:this.EOL_CHAR_CRLF;if(this.EOL_CHAR!=e)return this.EOL_CHAR=e,!0},this.setPadding=function(e){this.$padding=e,this.element.style.padding="0 "+e+"px"},this.getLineHeight=function(){return this.$fontMetrics.$characterSize.height||0},this.getCharacterWidth=function(){return this.$fontMetrics.$characterSize.width||0},this.$setFontMetrics=function(e){this.$fontMetrics=e,this.$fontMetrics.on("changeCharacterSize",function(e){this._signal("changeCharacterSize",e)}.bind(this)),this.$pollSizeChanges()},this.checkForSizeChanges=function(){this.$fontMetrics.checkForSizeChanges()},this.$pollSizeChanges=function(){return this.$pollSizeChangesTimer=this.$fontMetrics.$pollSizeChanges()},this.setSession=function(e){this.session=e,e&&this.$computeTabString()},this.showInvisibles=!1,this.setShowInvisibles=function(e){return this.showInvisibles==e?!1:(this.showInvisibles=e,this.$computeTabString(),!0)},this.displayIndentGuides=!0,this.setDisplayIndentGuides=function(e){return this.displayIndentGuides==e?!1:(this.displayIndentGuides=e,this.$computeTabString(),!0)},this.$tabStrings=[],this.onChangeTabSize=this.$computeTabString=function(){var e=this.session.getTabSize();this.tabSize=e;var t=this.$tabStrings=[0];for(var n=1;n"+this.TAB_CHAR+s.stringRepeat("\u00a0",n-1)+""):t.push(s.stringRepeat("\u00a0",n));if(this.displayIndentGuides){this.$indentGuideRe=/\s\S| \t|\t |\s$/;var r="ace_indent-guide",i="",o="";if(this.showInvisibles){r+=" ace_invisible",i=" ace_invisible_space",o=" ace_invisible_tab";var u=s.stringRepeat(this.SPACE_CHAR,this.tabSize),a=this.TAB_CHAR+s.stringRepeat("\u00a0",this.tabSize-1)}else var u=s.stringRepeat("\u00a0",this.tabSize),a=u;this.$tabStrings[" "]=""+u+"",this.$tabStrings[" "]=""+a+""}},this.updateLines=function(e,t,n){(this.config.lastRow!=e.lastRow||this.config.firstRow!=e.firstRow)&&this.scrollLines(e),this.config=e;var r=Math.max(t,e.firstRow),i=Math.min(n,e.lastRow),s=this.element.childNodes,o=0;for(var u=e.firstRow;uf&&(u=a.end.row+1,a=this.session.getNextFoldLine(u,a),f=a?a.start.row:Infinity);if(u>i)break;var l=s[o++];if(l){var c=[];this.$renderLine(c,u,!this.$useLineGroups(),u==f?a:!1),l.style.height=e.lineHeight*this.session.getRowLength(u)+"px",l.innerHTML=c.join("")}u++}},this.scrollLines=function(e){var t=this.config;this.config=e;if(!t||t.lastRow0;r--)n.removeChild(n.firstChild);if(t.lastRow>e.lastRow)for(var r=this.session.getFoldedRowCount(e.lastRow+1,t.lastRow);r>0;r--)n.removeChild(n.lastChild);if(e.firstRowt.lastRow){var i=this.$renderLinesFragment(e,t.lastRow+1,e.lastRow);n.appendChild(i)}},this.$renderLinesFragment=function(e,t,n){var r=this.element.ownerDocument.createDocumentFragment(),s=t,o=this.session.getNextFoldLine(s),u=o?o.start.row:Infinity;for(;;){s>u&&(s=o.end.row+1,o=this.session.getNextFoldLine(s,o),u=o?o.start.row:Infinity);if(s>n)break;var a=i.createElement("div"),f=[];this.$renderLine(f,s,!1,s==u?o:!1),a.innerHTML=f.join("");if(this.$useLineGroups())a.className="ace_line_group",r.appendChild(a),a.style.height=e.lineHeight*this.session.getRowLength(s)+"px";else while(a.firstChild)r.appendChild(a.firstChild);s++}return r},this.update=function(e){this.config=e;var t=[],n=e.firstRow,r=e.lastRow,i=n,s=this.session.getNextFoldLine(i),o=s?s.start.row:Infinity;for(;;){i>o&&(i=s.end.row+1,s=this.session.getNextFoldLine(i,s),o=s?s.start.row:Infinity);if(i>r)break;this.$useLineGroups()&&t.push("
"),this.$renderLine(t,i,!1,i==o?s:!1),this.$useLineGroups()&&t.push("
"),i++}this.element.innerHTML=t.join("")},this.$textToken={text:!0,rparen:!0,lparen:!0},this.$renderToken=function(e,t,n,r){var i=this,o=/\t|&|<|( +)|([\x00-\x1f\x80-\xa0\xad\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g,u=function(e,n,r,o,u){if(n)return i.showInvisibles?""+s.stringRepeat(i.SPACE_CHAR,e.length)+"":s.stringRepeat("\u00a0",e.length);if(e=="&")return"&";if(e=="<")return"<";if(e==" "){var a=i.session.getScreenTabSize(t+o);return t+=a-1,i.$tabStrings[a]}if(e=="\u3000"){var f=i.showInvisibles?"ace_cjk ace_invisible ace_invisible_space":"ace_cjk",l=i.showInvisibles?i.SPACE_CHAR:"";return t+=1,""+l+""}return r?""+i.SPACE_CHAR+"":(t+=1,""+e+"")},a=r.replace(o,u);if(!this.$textToken[n.type]){var f="ace_"+n.type.replace(/\./g," ace_"),l="";n.type=="fold"&&(l=" style='width:"+n.value.length*this.config.characterWidth+"px;' "),e.push("",a,"")}else e.push(a);return t+r.length},this.renderIndentGuide=function(e,t,n){var r=t.search(this.$indentGuideRe);return r<=0||r>=n?t:t[0]==" "?(r-=r%this.tabSize,e.push(s.stringRepeat(this.$tabStrings[" "],r/this.tabSize)),t.substr(r)):t[0]==" "?(e.push(s.stringRepeat(this.$tabStrings[" "],r)),t.substr(r)):t},this.$renderWrappedLine=function(e,t,n,r){var i=0,s=0,o=n[0],u=0;for(var a=0;a=o)u=this.$renderToken(e,u,f,l.substring(0,o-i)),l=l.substring(o-i),i=o,r||e.push("","
"),s++,u=0,o=n[s]||Number.MAX_VALUE;l.length!=0&&(i+=l.length,u=this.$renderToken(e,u,f,l))}}},this.$renderSimpleLine=function(e,t){var n=0,r=t[0],i=r.value;this.displayIndentGuides&&(i=this.renderIndentGuide(e,i)),i&&(n=this.$renderToken(e,n,r,i));for(var s=1;s");if(i.length){var s=this.session.getRowSplitData(t);s&&s.length?this.$renderWrappedLine(e,i,s,n):this.$renderSimpleLine(e,i)}this.showInvisibles&&(r&&(t=r.end.row),e.push("",t==this.session.getLength()-1?this.EOF_CHAR:this.EOL_CHAR,"")),n||e.push("
")},this.$getFoldLineTokens=function(e,t){function i(e,t,n){var i=0,s=0;while(s+e[i].value.lengthn-t&&(o=o.substring(0,n-t)),r.push({type:e[i].type,value:o}),s=t+o.length,i+=1}while(sn?r.push({type:e[i].type,value:o.substring(0,n-s)}):r.push(e[i]),s+=o.length,i+=1}}var n=this.session,r=[],s=n.getTokens(e);return t.walk(function(e,t,o,u,a){e!=null?r.push({type:"fold",value:e}):(a&&(s=n.getTokens(t)),s.length&&i(s,u,o))},t.end.row,this.session.getLine(t.end.row).length),r},this.$useLineGroups=function(){return this.session.getUseWrapMode()},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.$measureNode&&this.$measureNode.parentNode.removeChild(this.$measureNode),delete this.$measureNode}}).call(a.prototype),t.Text=a}),ace.define("ace/layer/cursor",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";var r=e("../lib/dom"),i,s=function(e){this.element=r.createElement("div"),this.element.className="ace_layer ace_cursor-layer",e.appendChild(this.element),i===undefined&&(i="opacity"in this.element),this.isVisible=!1,this.isBlinking=!0,this.blinkInterval=1e3,this.smoothBlinking=!1,this.cursors=[],this.cursor=this.addCursor(),r.addCssClass(this.element,"ace_hidden-cursors"),this.$updateCursors=this.$updateVisibility.bind(this)};(function(){this.$updateVisibility=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.visibility=e?"":"hidden"},this.$updateOpacity=function(e){var t=this.cursors;for(var n=t.length;n--;)t[n].style.opacity=e?"":"0"},this.$padding=0,this.setPadding=function(e){this.$padding=e},this.setSession=function(e){this.session=e},this.setBlinking=function(e){e!=this.isBlinking&&(this.isBlinking=e,this.restartTimer())},this.setBlinkInterval=function(e){e!=this.blinkInterval&&(this.blinkInterval=e,this.restartTimer())},this.setSmoothBlinking=function(e){e!=this.smoothBlinking&&!i&&(this.smoothBlinking=e,r.setCssClass(this.element,"ace_smooth-blinking",e),this.$updateCursors(!0),this.$updateCursors=(e?this.$updateOpacity:this.$updateVisibility).bind(this),this.restartTimer())},this.addCursor=function(){var e=r.createElement("div");return e.className="ace_cursor",this.element.appendChild(e),this.cursors.push(e),e},this.removeCursor=function(){if(this.cursors.length>1){var e=this.cursors.pop();return e.parentNode.removeChild(e),e}},this.hideCursor=function(){this.isVisible=!1,r.addCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.showCursor=function(){this.isVisible=!0,r.removeCssClass(this.element,"ace_hidden-cursors"),this.restartTimer()},this.restartTimer=function(){var e=this.$updateCursors;clearInterval(this.intervalId),clearTimeout(this.timeoutId),this.smoothBlinking&&r.removeCssClass(this.element,"ace_smooth-blinking"),e(!0);if(!this.isBlinking||!this.blinkInterval||!this.isVisible)return;this.smoothBlinking&&setTimeout(function(){r.addCssClass(this.element,"ace_smooth-blinking")}.bind(this));var t=function(){this.timeoutId=setTimeout(function(){e(!1)},.6*this.blinkInterval)}.bind(this);this.intervalId=setInterval(function(){e(!0),t()},this.blinkInterval),t()},this.getPixelPosition=function(e,t){if(!this.config||!this.session)return{left:0,top:0};e||(e=this.session.selection.getCursor());var n=this.session.documentToScreenPosition(e),r=this.$padding+n.column*this.config.characterWidth,i=(n.row-(t?this.config.firstRowScreen:0))*this.config.lineHeight;return{left:r,top:i}},this.update=function(e){this.config=e;var t=this.session.$selectionMarkers,n=0,r=0;if(t===undefined||t.length===0)t=[{cursor:null}];for(var n=0,i=t.length;ne.height+e.offset||s.top<0)&&n>1)continue;var o=(this.cursors[r++]||this.addCursor()).style;o.left=s.left+"px",o.top=s.top+"px",o.width=e.characterWidth+"px",o.height=e.lineHeight+"px"}while(this.cursors.length>r)this.removeCursor();var u=this.session.getOverwrite();this.$setOverwrite(u),this.$pixelPos=s,this.restartTimer()},this.$setOverwrite=function(e){e!=this.overwrite&&(this.overwrite=e,e?r.addCssClass(this.element,"ace_overwrite-cursors"):r.removeCssClass(this.element,"ace_overwrite-cursors"))},this.destroy=function(){clearInterval(this.intervalId),clearTimeout(this.timeoutId)}}).call(s.prototype),t.Cursor=s}),ace.define("ace/scrollbar",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/event","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./lib/event"),o=e("./lib/event_emitter").EventEmitter,u=function(e){this.element=i.createElement("div"),this.element.className="ace_scrollbar ace_scrollbar"+this.classSuffix,this.inner=i.createElement("div"),this.inner.className="ace_scrollbar-inner",this.element.appendChild(this.inner),e.appendChild(this.element),this.setVisible(!1),this.skipEvent=!1,s.addListener(this.element,"scroll",this.onScroll.bind(this)),s.addListener(this.element,"mousedown",s.preventDefault)};(function(){r.implement(this,o),this.setVisible=function(e){this.element.style.display=e?"":"none",this.isVisible=e}}).call(u.prototype);var a=function(e,t){u.call(this,e),this.scrollTop=0,t.$scrollbarWidth=this.width=i.scrollbarWidth(e.ownerDocument),this.inner.style.width=this.element.style.width=(this.width||15)+5+"px"};r.inherits(a,u),function(){this.classSuffix="-v",this.onScroll=function(){this.skipEvent||(this.scrollTop=this.element.scrollTop,this._emit("scroll",{data:this.scrollTop})),this.skipEvent=!1},this.getWidth=function(){return this.isVisible?this.width:0},this.setHeight=function(e){this.element.style.height=e+"px"},this.setInnerHeight=function(e){this.inner.style.height=e+"px"},this.setScrollHeight=function(e){this.inner.style.height=e+"px"},this.setScrollTop=function(e){this.scrollTop!=e&&(this.skipEvent=!0,this.scrollTop=this.element.scrollTop=e)}}.call(a.prototype);var f=function(e,t){u.call(this,e),this.scrollLeft=0,this.height=t.$scrollbarWidth,this.inner.style.height=this.element.style.height=(this.height||15)+5+"px"};r.inherits(f,u),function(){this.classSuffix="-h",this.onScroll=function(){this.skipEvent||(this.scrollLeft=this.element.scrollLeft,this._emit("scroll",{data:this.scrollLeft})),this.skipEvent=!1},this.getHeight=function(){return this.isVisible?this.height:0},this.setWidth=function(e){this.element.style.width=e+"px"},this.setInnerWidth=function(e){this.inner.style.width=e+"px"},this.setScrollWidth=function(e){this.inner.style.width=e+"px"},this.setScrollLeft=function(e){this.scrollLeft!=e&&(this.skipEvent=!0,this.scrollLeft=this.element.scrollLeft=e)}}.call(f.prototype),t.ScrollBar=a,t.ScrollBarV=a,t.ScrollBarH=f,t.VScrollBar=a,t.HScrollBar=f}),ace.define("ace/renderloop",["require","exports","module","ace/lib/event"],function(e,t,n){"use strict";var r=e("./lib/event"),i=function(e,t){this.onRender=e,this.pending=!1,this.changes=0,this.window=t||window};(function(){this.schedule=function(e){this.changes=this.changes|e;if(!this.pending&&this.changes){this.pending=!0;var t=this;r.nextFrame(function(){t.pending=!1;var e;while(e=t.changes)t.changes=0,t.onRender(e)},this.window)}}}).call(i.prototype),t.RenderLoop=i}),ace.define("ace/layer/font_metrics",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/lib/lang","ace/lib/useragent","ace/lib/event_emitter"],function(e,t,n){var r=e("../lib/oop"),i=e("../lib/dom"),s=e("../lib/lang"),o=e("../lib/useragent"),u=e("../lib/event_emitter").EventEmitter,a=0,f=t.FontMetrics=function(e,t){this.el=i.createElement("div"),this.$setMeasureNodeStyles(this.el.style,!0),this.$main=i.createElement("div"),this.$setMeasureNodeStyles(this.$main.style),this.$measureNode=i.createElement("div"),this.$setMeasureNodeStyles(this.$measureNode.style),this.el.appendChild(this.$main),this.el.appendChild(this.$measureNode),e.appendChild(this.el),a||this.$testFractionalRect(),this.$measureNode.innerHTML=s.stringRepeat("X",a),this.$characterSize={width:0,height:0},this.checkForSizeChanges()};(function(){r.implement(this,u),this.$characterSize={width:0,height:0},this.$testFractionalRect=function(){var e=i.createElement("div");this.$setMeasureNodeStyles(e.style),e.style.width="0.2px",document.documentElement.appendChild(e);var t=e.getBoundingClientRect().width;t>0&&t<1?a=50:a=100,e.parentNode.removeChild(e)},this.$setMeasureNodeStyles=function(e,t){e.width=e.height="auto",e.left=e.top="-100px",e.visibility="hidden",e.position="fixed",e.whiteSpace="pre",o.isIE<8?e["font-family"]="inherit":e.font="inherit",e.overflow=t?"hidden":"visible"},this.checkForSizeChanges=function(){var e=this.$measureSizes();if(e&&(this.$characterSize.width!==e.width||this.$characterSize.height!==e.height)){this.$measureNode.style.fontWeight="bold";var t=this.$measureSizes();this.$measureNode.style.fontWeight="",this.$characterSize=e,this.charSizes=Object.create(null),this.allowBoldFonts=t&&t.width===e.width&&t.height===e.height,this._emit("changeCharacterSize",{data:e})}},this.$pollSizeChanges=function(){if(this.$pollSizeChangesTimer)return this.$pollSizeChangesTimer;var e=this;return this.$pollSizeChangesTimer=setInterval(function(){e.checkForSizeChanges()},500)},this.setPolling=function(e){e?this.$pollSizeChanges():this.$pollSizeChangesTimer&&this.$pollSizeChangesTimer},this.$measureSizes=function(){if(a===50){var e=null;try{e=this.$measureNode.getBoundingClientRect()}catch(t){e={width:0,height:0}}var n={height:e.height,width:e.width/a}}else var n={height:this.$measureNode.clientHeight,width:this.$measureNode.clientWidth/a};return n.width===0||n.height===0?null:n},this.$measureCharWidth=function(e){this.$main.innerHTML=s.stringRepeat(e,a);var t=this.$main.getBoundingClientRect();return t.width/a},this.getCharacterWidth=function(e){var t=this.charSizes[e];return t===undefined&&(this.charSizes[e]=this.$measureCharWidth(e)/this.$characterSize.width),t},this.destroy=function(){clearInterval(this.$pollSizeChangesTimer),this.el&&this.el.parentNode&&this.el.parentNode.removeChild(this.el)}}).call(f.prototype)}),ace.define("ace/virtual_renderer",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/config","ace/lib/useragent","ace/layer/gutter","ace/layer/marker","ace/layer/text","ace/layer/cursor","ace/scrollbar","ace/scrollbar","ace/renderloop","ace/layer/font_metrics","ace/lib/event_emitter"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./config"),o=e("./lib/useragent"),u=e("./layer/gutter").Gutter,a=e("./layer/marker").Marker,f=e("./layer/text").Text,l=e("./layer/cursor").Cursor,c=e("./scrollbar").HScrollBar,h=e("./scrollbar").VScrollBar,p=e("./renderloop").RenderLoop,d=e("./layer/font_metrics").FontMetrics,v=e("./lib/event_emitter").EventEmitter,m='.ace_editor {position: relative;overflow: hidden;font: 12px/normal \'Monaco\', \'Menlo\', \'Ubuntu Mono\', \'Consolas\', \'source-code-pro\', monospace;direction: ltr;}.ace_scroller {position: absolute;overflow: hidden;top: 0;bottom: 0;background-color: inherit;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;cursor: text;}.ace_content {position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;min-width: 100%;}.ace_dragging .ace_scroller:before{position: absolute;top: 0;left: 0;right: 0;bottom: 0;content: \'\';background: rgba(250, 250, 250, 0.01);z-index: 1000;}.ace_dragging.ace_dark .ace_scroller:before{background: rgba(0, 0, 0, 0.01);}.ace_selecting, .ace_selecting * {cursor: text !important;}.ace_gutter {position: absolute;overflow : hidden;width: auto;top: 0;bottom: 0;left: 0;cursor: default;z-index: 4;-ms-user-select: none;-moz-user-select: none;-webkit-user-select: none;user-select: none;}.ace_gutter-active-line {position: absolute;left: 0;right: 0;}.ace_scroller.ace_scroll-left {box-shadow: 17px 0 16px -16px rgba(0, 0, 0, 0.4) inset;}.ace_gutter-cell {padding-left: 19px;padding-right: 6px;background-repeat: no-repeat;}.ace_gutter-cell.ace_error {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: 2px center;}.ace_gutter-cell.ace_warning {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");background-position: 2px center;}.ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");background-position: 2px center;}.ace_dark .ace_gutter-cell.ace_info {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");}.ace_scrollbar {position: absolute;right: 0;bottom: 0;z-index: 6;}.ace_scrollbar-inner {position: absolute;cursor: text;left: 0;top: 0;}.ace_scrollbar-v{overflow-x: hidden;overflow-y: scroll;top: 0;}.ace_scrollbar-h {overflow-x: scroll;overflow-y: hidden;left: 0;}.ace_print-margin {position: absolute;height: 100%;}.ace_text-input {position: absolute;z-index: 0;width: 0.5em;height: 1em;opacity: 0;background: transparent;-moz-appearance: none;appearance: none;border: none;resize: none;outline: none;overflow: hidden;font: inherit;padding: 0 1px;margin: 0 -1px;text-indent: -1em;-ms-user-select: text;-moz-user-select: text;-webkit-user-select: text;user-select: text;}.ace_text-input.ace_composition {background: inherit;color: inherit;z-index: 1000;opacity: 1;text-indent: 0;}.ace_layer {z-index: 1;position: absolute;overflow: hidden;white-space: pre;height: 100%;width: 100%;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;pointer-events: none;}.ace_gutter-layer {position: relative;width: auto;text-align: right;pointer-events: auto;}.ace_text-layer {font: inherit !important;}.ace_cjk {display: inline-block;text-align: center;}.ace_cursor-layer {z-index: 4;}.ace_cursor {z-index: 4;position: absolute;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;border-left: 2px solid}.ace_slim-cursors .ace_cursor {border-left-width: 1px;}.ace_overwrite-cursors .ace_cursor {border-left-width: 0;border-bottom: 1px solid;}.ace_hidden-cursors .ace_cursor {opacity: 0.2;}.ace_smooth-blinking .ace_cursor {-webkit-transition: opacity 0.18s;transition: opacity 0.18s;}.ace_editor.ace_multiselect .ace_cursor {border-left-width: 1px;}.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {position: absolute;z-index: 3;}.ace_marker-layer .ace_selection {position: absolute;z-index: 5;}.ace_marker-layer .ace_bracket {position: absolute;z-index: 6;}.ace_marker-layer .ace_active-line {position: absolute;z-index: 2;}.ace_marker-layer .ace_selected-word {position: absolute;z-index: 4;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;}.ace_line .ace_fold {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;display: inline-block;height: 11px;margin-top: -2px;vertical-align: middle;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");background-repeat: no-repeat, repeat-x;background-position: center center, top left;color: transparent;border: 1px solid black;border-radius: 2px;cursor: pointer;pointer-events: auto;}.ace_dark .ace_fold {}.ace_fold:hover{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");}.ace_tooltip {background-color: #FFF;background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));border: 1px solid gray;border-radius: 1px;box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);color: black;max-width: 100%;padding: 3px 4px;position: fixed;z-index: 999999;-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;cursor: default;white-space: pre;word-wrap: break-word;line-height: normal;font-style: normal;font-weight: normal;letter-spacing: normal;pointer-events: none;}.ace_folding-enabled > .ace_gutter-cell {padding-right: 13px;}.ace_fold-widget {-moz-box-sizing: border-box;-webkit-box-sizing: border-box;box-sizing: border-box;margin: 0 -12px 0 1px;display: none;width: 11px;vertical-align: top;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");background-repeat: no-repeat;background-position: center;border-radius: 3px;border: 1px solid transparent;cursor: pointer;}.ace_folding-enabled .ace_fold-widget {display: inline-block; }.ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");}.ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");}.ace_fold-widget:hover {border: 1px solid rgba(0, 0, 0, 0.3);background-color: rgba(255, 255, 255, 0.2);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);}.ace_fold-widget:active {border: 1px solid rgba(0, 0, 0, 0.4);background-color: rgba(0, 0, 0, 0.05);box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);}.ace_dark .ace_fold-widget {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHklEQVQIW2P4//8/AzoGEQ7oGCaLLAhWiSwB146BAQCSTPYocqT0AAAAAElFTkSuQmCC");}.ace_dark .ace_fold-widget.ace_end {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH0lEQVQIW2P4//8/AxQ7wNjIAjDMgC4AxjCVKBirIAAF0kz2rlhxpAAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget.ace_closed {background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAHElEQVQIW2P4//+/AxAzgDADlOOAznHAKgPWAwARji8UIDTfQQAAAABJRU5ErkJggg==");}.ace_dark .ace_fold-widget:hover {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);background-color: rgba(255, 255, 255, 0.1);}.ace_dark .ace_fold-widget:active {box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);}.ace_fold-widget.ace_invalid {background-color: #FFB4B4;border-color: #DE5555;}.ace_fade-fold-widgets .ace_fold-widget {-webkit-transition: opacity 0.4s ease 0.05s;transition: opacity 0.4s ease 0.05s;opacity: 0;}.ace_fade-fold-widgets:hover .ace_fold-widget {-webkit-transition: opacity 0.05s ease 0.05s;transition: opacity 0.05s ease 0.05s;opacity:1;}.ace_underline {text-decoration: underline;}.ace_bold {font-weight: bold;}.ace_nobold .ace_bold {font-weight: normal;}.ace_italic {font-style: italic;}.ace_error-marker {background-color: rgba(255, 0, 0,0.2);position: absolute;z-index: 9;}.ace_highlight-marker {background-color: rgba(255, 255, 0,0.2);position: absolute;z-index: 8;}';i.importCssString(m,"ace_editor");var g=function(e,t){var n=this;this.container=e||i.createElement("div"),this.$keepTextAreaAtCursor=!o.isOldIE,i.addCssClass(this.container,"ace_editor"),this.setTheme(t),this.$gutter=i.createElement("div"),this.$gutter.className="ace_gutter",this.container.appendChild(this.$gutter),this.scroller=i.createElement("div"),this.scroller.className="ace_scroller",this.container.appendChild(this.scroller),this.content=i.createElement("div"),this.content.className="ace_content",this.scroller.appendChild(this.content),this.$gutterLayer=new u(this.$gutter),this.$gutterLayer.on("changeGutterWidth",this.onGutterResize.bind(this)),this.$markerBack=new a(this.content);var r=this.$textLayer=new f(this.content);this.canvas=r.element,this.$markerFront=new a(this.content),this.$cursorLayer=new l(this.content),this.$horizScroll=!1,this.$vScroll=!1,this.scrollBar=this.scrollBarV=new h(this.container,this),this.scrollBarH=new c(this.container,this),this.scrollBarV.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollTop(e.data-n.scrollMargin.top)}),this.scrollBarH.addEventListener("scroll",function(e){n.$scrollAnimation||n.session.setScrollLeft(e.data-n.scrollMargin.left)}),this.scrollTop=0,this.scrollLeft=0,this.cursorPos={row:0,column:0},this.$fontMetrics=new d(this.container,500),this.$textLayer.$setFontMetrics(this.$fontMetrics),this.$textLayer.addEventListener("changeCharacterSize",function(e){n.updateCharacterSize(),n.onResize(!0,n.gutterWidth,n.$size.width,n.$size.height),n._signal("changeCharacterSize",e)}),this.$size={width:0,height:0,scrollerHeight:0,scrollerWidth:0,$dirty:!0},this.layerConfig={width:1,padding:0,firstRow:0,firstRowScreen:0,lastRow:0,lineHeight:0,characterWidth:0,minHeight:1,maxHeight:1,offset:0,height:1,gutterOffset:1},this.scrollMargin={left:0,right:0,top:0,bottom:0,v:0,h:0},this.$loop=new p(this.$renderChanges.bind(this),this.container.ownerDocument.defaultView),this.$loop.schedule(this.CHANGE_FULL),this.updateCharacterSize(),this.setPadding(4),s.resetOptions(this),s._emit("renderer",this)};(function(){this.CHANGE_CURSOR=1,this.CHANGE_MARKER=2,this.CHANGE_GUTTER=4,this.CHANGE_SCROLL=8,this.CHANGE_LINES=16,this.CHANGE_TEXT=32,this.CHANGE_SIZE=64,this.CHANGE_MARKER_BACK=128,this.CHANGE_MARKER_FRONT=256,this.CHANGE_FULL=512,this.CHANGE_H_SCROLL=1024,r.implement(this,v),this.updateCharacterSize=function(){this.$textLayer.allowBoldFonts!=this.$allowBoldFonts&&(this.$allowBoldFonts=this.$textLayer.allowBoldFonts,this.setStyle("ace_nobold",!this.$allowBoldFonts)),this.layerConfig.characterWidth=this.characterWidth=this.$textLayer.getCharacterWidth(),this.layerConfig.lineHeight=this.lineHeight=this.$textLayer.getLineHeight(),this.$updatePrintMargin()},this.setSession=function(e){this.session&&this.session.doc.off("changeNewLineMode",this.onChangeNewLineMode),this.session=e,e&&this.scrollMargin.top&&e.getScrollTop()<=0&&e.setScrollTop(-this.scrollMargin.top),this.$cursorLayer.setSession(e),this.$markerBack.setSession(e),this.$markerFront.setSession(e),this.$gutterLayer.setSession(e),this.$textLayer.setSession(e);if(!e)return;this.$loop.schedule(this.CHANGE_FULL),this.session.$setFontMetrics(this.$fontMetrics),this.onChangeNewLineMode=this.onChangeNewLineMode.bind(this),this.onChangeNewLineMode(),this.session.doc.on("changeNewLineMode",this.onChangeNewLineMode)},this.updateLines=function(e,t,n){t===undefined&&(t=Infinity),this.$changedLines?(this.$changedLines.firstRow>e&&(this.$changedLines.firstRow=e),this.$changedLines.lastRowthis.layerConfig.lastRow)return;this.$loop.schedule(this.CHANGE_LINES)},this.onChangeNewLineMode=function(){this.$loop.schedule(this.CHANGE_TEXT),this.$textLayer.$updateEolChar()},this.onChangeTabSize=function(){this.$loop.schedule(this.CHANGE_TEXT|this.CHANGE_MARKER),this.$textLayer.onChangeTabSize()},this.updateText=function(){this.$loop.schedule(this.CHANGE_TEXT)},this.updateFull=function(e){e?this.$renderChanges(this.CHANGE_FULL,!0):this.$loop.schedule(this.CHANGE_FULL)},this.updateFontSize=function(){this.$textLayer.checkForSizeChanges()},this.$changes=0,this.$updateSizeAsync=function(){this.$loop.pending?this.$size.$dirty=!0:this.onResize()},this.onResize=function(e,t,n,r){if(this.resizing>2)return;this.resizing>0?this.resizing++:this.resizing=e?1:0;var i=this.container;r||(r=i.clientHeight||i.scrollHeight),n||(n=i.clientWidth||i.scrollWidth);var s=this.$updateCachedSize(e,t,n,r);if(!this.$size.scrollerHeight||!n&&!r)return this.resizing=0;e&&(this.$gutterLayer.$padding=null),e?this.$renderChanges(s|this.$changes,!0):this.$loop.schedule(s|this.$changes),this.resizing&&(this.resizing=0)},this.$updateCachedSize=function(e,t,n,r){r-=this.$extraHeight||0;var i=0,s=this.$size,o={width:s.width,height:s.height,scrollerHeight:s.scrollerHeight,scrollerWidth:s.scrollerWidth};r&&(e||s.height!=r)&&(s.height=r,i|=this.CHANGE_SIZE,s.scrollerHeight=s.height,this.$horizScroll&&(s.scrollerHeight-=this.scrollBarH.getHeight()),this.scrollBarV.element.style.bottom=this.scrollBarH.getHeight()+"px",i|=this.CHANGE_SCROLL);if(n&&(e||s.width!=n)){i|=this.CHANGE_SIZE,s.width=n,t==null&&(t=this.$showGutter?this.$gutter.offsetWidth:0),this.gutterWidth=t,this.scrollBarH.element.style.left=this.scroller.style.left=t+"px",s.scrollerWidth=Math.max(0,n-t-this.scrollBarV.getWidth()),this.scrollBarH.element.style.right=this.scroller.style.right=this.scrollBarV.getWidth()+"px",this.scroller.style.bottom=this.scrollBarH.getHeight()+"px";if(this.session&&this.session.getUseWrapMode()&&this.adjustWrapLimit()||e)i|=this.CHANGE_FULL}return s.$dirty=!n||!r,i&&this._signal("resize",o),i},this.onGutterResize=function(){var e=this.$showGutter?this.$gutter.offsetWidth:0;e!=this.gutterWidth&&(this.$changes|=this.$updateCachedSize(!0,e,this.$size.width,this.$size.height)),this.session.getUseWrapMode()&&this.adjustWrapLimit()?this.$loop.schedule(this.CHANGE_FULL):this.$size.$dirty?this.$loop.schedule(this.CHANGE_FULL):(this.$computeLayerConfig(),this.$loop.schedule(this.CHANGE_MARKER))},this.adjustWrapLimit=function(){var e=this.$size.scrollerWidth-this.$padding*2,t=Math.floor(e/this.characterWidth);return this.session.adjustWrapLimit(t,this.$showPrintMargin&&this.$printMarginColumn)},this.setAnimatedScroll=function(e){this.setOption("animatedScroll",e)},this.getAnimatedScroll=function(){return this.$animatedScroll},this.setShowInvisibles=function(e){this.setOption("showInvisibles",e)},this.getShowInvisibles=function(){return this.getOption("showInvisibles")},this.getDisplayIndentGuides=function(){return this.getOption("displayIndentGuides")},this.setDisplayIndentGuides=function(e){this.setOption("displayIndentGuides",e)},this.setShowPrintMargin=function(e){this.setOption("showPrintMargin",e)},this.getShowPrintMargin=function(){return this.getOption("showPrintMargin")},this.setPrintMarginColumn=function(e){this.setOption("printMarginColumn",e)},this.getPrintMarginColumn=function(){return this.getOption("printMarginColumn")},this.getShowGutter=function(){return this.getOption("showGutter")},this.setShowGutter=function(e){return this.setOption("showGutter",e)},this.getFadeFoldWidgets=function(){return this.getOption("fadeFoldWidgets")},this.setFadeFoldWidgets=function(e){this.setOption("fadeFoldWidgets",e)},this.setHighlightGutterLine=function(e){this.setOption("highlightGutterLine",e)},this.getHighlightGutterLine=function(){return this.getOption("highlightGutterLine")},this.$updateGutterLineHighlight=function(){var e=this.$cursorLayer.$pixelPos,t=this.layerConfig.lineHeight;if(this.session.getUseWrapMode()){var n=this.session.selection.getCursor();n.column=0,e=this.$cursorLayer.getPixelPosition(n,!0),t*=this.session.getRowLength(n.row)}this.$gutterLineHighlight.style.top=e.top-this.layerConfig.offset+"px",this.$gutterLineHighlight.style.height=t+"px"},this.$updatePrintMargin=function(){if(!this.$showPrintMargin&&!this.$printMarginEl)return;if(!this.$printMarginEl){var e=i.createElement("div");e.className="ace_layer ace_print-margin-layer",this.$printMarginEl=i.createElement("div"),this.$printMarginEl.className="ace_print-margin",e.appendChild(this.$printMarginEl),this.content.insertBefore(e,this.content.firstChild)}var t=this.$printMarginEl.style;t.left=this.characterWidth*this.$printMarginColumn+this.$padding+"px",t.visibility=this.$showPrintMargin?"visible":"hidden",this.session&&this.session.$wrap==-1&&this.adjustWrapLimit()},this.getContainerElement=function(){return this.container},this.getMouseEventTarget=function(){return this.content},this.getTextAreaContainer=function(){return this.container},this.$moveTextAreaToCursor=function(){if(!this.$keepTextAreaAtCursor)return;var e=this.layerConfig,t=this.$cursorLayer.$pixelPos.top,n=this.$cursorLayer.$pixelPos.left;t-=e.offset;var r=this.lineHeight;if(t<0||t>e.height-r)return;var i=this.characterWidth;if(this.$composition){var s=this.textarea.value.replace(/^\x01+/,"");i*=this.session.$getStringScreenWidth(s)[0]+2,r+=2}n-=this.scrollLeft,n>this.$size.scrollerWidth-i&&(n=this.$size.scrollerWidth-i),n+=this.gutterWidth,this.textarea.style.height=r+"px",this.textarea.style.width=i+"px",this.textarea.style.left=Math.min(n,this.$size.scrollerWidth-i)+"px",this.textarea.style.top=Math.min(t,this.$size.height-r)+"px"},this.getFirstVisibleRow=function(){return this.layerConfig.firstRow},this.getFirstFullyVisibleRow=function(){return this.layerConfig.firstRow+(this.layerConfig.offset===0?0:1)},this.getLastFullyVisibleRow=function(){var e=Math.floor((this.layerConfig.height+this.layerConfig.offset)/this.layerConfig.lineHeight);return this.layerConfig.firstRow-1+e},this.getLastVisibleRow=function(){return this.layerConfig.lastRow},this.$padding=null,this.setPadding=function(e){this.$padding=e,this.$textLayer.setPadding(e),this.$cursorLayer.setPadding(e),this.$markerFront.setPadding(e),this.$markerBack.setPadding(e),this.$loop.schedule(this.CHANGE_FULL),this.$updatePrintMargin()},this.setScrollMargin=function(e,t,n,r){var i=this.scrollMargin;i.top=e|0,i.bottom=t|0,i.right=r|0,i.left=n|0,i.v=i.top+i.bottom,i.h=i.left+i.right,i.top&&this.scrollTop<=0&&this.session&&this.session.setScrollTop(-i.top),this.updateFull()},this.getHScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setHScrollBarAlwaysVisible=function(e){this.setOption("hScrollBarAlwaysVisible",e)},this.getVScrollBarAlwaysVisible=function(){return this.$hScrollBarAlwaysVisible},this.setVScrollBarAlwaysVisible=function(e){this.setOption("vScrollBarAlwaysVisible",e)},this.$updateScrollBarV=function(){var e=this.layerConfig.maxHeight,t=this.$size.scrollerHeight;!this.$maxLines&&this.$scrollPastEnd&&(e-=(t-this.lineHeight)*this.$scrollPastEnd,this.scrollTop>e-t&&(e=this.scrollTop+t,this.scrollBarV.scrollTop=null)),this.scrollBarV.setScrollHeight(e+this.scrollMargin.v),this.scrollBarV.setScrollTop(this.scrollTop+this.scrollMargin.top)},this.$updateScrollBarH=function(){this.scrollBarH.setScrollWidth(this.layerConfig.width+2*this.$padding+this.scrollMargin.h),this.scrollBarH.setScrollLeft(this.scrollLeft+this.scrollMargin.left)},this.$frozen=!1,this.freeze=function(){this.$frozen=!0},this.unfreeze=function(){this.$frozen=!1},this.$renderChanges=function(e,t){this.$changes&&(e|=this.$changes,this.$changes=0);if(!this.session||!this.container.offsetWidth||this.$frozen||!e&&!t){this.$changes|=e;return}if(this.$size.$dirty)return this.$changes|=e,this.onResize(!0);this.lineHeight||this.$textLayer.checkForSizeChanges(),this._signal("beforeRender");var n=this.layerConfig;if(e&this.CHANGE_FULL||e&this.CHANGE_SIZE||e&this.CHANGE_TEXT||e&this.CHANGE_LINES||e&this.CHANGE_SCROLL||e&this.CHANGE_H_SCROLL){e|=this.$computeLayerConfig();if(n.firstRow!=this.layerConfig.firstRow&&n.firstRowScreen==this.layerConfig.firstRowScreen){var r=this.scrollTop+(n.firstRow-this.layerConfig.firstRow)*this.lineHeight;r>0&&(this.scrollTop=r,e|=this.CHANGE_SCROLL,e|=this.$computeLayerConfig())}n=this.layerConfig,this.$updateScrollBarV(),e&this.CHANGE_H_SCROLL&&this.$updateScrollBarH(),this.$gutterLayer.element.style.marginTop=-n.offset+"px",this.content.style.marginTop=-n.offset+"px",this.content.style.width=n.width+2*this.$padding+"px",this.content.style.height=n.minHeight+"px"}e&this.CHANGE_H_SCROLL&&(this.content.style.marginLeft=-this.scrollLeft+"px",this.scroller.className=this.scrollLeft<=0?"ace_scroller":"ace_scroller ace_scroll-left");if(e&this.CHANGE_FULL){this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this._signal("afterRender");return}if(e&this.CHANGE_SCROLL){e&this.CHANGE_TEXT||e&this.CHANGE_LINES?this.$textLayer.update(n):this.$textLayer.scrollLines(n),this.$showGutter&&this.$gutterLayer.update(n),this.$markerBack.update(n),this.$markerFront.update(n),this.$cursorLayer.update(n),this.$highlightGutterLine&&this.$updateGutterLineHighlight(),this.$moveTextAreaToCursor(),this._signal("afterRender");return}e&this.CHANGE_TEXT?(this.$textLayer.update(n),this.$showGutter&&this.$gutterLayer.update(n)):e&this.CHANGE_LINES?(this.$updateLines()||e&this.CHANGE_GUTTER&&this.$showGutter)&&this.$gutterLayer.update(n):(e&this.CHANGE_TEXT||e&this.CHANGE_GUTTER)&&this.$showGutter&&this.$gutterLayer.update(n),e&this.CHANGE_CURSOR&&(this.$cursorLayer.update(n),this.$moveTextAreaToCursor(),this.$highlightGutterLine&&this.$updateGutterLineHighlight()),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_FRONT)&&this.$markerFront.update(n),e&(this.CHANGE_MARKER|this.CHANGE_MARKER_BACK)&&this.$markerBack.update(n),this._signal("afterRender")},this.$autosize=function(){var e=this.session.getScreenLength()*this.lineHeight,t=this.$maxLines*this.lineHeight,n=Math.max((this.$minLines||1)*this.lineHeight,Math.min(t,e))+this.scrollMargin.v+(this.$extraHeight||0),r=e>t;if(n!=this.desiredHeight||this.$size.height!=this.desiredHeight||r!=this.$vScroll){r!=this.$vScroll&&(this.$vScroll=r,this.scrollBarV.setVisible(r));var i=this.container.clientWidth;this.container.style.height=n+"px",this.$updateCachedSize(!0,this.$gutterWidth,i,n),this.desiredHeight=n,this._signal("autosize")}},this.$computeLayerConfig=function(){this.$maxLines&&this.lineHeight>1&&this.$autosize();var e=this.session,t=this.$size,n=t.height<=2*this.lineHeight,r=this.session.getScreenLength(),i=r*this.lineHeight,s=this.scrollTop%this.lineHeight,o=t.scrollerHeight+this.lineHeight,u=this.$getLongestLine(),a=!n&&(this.$hScrollBarAlwaysVisible||t.scrollerWidth-u-2*this.$padding<0),f=this.$horizScroll!==a;f&&(this.$horizScroll=a,this.scrollBarH.setVisible(a));var l=!this.$maxLines&&this.$scrollPastEnd?(t.scrollerHeight-this.lineHeight)*this.$scrollPastEnd:0;i+=l,this.session.setScrollTop(Math.max(-this.scrollMargin.top,Math.min(this.scrollTop,i-t.scrollerHeight+this.scrollMargin.bottom))),this.session.setScrollLeft(Math.max(-this.scrollMargin.left,Math.min(this.scrollLeft,u+2*this.$padding-t.scrollerWidth+this.scrollMargin.right)));var c=!n&&(this.$vScrollBarAlwaysVisible||t.scrollerHeight-i+l<0||this.scrollTop),h=this.$vScroll!==c;h&&(this.$vScroll=c,this.scrollBarV.setVisible(c));var p=Math.ceil(o/this.lineHeight)-1,d=Math.max(0,Math.round((this.scrollTop-s)/this.lineHeight)),v=d+p,m,g,y=this.lineHeight;d=e.screenToDocumentRow(d,0);var b=e.getFoldLine(d);b&&(d=b.start.row),m=e.documentToScreenRow(d,0),g=e.getRowLength(d)*y,v=Math.min(e.screenToDocumentRow(v,0),e.getLength()-1),o=t.scrollerHeight+e.getRowLength(v)*y+g,s=this.scrollTop-m*y;var w=0;this.layerConfig.width!=u&&(w=this.CHANGE_H_SCROLL);if(f||h)w=this.$updateCachedSize(!0,this.gutterWidth,t.width,t.height),this._signal("scrollbarVisibilityChanged"),h&&(u=this.$getLongestLine());return this.layerConfig={width:u,padding:this.$padding,firstRow:d,firstRowScreen:m,lastRow:v,lineHeight:y,characterWidth:this.characterWidth,minHeight:o,maxHeight:i,offset:s,gutterOffset:Math.max(0,Math.ceil((s+t.height-t.scrollerHeight)/y)),height:this.$size.scrollerHeight},w},this.$updateLines=function(){var e=this.$changedLines.firstRow,t=this.$changedLines.lastRow;this.$changedLines=null;var n=this.layerConfig;if(e>n.lastRow+1)return;if(ts?(t&&(s-=t*this.$size.scrollerHeight),s===0&&(s=-this.scrollMargin.top),this.session.setScrollTop(s)):a+this.$size.scrollerHeight-ui?(i=1-this.scrollMargin.top)return!0;if(t>0&&this.session.getScrollTop()+this.$size.scrollerHeight-this.layerConfig.maxHeight<-1+this.scrollMargin.bottom)return!0;if(e<0&&this.session.getScrollLeft()>=1-this.scrollMargin.left)return!0;if(e>0&&this.session.getScrollLeft()+this.$size.scrollerWidth-this.layerConfig.width<-1+this.scrollMargin.right)return!0},this.pixelToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=(e+this.scrollLeft-n.left-this.$padding)/this.characterWidth,i=Math.floor((t+this.scrollTop-n.top)/this.lineHeight),s=Math.round(r);return{row:i,column:s,side:r-s>0?1:-1}},this.screenToTextCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=Math.round((e+this.scrollLeft-n.left-this.$padding)/this.characterWidth),i=(t+this.scrollTop-n.top)/this.lineHeight;return this.session.screenToDocumentPosition(i,Math.max(r,0))},this.textToScreenCoordinates=function(e,t){var n=this.scroller.getBoundingClientRect(),r=this.session.documentToScreenPosition(e,t),i=this.$padding+Math.round(r.column*this.characterWidth),s=r.row*this.lineHeight;return{pageX:n.left+i-this.scrollLeft,pageY:n.top+s-this.scrollTop}},this.visualizeFocus=function(){i.addCssClass(this.container,"ace_focus")},this.visualizeBlur=function(){i.removeCssClass(this.container,"ace_focus")},this.showComposition=function(e){this.$composition||(this.$composition={keepTextAreaAtCursor:this.$keepTextAreaAtCursor,cssText:this.textarea.style.cssText}),this.$keepTextAreaAtCursor=!0,i.addCssClass(this.textarea,"ace_composition"),this.textarea.style.cssText="",this.$moveTextAreaToCursor()},this.setCompositionText=function(e){this.$moveTextAreaToCursor()},this.hideComposition=function(){if(!this.$composition)return;i.removeCssClass(this.textarea,"ace_composition"),this.$keepTextAreaAtCursor=this.$composition.keepTextAreaAtCursor,this.textarea.style.cssText=this.$composition.cssText,this.$composition=null},this.setTheme=function(e,t){function o(r){if(n.$themeId!=e)return t&&t();if(!r.cssClass)return;i.importCssString(r.cssText,r.cssClass,n.container.ownerDocument),n.theme&&i.removeCssClass(n.container,n.theme.cssClass);var s="padding"in r?r.padding:"padding"in(n.theme||{})?4:n.$padding;n.$padding&&s!=n.$padding&&n.setPadding(s),n.$theme=r.cssClass,n.theme=r,i.addCssClass(n.container,r.cssClass),i.setCssClass(n.container,"ace_dark",r.isDark),n.$size&&(n.$size.width=0,n.$updateSizeAsync()),n._dispatchEvent("themeLoaded",{theme:r}),t&&t()}var n=this;this.$themeId=e,n._dispatchEvent("themeChange",{theme:e});if(!e||typeof e=="string"){var r=e||this.$options.theme.initialValue;s.loadModule(["theme",r],o)}else o(e)},this.getTheme=function(){return this.$themeId},this.setStyle=function(e,t){i.setCssClass(this.container,e,t!==!1)},this.unsetStyle=function(e){i.removeCssClass(this.container,e)},this.setCursorStyle=function(e){this.scroller.style.cursor!=e&&(this.scroller.style.cursor=e)},this.setMouseCursor=function(e){this.scroller.style.cursor=e},this.destroy=function(){this.$textLayer.destroy(),this.$cursorLayer.destroy()}}).call(g.prototype),s.defineOptions(g.prototype,"renderer",{animatedScroll:{initialValue:!1},showInvisibles:{set:function(e){this.$textLayer.setShowInvisibles(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!1},showPrintMargin:{set:function(){this.$updatePrintMargin()},initialValue:!0},printMarginColumn:{set:function(){this.$updatePrintMargin()},initialValue:80},printMargin:{set:function(e){typeof e=="number"&&(this.$printMarginColumn=e),this.$showPrintMargin=!!e,this.$updatePrintMargin()},get:function(){return this.$showPrintMargin&&this.$printMarginColumn}},showGutter:{set:function(e){this.$gutter.style.display=e?"block":"none",this.$loop.schedule(this.CHANGE_FULL),this.onGutterResize()},initialValue:!0},fadeFoldWidgets:{set:function(e){i.setCssClass(this.$gutter,"ace_fade-fold-widgets",e)},initialValue:!1},showFoldWidgets:{set:function(e){this.$gutterLayer.setShowFoldWidgets(e)},initialValue:!0},showLineNumbers:{set:function(e){this.$gutterLayer.setShowLineNumbers(e),this.$loop.schedule(this.CHANGE_GUTTER)},initialValue:!0},displayIndentGuides:{set:function(e){this.$textLayer.setDisplayIndentGuides(e)&&this.$loop.schedule(this.CHANGE_TEXT)},initialValue:!0},highlightGutterLine:{set:function(e){if(!this.$gutterLineHighlight){this.$gutterLineHighlight=i.createElement("div"),this.$gutterLineHighlight.className="ace_gutter-active-line",this.$gutter.appendChild(this.$gutterLineHighlight);return}this.$gutterLineHighlight.style.display=e?"":"none",this.$cursorLayer.$pixelPos&&this.$updateGutterLineHighlight()},initialValue:!1,value:!0},hScrollBarAlwaysVisible:{set:function(e){(!this.$hScrollBarAlwaysVisible||!this.$horizScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},vScrollBarAlwaysVisible:{set:function(e){(!this.$vScrollBarAlwaysVisible||!this.$vScroll)&&this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:!1},fontSize:{set:function(e){typeof e=="number"&&(e+="px"),this.container.style.fontSize=e,this.updateFontSize()},initialValue:12},fontFamily:{set:function(e){this.container.style.fontFamily=e,this.updateFontSize()}},maxLines:{set:function(e){this.updateFull()}},minLines:{set:function(e){this.updateFull()}},scrollPastEnd:{set:function(e){e=+e||0;if(this.$scrollPastEnd==e)return;this.$scrollPastEnd=e,this.$loop.schedule(this.CHANGE_SCROLL)},initialValue:0,handlesSet:!0},fixedWidthGutter:{set:function(e){this.$gutterLayer.$fixedWidth=!!e,this.$loop.schedule(this.CHANGE_GUTTER)}},theme:{set:function(e){this.setTheme(e)},get:function(){return this.$themeId||this.theme},initialValue:"./theme/textmate",handlesSet:!0}}),t.VirtualRenderer=g}),ace.define("ace/worker/worker_client",["require","exports","module","ace/lib/oop","ace/lib/net","ace/lib/event_emitter","ace/config"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("../lib/net"),s=e("../lib/event_emitter").EventEmitter,o=e("../config"),u=function(t,n,r,i){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.onMessage=this.onMessage.bind(this),e.nameToUrl&&!e.toUrl&&(e.toUrl=e.nameToUrl);if(o.get("packaged")||!e.toUrl)i=i||o.moduleUrl(n,"worker");else{var s=this.$normalizePath;i=i||s(e.toUrl("ace/worker/worker.js",null,"_"));var u={};t.forEach(function(t){u[t]=s(e.toUrl(t,null,"_").replace(/(\.js)?(\?.*)?$/,""))})}try{this.$worker=new Worker(i)}catch(a){if(!(a instanceof window.DOMException))throw a;var f=this.$workerBlob(i),l=window.URL||window.webkitURL,c=l.createObjectURL(f);this.$worker=new Worker(c),l.revokeObjectURL(c)}this.$worker.postMessage({init:!0,tlns:u,module:n,classname:r}),this.callbackId=1,this.callbacks={},this.$worker.onmessage=this.onMessage};(function(){r.implement(this,s),this.onMessage=function(e){var t=e.data;switch(t.type){case"event":this._signal(t.name,{data:t.data});break;case"call":var n=this.callbacks[t.id];n&&(n(t.data),delete this.callbacks[t.id]);break;case"error":this.reportError(t.data);break;case"log":window.console&&console.log&&console.log.apply(console,t.data)}},this.reportError=function(e){window.console&&console.error&&console.error(e)},this.$normalizePath=function(e){return i.qualifyURL(e)},this.terminate=function(){this._signal("terminate",{}),this.deltaQueue=null,this.$worker.terminate(),this.$worker=null,this.$doc&&this.$doc.off("change",this.changeListener),this.$doc=null},this.send=function(e,t){this.$worker.postMessage({command:e,args:t})},this.call=function(e,t,n){if(n){var r=this.callbackId++;this.callbacks[r]=n,t.push(r)}this.send(e,t)},this.emit=function(e,t){try{this.$worker.postMessage({event:e,data:{data:t.data}})}catch(n){console.error(n.stack)}},this.attachToDocument=function(e){this.$doc&&this.terminate(),this.$doc=e,this.call("setValue",[e.getValue()]),e.on("change",this.changeListener)},this.changeListener=function(e){this.deltaQueue?this.deltaQueue.push(e.data):(this.deltaQueue=[e.data],setTimeout(this.$sendDeltaQueue,0))},this.$sendDeltaQueue=function(){var e=this.deltaQueue;if(!e)return;this.deltaQueue=null,e.length>20&&e.length>this.$doc.getLength()>>1?this.call("setValue",[this.$doc.getValue()]):this.emit("change",{data:e})},this.$workerBlob=function(e){var t="importScripts('"+i.qualifyURL(e)+"');";try{return new Blob([t],{type:"application/javascript"})}catch(n){var r=window.BlobBuilder||window.WebKitBlobBuilder||window.MozBlobBuilder,s=new r;return s.append(t),s.getBlob("application/javascript")}}}).call(u.prototype);var a=function(e,t,n){this.$sendDeltaQueue=this.$sendDeltaQueue.bind(this),this.changeListener=this.changeListener.bind(this),this.callbackId=1,this.callbacks={},this.messageBuffer=[];var r=null,i=!1,u=Object.create(s),a=this;this.$worker={},this.$worker.terminate=function(){},this.$worker.postMessage=function(e){a.messageBuffer.push(e),r&&(i?setTimeout(f):f())},this.setEmitSync=function(e){i=e};var f=function(){var e=a.messageBuffer.shift();e.command?r[e.command].apply(r,e.args):e.event&&u._signal(e.event,e.data)};u.postMessage=function(e){a.onMessage({data:e})},u.callback=function(e,t){this.postMessage({type:"call",id:t,data:e})},u.emit=function(e,t){this.postMessage({type:"event",name:e,data:t})},o.loadModule(["worker",t],function(e){r=new e[n](u);while(a.messageBuffer.length)f()})};a.prototype=u.prototype,t.UIWorkerClient=a,t.WorkerClient=u}),ace.define("ace/placeholder",["require","exports","module","ace/range","ace/lib/event_emitter","ace/lib/oop"],function(e,t,n){"use strict";var r=e("./range").Range,i=e("./lib/event_emitter").EventEmitter,s=e("./lib/oop"),o=function(e,t,n,r,i,s){var o=this;this.length=t,this.session=e,this.doc=e.getDocument(),this.mainClass=i,this.othersClass=s,this.$onUpdate=this.onUpdate.bind(this),this.doc.on("change",this.$onUpdate),this.$others=r,this.$onCursorChange=function(){setTimeout(function(){o.onCursorChange()})},this.$pos=n;var u=e.getUndoManager().$undoStack||e.getUndoManager().$undostack||{length:-1};this.$undoStackDepth=u.length,this.setup(),e.selection.on("changeCursor",this.$onCursorChange)};(function(){s.implement(this,i),this.setup=function(){var e=this,t=this.doc,n=this.session,i=this.$pos;this.selectionBefore=n.selection.toJSON(),n.selection.inMultiSelectMode&&n.selection.toSingleRange(),this.pos=t.createAnchor(i.row,i.column),this.markerId=n.addMarker(new r(i.row,i.column,i.row,i.column+this.length),this.mainClass,null,!1),this.pos.on("change",function(t){n.removeMarker(e.markerId),e.markerId=n.addMarker(new r(t.value.row,t.value.column,t.value.row,t.value.column+e.length),e.mainClass,null,!1)}),this.others=[],this.$others.forEach(function(n){var r=t.createAnchor(n.row,n.column);e.others.push(r)}),n.setUndoSelect(!1)},this.showOtherMarkers=function(){if(this.othersActive)return;var e=this.session,t=this;this.othersActive=!0,this.others.forEach(function(n){n.markerId=e.addMarker(new r(n.row,n.column,n.row,n.column+t.length),t.othersClass,null,!1),n.on("change",function(i){e.removeMarker(n.markerId),n.markerId=e.addMarker(new r(i.value.row,i.value.column,i.value.row,i.value.column+t.length),t.othersClass,null,!1)})})},this.hideOtherMarkers=function(){if(!this.othersActive)return;this.othersActive=!1;for(var e=0;e=this.pos.column&&n.start.column<=this.pos.column+this.length+1){var s=n.start.column-this.pos.column;this.length+=i;if(!this.session.$fromUndo){if(t.action==="insertText")for(var o=this.others.length-1;o>=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column=0;o--){var u=this.others[o],a={row:u.row,column:u.column+s};u.row===n.start.row&&n.start.column=this.pos.column&&t.column<=this.pos.column+this.length?(this.showOtherMarkers(),this._emit("cursorEnter",e)):(this.hideOtherMarkers(),this._emit("cursorLeave",e))},this.detach=function(){this.session.removeMarker(this.markerId),this.hideOtherMarkers(),this.doc.removeEventListener("change",this.$onUpdate),this.session.selection.removeEventListener("changeCursor",this.$onCursorChange),this.pos.detach();for(var e=0;e1&&!this.inMultiSelectMode&&(this._signal("multiSelect"),this.inMultiSelectMode=!0,this.session.$undoSelect=!1,this.rangeList.attach(this.session)),t||this.fromOrientedRange(e)},this.toSingleRange=function(e){e=e||this.ranges[0];var t=this.rangeList.removeAll();t.length&&this.$onRemoveRange(t),e&&this.fromOrientedRange(e)},this.substractPoint=function(e){var t=this.rangeList.substractPoint(e);if(t)return this.$onRemoveRange(t),t[0]},this.mergeOverlappingRanges=function(){var e=this.rangeList.merge();e.length?this.$onRemoveRange(e):this.ranges[0]&&this.fromOrientedRange(this.ranges[0])},this.$onAddRange=function(e){this.rangeCount=this.rangeList.ranges.length,this.ranges.unshift(e),this._signal("addRange",{range:e})},this.$onRemoveRange=function(e){this.rangeCount=this.rangeList.ranges.length;if(this.rangeCount==1&&this.inMultiSelectMode){var t=this.rangeList.ranges.pop();e.push(t),this.rangeCount=0}for(var n=e.length;n--;){var r=this.ranges.indexOf(e[n]);this.ranges.splice(r,1)}this._signal("removeRange",{ranges:e}),this.rangeCount===0&&this.inMultiSelectMode&&(this.inMultiSelectMode=!1,this._signal("singleSelect"),this.session.$undoSelect=!0,this.rangeList.detach(this.session)),t=t||this.ranges[0],t&&!t.isEqual(this.getRange())&&this.fromOrientedRange(t)},this.$initRangeList=function(){if(this.rangeList)return;this.rangeList=new r,this.ranges=[],this.rangeCount=0},this.getAllRanges=function(){return this.rangeCount?this.rangeList.ranges.concat():[this.getRange()]},this.splitIntoLines=function(){if(this.rangeCount>1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var n=this.getRange(),r=this.isBackwards(),s=n.start.row,o=n.end.row;if(s==o){if(r)var u=n.end,a=n.start;else var u=n.start,a=n.end;this.addRange(i.fromPoints(a,a)),this.addRange(i.fromPoints(u,u));return}var f=[],l=this.getLineRange(s,!0);l.start.column=n.start.column,f.push(l);for(var c=s+1;c1){var e=this.rangeList.ranges,t=e[e.length-1],n=i.fromPoints(e[0].start,t.end);this.toSingleRange(),this.setSelectionRange(n,t.cursor==t.start)}else{var r=this.session.documentToScreenPosition(this.selectionLead),s=this.session.documentToScreenPosition(this.selectionAnchor),o=this.rectangularRangeBlock(r,s);o.forEach(this.addRange,this)}},this.rectangularRangeBlock=function(e,t,n){var r=[],s=e.column0)d--;if(d>0){var m=0;while(r[m].isEmpty())m++}for(var g=d;g>=m;g--)r[g].isEmpty()&&r.splice(g,1)}return r}}.call(s.prototype);var d=e("./editor").Editor;(function(){this.updateSelectionMarkers=function(){this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.addSelectionMarker=function(e){e.cursor||(e.cursor=e.end);var t=this.getSelectionStyle();return e.marker=this.session.addMarker(e,"ace_selection",t),this.session.$selectionMarkers.push(e),this.session.selectionMarkerCount=this.session.$selectionMarkers.length,e},this.removeSelectionMarker=function(e){if(!e.marker)return;this.session.removeMarker(e.marker);var t=this.session.$selectionMarkers.indexOf(e);t!=-1&&this.session.$selectionMarkers.splice(t,1),this.session.selectionMarkerCount=this.session.$selectionMarkers.length},this.removeSelectionMarkers=function(e){var t=this.session.$selectionMarkers;for(var n=e.length;n--;){var r=e[n];if(!r.marker)continue;this.session.removeMarker(r.marker);var i=t.indexOf(r);i!=-1&&t.splice(i,1)}this.session.selectionMarkerCount=t.length},this.$onAddRange=function(e){this.addSelectionMarker(e.range),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onRemoveRange=function(e){this.removeSelectionMarkers(e.ranges),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onMultiSelect=function(e){if(this.inMultiSelectMode)return;this.inMultiSelectMode=!0,this.setStyle("ace_multiselect"),this.keyBinding.addKeyboardHandler(f.keyboardHandler),this.commands.setDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers()},this.$onSingleSelect=function(e){if(this.session.multiSelect.inVirtualMode)return;this.inMultiSelectMode=!1,this.unsetStyle("ace_multiselect"),this.keyBinding.removeKeyboardHandler(f.keyboardHandler),this.commands.removeDefaultHandler("exec",this.$onMultiSelectExec),this.renderer.updateCursor(),this.renderer.updateBackMarkers(),this._emit("changeSelection")},this.$onMultiSelectExec=function(e){var t=e.command,n=e.editor;if(!n.multiSelect)return;if(!t.multiSelectAction){var r=t.exec(n,e.args||{});n.multiSelect.addRange(n.multiSelect.toOrientedRange()),n.multiSelect.mergeOverlappingRanges()}else t.multiSelectAction=="forEach"?r=n.forEachSelection(t,e.args):t.multiSelectAction=="forEachLine"?r=n.forEachSelection(t,e.args,!0):t.multiSelectAction=="single"?(n.exitMultiSelectMode(),r=t.exec(n,e.args||{})):r=t.multiSelectAction(n,e.args||{});return r},this.forEachSelection=function(e,t,n){if(this.inVirtualSelectionMode)return;var r=n&&n.keepOrder,i=n==1||n&&n.$byLines,o=this.session,u=this.selection,a=u.rangeList,f=(r?u:a).ranges,l;if(!f.length)return e.exec?e.exec(this,t||{}):e(this,t||{});var c=u._eventRegistry;u._eventRegistry={};var h=new s(o);this.inVirtualSelectionMode=!0;for(var p=f.length;p--;){if(i)while(p>0&&f[p].start.row==f[p-1].end.row)p--;h.fromOrientedRange(f[p]),h.index=p,this.selection=o.selection=h;var d=e.exec?e.exec(this,t||{}):e(this,t||{});!l&&d!==undefined&&(l=d),h.toOrientedRange(f[p])}h.detach(),this.selection=o.selection=u,this.inVirtualSelectionMode=!1,u._eventRegistry=c,u.mergeOverlappingRanges();var v=this.renderer.$scrollAnimation;return this.onCursorChange(),this.onSelectionChange(),v&&v.from==v.to&&this.renderer.animateScrolling(v.from),l},this.exitMultiSelectMode=function(){if(!this.inMultiSelectMode||this.inVirtualSelectionMode)return;this.multiSelect.toSingleRange()},this.getSelectedText=function(){var e="";if(this.inMultiSelectMode&&!this.inVirtualSelectionMode){var t=this.multiSelect.rangeList.ranges,n=[];for(var r=0;rr.length||n.length<2||!n[1])return this.commands.exec("insertstring",this,e);for(var i=r.length;i--;){var s=r[i];s.isEmpty()||this.session.remove(s),this.session.insert(s.start,n[i])}},this.findAll=function(e,t,n){t=t||{},t.needle=e||t.needle;if(t.needle==undefined){var r=this.selection.isEmpty()?this.selection.getWordRange():this.selection.getRange();t.needle=this.session.getTextRange(r)}this.$search.set(t);var i=this.$search.findAll(this.session);if(!i.length)return 0;this.$blockScrolling+=1;var s=this.multiSelect;n||s.toSingleRange(i[0]);for(var o=i.length;o--;)s.addRange(i[o],!0);return r&&s.rangeList.rangeAtPoint(r.start)&&s.addRange(r,!0),this.$blockScrolling-=1,i.length},this.selectMoreLines=function(e,t){var n=this.selection.toOrientedRange(),r=n.cursor==n.end,s=this.session.documentToScreenPosition(n.cursor);this.selection.$desiredColumn&&(s.column=this.selection.$desiredColumn);var o=this.session.screenToDocumentPosition(s.row+e,s.column);if(!n.isEmpty())var u=this.session.documentToScreenPosition(r?n.end:n.start),a=this.session.screenToDocumentPosition(u.row+e,u.column);else var a=o;if(r){var f=i.fromPoints(o,a);f.cursor=f.start}else{var f=i.fromPoints(a,o);f.cursor=f.end}f.desiredColumn=s.column;if(!this.selection.inMultiSelectMode)this.selection.addRange(n);else if(t)var l=n.cursor;this.selection.addRange(f),l&&this.selection.substractPoint(l)},this.transposeSelections=function(e){var t=this.session,n=t.multiSelect,r=n.ranges;for(var i=r.length;i--;){var s=r[i];if(s.isEmpty()){var o=t.getWordRange(s.start.row,s.start.column);s.start.row=o.start.row,s.start.column=o.start.column,s.end.row=o.end.row,s.end.column=o.end.column}}n.mergeOverlappingRanges();var u=[];for(var i=r.length;i--;){var s=r[i];u.unshift(t.getTextRange(s))}e<0?u.unshift(u.pop()):u.push(u.shift());for(var i=r.length;i--;){var s=r[i],o=s.clone();t.replace(s,u[i]),s.start.row=o.start.row,s.start.column=o.start.column}},this.selectMore=function(e,t,n){var r=this.session,i=r.multiSelect,s=i.toOrientedRange();if(s.isEmpty()){s=r.getWordRange(s.start.row,s.start.column),s.cursor=e==-1?s.start:s.end,this.multiSelect.addRange(s);if(n)return}var o=r.getTextRange(s),u=h(r,o,e);u&&(u.cursor=e==-1?u.start:u.end,this.$blockScrolling+=1,this.session.unfold(u),this.multiSelect.addRange(u),this.$blockScrolling-=1,this.renderer.scrollCursorIntoView(null,.5)),t&&this.multiSelect.substractPoint(s.cursor)},this.alignCursors=function(){var e=this.session,t=e.multiSelect,n=t.ranges,r=-1,s=n.filter(function(e){if(e.cursor.row==r)return!0;r=e.cursor.row});if(!n.length||s.length==n.length-1){var o=this.selection.getRange(),u=o.start.row,f=o.end.row,l=u==f;if(l){var c=this.session.getLength(),h;do h=this.session.getLine(f);while(/[=:]/.test(h)&&++f0);u<0&&(u=0),f>=c&&(f=c-1)}var p=this.session.doc.removeLines(u,f);p=this.$reAlignText(p,l),this.session.doc.insert({row:u,column:0},p.join("\n")+"\n"),l||(o.start.column=0,o.end.column=p[p.length-1].length),this.selection.setRange(o)}else{s.forEach(function(e){t.substractPoint(e.cursor)});var d=0,v=Infinity,m=n.map(function(t){var n=t.cursor,r=e.getLine(n.row),i=r.substr(n.column).search(/\S/g);return i==-1&&(i=0),n.column>d&&(d=n.column),io?e.insert(r,a.stringRepeat(" ",s-o)):e.remove(new i(r.row,r.column,r.row,r.column-s+o)),t.start.column=t.end.column=d,t.start.row=t.end.row=r.row,t.cursor=t.end}),t.fromOrientedRange(n[0]),this.renderer.updateCursor(),this.renderer.updateBackMarkers()}},this.$reAlignText=function(e,t){function u(e){return a.stringRepeat(" ",e)}function f(e){return e[2]?u(i)+e[2]+u(s-e[2].length+o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function l(e){return e[2]?u(i+s-e[2].length)+e[2]+u(o," ")+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}function c(e){return e[2]?u(i)+e[2]+u(o)+e[4].replace(/^([=:])\s+/,"$1 "):e[0]}var n=!0,r=!0,i,s,o;return e.map(function(e){var t=e.match(/(\s*)(.*?)(\s*)([=:].*)/);return t?i==null?(i=t[1].length,s=t[2].length,o=t[3].length,t):(i+s+o!=t[1].length+t[2].length+t[3].length&&(r=!1),i!=t[1].length&&(n=!1),i>t[1].length&&(i=t[1].length),st[3].length&&(o=t[3].length),t):[e]}).map(t?f:n?r?l:f:c)}}).call(d.prototype),t.onSessionChange=function(e){var t=e.session;t&&!t.multiSelect&&(t.$selectionMarkers=[],t.selection.$initRangeList(),t.multiSelect=t.selection),this.multiSelect=t&&t.multiSelect;var n=e.oldSession;n&&(n.multiSelect.off("addRange",this.$onAddRange),n.multiSelect.off("removeRange",this.$onRemoveRange),n.multiSelect.off("multiSelect",this.$onMultiSelect),n.multiSelect.off("singleSelect",this.$onSingleSelect),n.multiSelect.lead.off("change",this.$checkMultiselectChange),n.multiSelect.anchor.off("change",this.$checkMultiselectChange)),t&&(t.multiSelect.on("addRange",this.$onAddRange),t.multiSelect.on("removeRange",this.$onRemoveRange),t.multiSelect.on("multiSelect",this.$onMultiSelect),t.multiSelect.on("singleSelect",this.$onSingleSelect),t.multiSelect.lead.on("change",this.$checkMultiselectChange),t.multiSelect.anchor.on("change",this.$checkMultiselectChange)),t&&this.inMultiSelectMode!=t.selection.inMultiSelectMode&&(t.selection.inMultiSelectMode?this.$onMultiSelect():this.$onSingleSelect())},t.MultiSelect=m,e("./config").defineOptions(d.prototype,"editor",{enableMultiselect:{set:function(e){m(this),e?(this.on("changeSession",this.$multiselectOnSessionChange),this.on("mousedown",o)):(this.off("changeSession",this.$multiselectOnSessionChange),this.off("mousedown",o))},value:!0}})}),ace.define("ace/mode/folding/fold_mode",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../../range").Range,i=t.FoldMode=function(){};(function(){this.foldingStartMarker=null,this.foldingStopMarker=null,this.getFoldWidget=function(e,t,n){var r=e.getLine(n);return this.foldingStartMarker.test(r)?"start":t=="markbeginend"&&this.foldingStopMarker&&this.foldingStopMarker.test(r)?"end":""},this.getFoldWidgetRange=function(e,t,n){return null},this.indentationBlock=function(e,t,n){var i=/\S/,s=e.getLine(t),o=s.search(i);if(o==-1)return;var u=n||s.length,a=e.getLength(),f=t,l=t;while(++tf){var h=e.getLine(l).length;return new r(f,u,l,h)}},this.openingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i+1},u=e.$findClosingBracket(t,o,s);if(!u)return;var a=e.foldWidgets[u.row];return a==null&&(a=e.getFoldWidget(u.row)),a=="start"&&u.row>o.row&&(u.row--,u.column=e.getLine(u.row).length),r.fromPoints(o,u)},this.closingBracketBlock=function(e,t,n,i,s){var o={row:n,column:i},u=e.$findOpeningBracket(t,o);if(!u)return;return u.column++,o.column--,r.fromPoints(u,o)}}).call(i.prototype)}),ace.define("ace/theme/textmate",["require","exports","module","ace/lib/dom"],function(e,t,n){"use strict";t.isDark=!1,t.cssClass="ace-tm",t.cssText='.ace-tm .ace_gutter {background: #f0f0f0;color: #333;}.ace-tm .ace_print-margin {width: 1px;background: #e8e8e8;}.ace-tm .ace_fold {background-color: #6B72E6;}.ace-tm {background-color: #FFFFFF;color: black;}.ace-tm .ace_cursor {color: black;}.ace-tm .ace_invisible {color: rgb(191, 191, 191);}.ace-tm .ace_storage,.ace-tm .ace_keyword {color: blue;}.ace-tm .ace_constant {color: rgb(197, 6, 11);}.ace-tm .ace_constant.ace_buildin {color: rgb(88, 72, 246);}.ace-tm .ace_constant.ace_language {color: rgb(88, 92, 246);}.ace-tm .ace_constant.ace_library {color: rgb(6, 150, 14);}.ace-tm .ace_invalid {background-color: rgba(255, 0, 0, 0.1);color: red;}.ace-tm .ace_support.ace_function {color: rgb(60, 76, 114);}.ace-tm .ace_support.ace_constant {color: rgb(6, 150, 14);}.ace-tm .ace_support.ace_type,.ace-tm .ace_support.ace_class {color: rgb(109, 121, 222);}.ace-tm .ace_keyword.ace_operator {color: rgb(104, 118, 135);}.ace-tm .ace_string {color: rgb(3, 106, 7);}.ace-tm .ace_comment {color: rgb(76, 136, 107);}.ace-tm .ace_comment.ace_doc {color: rgb(0, 102, 255);}.ace-tm .ace_comment.ace_doc.ace_tag {color: rgb(128, 159, 191);}.ace-tm .ace_constant.ace_numeric {color: rgb(0, 0, 205);}.ace-tm .ace_variable {color: rgb(49, 132, 149);}.ace-tm .ace_xml-pe {color: rgb(104, 104, 91);}.ace-tm .ace_entity.ace_name.ace_function {color: #0000A2;}.ace-tm .ace_heading {color: rgb(12, 7, 255);}.ace-tm .ace_list {color:rgb(185, 6, 144);}.ace-tm .ace_meta.ace_tag {color:rgb(0, 22, 142);}.ace-tm .ace_string.ace_regex {color: rgb(255, 0, 0)}.ace-tm .ace_marker-layer .ace_selection {background: rgb(181, 213, 255);}.ace-tm.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px white;border-radius: 2px;}.ace-tm .ace_marker-layer .ace_step {background: rgb(252, 255, 0);}.ace-tm .ace_marker-layer .ace_stack {background: rgb(164, 229, 101);}.ace-tm .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid rgb(192, 192, 192);}.ace-tm .ace_marker-layer .ace_active-line {background: rgba(0, 0, 0, 0.07);}.ace-tm .ace_gutter-active-line {background-color : #dcdcdc;}.ace-tm .ace_marker-layer .ace_selected-word {background: rgb(250, 250, 255);border: 1px solid rgb(200, 200, 250);}.ace-tm .ace_indent-guide {background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bLly//BwAmVgd1/w11/gAAAABJRU5ErkJggg==") right repeat-y;}';var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}),ace.define("ace/line_widgets",["require","exports","module","ace/lib/oop","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e){this.session=e,this.session.widgetManager=this,this.session.getRowLength=this.getRowLength,this.session.$getWidgetScreenLength=this.$getWidgetScreenLength,this.updateOnChange=this.updateOnChange.bind(this),this.renderWidgets=this.renderWidgets.bind(this),this.measureWidgets=this.measureWidgets.bind(this),this.session._changedWidgets=[],this.$onChangeEditor=this.$onChangeEditor.bind(this),this.session.on("change",this.updateOnChange),this.session.on("changeEditor",this.$onChangeEditor)}var r=e("./lib/oop"),i=e("./lib/dom"),s=e("./range").Range;(function(){this.getRowLength=function(e){var t;return this.lineWidgets?t=this.lineWidgets[e]&&this.lineWidgets[e].rowCount||0:t=0,!this.$useWrapMode||!this.$wrapData[e]?1+t:this.$wrapData[e].length+1+t},this.$getWidgetScreenLength=function(){var e=0;return this.lineWidgets.forEach(function(t){t&&t.rowCount&&(e+=t.rowCount)}),e},this.$onChangeEditor=function(e){this.attach(e.editor)},this.attach=function(e){e&&e.widgetManager&&e.widgetManager!=this&&e.widgetManager.detach();if(this.editor==e)return;this.detach(),this.editor=e,e&&(e.widgetManager=this,e.renderer.on("beforeRender",this.measureWidgets),e.renderer.on("afterRender",this.renderWidgets))},this.detach=function(e){var t=this.editor;if(!t)return;this.editor=null,t.widgetManager=null,t.renderer.off("beforeRender",this.measureWidgets),t.renderer.off("afterRender",this.renderWidgets);var n=this.session.lineWidgets;n&&n.forEach(function(e){e&&e.el&&e.el.parentNode&&(e._inDocument=!1,e.el.parentNode.removeChild(e.el))})},this.updateOnChange=function(e){var t=this.session.lineWidgets;if(!t)return;var n=e.data,r=n.range,i=r.start.row,s=r.end.row-i;if(s!==0)if(n.action=="removeText"||n.action=="removeLines"){var o=t.splice(i+1,s);o.forEach(function(e){e&&this.removeLineWidget(e)},this),this.$updateRows()}else{var u=new Array(s);u.unshift(i,0),t.splice.apply(t,u),this.$updateRows()}},this.$updateRows=function(){var e=this.session.lineWidgets;if(!e)return;var t=!0;e.forEach(function(e,n){e&&(t=!1,e.row=n)}),t&&(this.session.lineWidgets=null)},this.addLineWidget=function(e){this.session.lineWidgets||(this.session.lineWidgets=new Array(this.session.getLength())),this.session.lineWidgets[e.row]=e;var t=this.editor.renderer;return e.html&&!e.el&&(e.el=i.createElement("div"),e.el.innerHTML=e.html),e.el&&(i.addCssClass(e.el,"ace_lineWidgetContainer"),e.el.style.position="absolute",e.el.style.zIndex=5,t.container.appendChild(e.el),e._inDocument=!0),e.coverGutter||(e.el.style.zIndex=3),e.pixelHeight||(e.pixelHeight=e.el.offsetHeight),e.rowCount==null&&(e.rowCount=e.pixelHeight/t.layerConfig.lineHeight),this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows(),this.renderWidgets(null,t),e},this.removeLineWidget=function(e){e._inDocument=!1,e.el&&e.el.parentNode&&e.el.parentNode.removeChild(e.el);if(e.editor&&e.editor.destroy)try{e.editor.destroy()}catch(t){}this.session.lineWidgets&&(this.session.lineWidgets[e.row]=undefined),this.session._emit("changeFold",{data:{start:{row:e.row}}}),this.$updateRows()},this.onWidgetChanged=function(e){this.session._changedWidgets.push(e),this.editor&&this.editor.renderer.updateFull()},this.measureWidgets=function(e,t){var n=this.session._changedWidgets,r=t.layerConfig;if(!n||!n.length)return;var i=Infinity;for(var s=0;s0&&!r[i])i--;this.firstRow=n.firstRow,this.lastRow=n.lastRow,t.$cursorLayer.config=n;for(var o=i;o<=s;o++){var u=r[o];if(!u||!u.el)continue;u._inDocument||(u._inDocument=!0,t.container.appendChild(u.el));var a=t.$cursorLayer.getPixelPosition({row:o,column:0},!0).top;u.coverLine||(a+=n.lineHeight*this.session.getRowLineCount(u.row)),u.el.style.top=a-n.offset+"px";var f=u.coverGutter?0:t.gutterWidth;u.fixedWidth||(f-=t.scrollLeft),u.el.style.left=f+"px",u.fixedWidth?u.el.style.right=t.scrollBar.getWidth()+"px":u.el.style.right=""}}}).call(o.prototype),t.LineWidgets=o}),ace.define("ace/ext/error_marker",["require","exports","module","ace/line_widgets","ace/lib/dom","ace/range"],function(e,t,n){"use strict";function o(e,t,n){var r=0,i=e.length-1;while(r<=i){var s=r+i>>1,o=n(t,e[s]);if(o>0)r=s+1;else{if(!(o<0))return s;i=s-1}}return-(r+1)}function u(e,t,n){var r=e.getAnnotations().sort(s.comparePoints);if(!r.length)return;var i=o(r,{row:t,column:-1},s.comparePoints);i<0&&(i=-i-1),i>=r.length-1?i=n>0?0:r.length-1:i===0&&n<0&&(i=r.length-1);var u=r[i];if(!u||!n)return;if(u.row===t){do u=r[i+=n];while(u&&u.row===t);if(!u)return r.slice()}var a=[];t=u.row;do a[n<0?"unshift":"push"](u),u=r[i+=n];while(u&&u.row==t);return a.length&&a}var r=e("../line_widgets").LineWidgets,i=e("../lib/dom"),s=e("../range").Range;t.showErrorMarker=function(e,t){var n=e.session;n.widgetManager||(n.widgetManager=new r(n),n.widgetManager.attach(e));var s=e.getCursorPosition(),o=s.row,a=n.lineWidgets&&n.lineWidgets[o];a?a.destroy():o-=t;var f=u(n,o,t),l;if(f){var c=f[0];s.column=(c.pos&&typeof c.column!="number"?c.pos.sc:c.column)||0,s.row=c.row,l=e.renderer.$gutterLayer.$annotations[s.row]}else{if(a)return;l={text:["Looks good!"],className:"ace_ok"}}e.session.unfold(s.row),e.selection.moveToPosition(s);var h={row:s.row,fixedWidth:!0,coverGutter:!0,el:i.createElement("div")},p=h.el.appendChild(i.createElement("div")),d=h.el.appendChild(i.createElement("div"));d.className="error_widget_arrow "+l.className;var v=e.renderer.$cursorLayer.getPixelPosition(s).left;d.style.left=v+e.renderer.gutterWidth-5+"px",h.el.className="error_widget_wrapper",p.className="error_widget "+l.className,p.innerHTML=l.text.join("
"),p.appendChild(i.createElement("div"));var m=function(e,t,n){if(t===0&&(n==="esc"||n==="return"))return h.destroy(),{command:"null"}};h.destroy=function(){if(e.$mouseHandler.isMousePressed)return;e.keyBinding.removeKeyboardHandler(m),n.widgetManager.removeLineWidget(h),e.off("changeSelection",h.destroy),e.off("changeSession",h.destroy),e.off("mouseup",h.destroy),e.off("change",h.destroy)},e.keyBinding.addKeyboardHandler(m),e.on("changeSelection",h.destroy),e.on("changeSession",h.destroy),e.on("mouseup",h.destroy),e.on("change",h.destroy),e.session.widgetManager.addLineWidget(h),h.el.onmousedown=e.focus.bind(e),e.renderer.scrollCursorIntoView(null,.5,{bottom:h.el.offsetHeight})},i.importCssString(" .error_widget_wrapper { background: inherit; color: inherit; border:none } .error_widget { border-top: solid 2px; border-bottom: solid 2px; margin: 5px 0; padding: 10px 40px; white-space: pre-wrap; } .error_widget.ace_error, .error_widget_arrow.ace_error{ border-color: #ff5a5a } .error_widget.ace_warning, .error_widget_arrow.ace_warning{ border-color: #F1D817 } .error_widget.ace_info, .error_widget_arrow.ace_info{ border-color: #5a5a5a } .error_widget.ace_ok, .error_widget_arrow.ace_ok{ border-color: #5aaa5a } .error_widget_arrow { position: absolute; border: solid 5px; border-top-color: transparent!important; border-right-color: transparent!important; border-left-color: transparent!important; top: -5px; }","")}),ace.define("ace/ace",["require","exports","module","ace/lib/fixoldbrowsers","ace/lib/dom","ace/lib/event","ace/editor","ace/edit_session","ace/undomanager","ace/virtual_renderer","ace/worker/worker_client","ace/keyboard/hash_handler","ace/placeholder","ace/multi_select","ace/mode/folding/fold_mode","ace/theme/textmate","ace/ext/error_marker","ace/config"],function(e,t,n){"use strict";e("./lib/fixoldbrowsers");var r=e("./lib/dom"),i=e("./lib/event"),s=e("./editor").Editor,o=e("./edit_session").EditSession,u=e("./undomanager").UndoManager,a=e("./virtual_renderer").VirtualRenderer;e("./worker/worker_client"),e("./keyboard/hash_handler"),e("./placeholder"),e("./multi_select"),e("./mode/folding/fold_mode"),e("./theme/textmate"),e("./ext/error_marker"),t.config=e("./config"),t.require=e,t.edit=function(e){if(typeof e=="string"){var n=e;e=document.getElementById(n);if(!e)throw new Error("ace.edit can't find div #"+n)}if(e&&e.env&&e.env.editor instanceof s)return e.env.editor;var o="";if(e&&/input|textarea/i.test(e.tagName)){var u=e;o=u.value,e=r.createElement("pre"),u.parentNode.replaceChild(e,u)}else o=r.getInnerText(e),e.innerHTML="";var f=t.createEditSession(o),l=new s(new a(e));l.setSession(f);var c={document:f,editor:l,onResize:l.resize.bind(l,null)};return u&&(c.textarea=u),i.addListener(window,"resize",c.onResize),l.on("destroy",function(){i.removeListener(window,"resize",c.onResize),c.editor.container.env=null}),l.container.env=l.env=c,l},t.createEditSession=function(e,t){var n=new o(e,t);return n.setUndoManager(new u),n},t.EditSession=o,t.UndoManager=u}); (function() { ace.require(["ace/ace"], function(a) { a && a.config.init(true); if (!window.ace) window.ace = a; for (var key in a) if (a.hasOwnProperty(key)) window.ace[key] = a[key]; }); })(); ================================================ FILE: docs/js/ace/mode-javascript.js ================================================ ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text_highlight_rules").TextHighlightRules,s=function(){this.$rules={start:[{token:"comment.doc.tag",regex:"@[\\w\\d_]+"},s.getTagRule(),{defaultToken:"comment.doc",caseInsensitive:!0}]}};r.inherits(s,i),s.getTagRule=function(e){return{token:"comment.doc.tag.storage.type",regex:"\\b(?:TODO|FIXME|XXX|HACK)\\b"}},s.getStartRule=function(e){return{token:"comment.doc",regex:"\\/\\*(?=\\*)",next:e}},s.getEndRule=function(e){return{token:"comment.doc",regex:"\\*\\/",next:e}},t.DocCommentHighlightRules=s}),ace.define("ace/mode/javascript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./doc_comment_highlight_rules").DocCommentHighlightRules,s=e("./text_highlight_rules").TextHighlightRules,o=function(e){var t=this.createKeywordMapper({"variable.language":"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|Namespace|QName|XML|XMLList|ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt|JSON|Math|this|arguments|prototype|window|document",keyword:"const|yield|import|get|set|break|case|catch|continue|default|delete|do|else|finally|for|function|if|in|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|__parent__|__count__|escape|unescape|with|__proto__|class|enum|extends|super|export|implements|private|public|interface|package|protected|static","storage.type":"const|let|var|function","constant.language":"null|Infinity|NaN|undefined","support.function":"alert","constant.language.boolean":"true|false"},"identifier"),n="case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void",r="[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*\\b",s="\\\\(?:x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|[0-2][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.)";this.$rules={no_regex:[{token:"comment",regex:"\\/\\/",next:"line_comment"},i.getStartRule("doc-start"),{token:"comment",regex:/\/\*/,next:"comment"},{token:"string",regex:"'(?=.)",next:"qstring"},{token:"string",regex:'"(?=.)',next:"qqstring"},{token:"constant.numeric",regex:/0[xX][0-9a-fA-F]+\b/},{token:"constant.numeric",regex:/[+-]?\d+(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/},{token:["storage.type","punctuation.operator","support.function","punctuation.operator","entity.name.function","text","keyword.operator"],regex:"("+r+")(\\.)(prototype)(\\.)("+r+")(\\s*)(=)",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+r+")(\\.)("+r+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","keyword.operator","text","storage.type","text","paren.lparen"],regex:"("+r+")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","punctuation.operator","entity.name.function","text","keyword.operator","text","storage.type","text","entity.name.function","text","paren.lparen"],regex:"("+r+")(\\.)("+r+")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",next:"function_arguments"},{token:["storage.type","text","entity.name.function","text","paren.lparen"],regex:"(function)(\\s+)("+r+")(\\s*)(\\()",next:"function_arguments"},{token:["entity.name.function","text","punctuation.operator","text","storage.type","text","paren.lparen"],regex:"("+r+")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:["text","text","storage.type","text","paren.lparen"],regex:"(:)(\\s*)(function)(\\s*)(\\()",next:"function_arguments"},{token:"keyword",regex:"(?:"+n+")\\b",next:"start"},{token:["punctuation.operator","support.function"],regex:/(\.)(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/},{token:["punctuation.operator","support.function.dom"],regex:/(\.)(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/},{token:["punctuation.operator","support.constant"],regex:/(\.)(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/},{token:["support.constant"],regex:/that\b/},{token:["storage.type","punctuation.operator","support.function.firebug"],regex:/(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/},{token:t,regex:r},{token:"keyword.operator",regex:/--|\+\+|===|==|=|!=|!==|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\|\||\?\:|[!$%&*+\-~\/^]=?/,next:"start"},{token:"punctuation.operator",regex:/[?:,;.]/,next:"start"},{token:"paren.lparen",regex:/[\[({]/,next:"start"},{token:"paren.rparen",regex:/[\])}]/},{token:"comment",regex:/^#!.*$/}],start:[i.getStartRule("doc-start"),{token:"comment",regex:"\\/\\*",next:"comment_regex_allowed"},{token:"comment",regex:"\\/\\/",next:"line_comment_regex_allowed"},{token:"string.regexp",regex:"\\/",next:"regex"},{token:"text",regex:"\\s+|^$",next:"start"},{token:"empty",regex:"",next:"no_regex"}],regex:[{token:"regexp.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"string.regexp",regex:"/[sxngimy]*",next:"no_regex"},{token:"invalid",regex:/\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/},{token:"constant.language.escape",regex:/\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/},{token:"constant.language.delimiter",regex:/\|/},{token:"constant.language.escape",regex:/\[\^?/,next:"regex_character_class"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp"}],regex_character_class:[{token:"regexp.charclass.keyword.operator",regex:"\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"},{token:"constant.language.escape",regex:"]",next:"regex"},{token:"constant.language.escape",regex:"-"},{token:"empty",regex:"$",next:"no_regex"},{defaultToken:"string.regexp.charachterclass"}],function_arguments:[{token:"variable.parameter",regex:r},{token:"punctuation.operator",regex:"[, ]+"},{token:"punctuation.operator",regex:"$"},{token:"empty",regex:"",next:"no_regex"}],comment_regex_allowed:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:"start"},{defaultToken:"comment",caseInsensitive:!0}],comment:[i.getTagRule(),{token:"comment",regex:"\\*\\/",next:"no_regex"},{defaultToken:"comment",caseInsensitive:!0}],line_comment_regex_allowed:[i.getTagRule(),{token:"comment",regex:"$|^",next:"start"},{defaultToken:"comment",caseInsensitive:!0}],line_comment:[i.getTagRule(),{token:"comment",regex:"$|^",next:"no_regex"},{defaultToken:"comment",caseInsensitive:!0}],qqstring:[{token:"constant.language.escape",regex:s},{token:"string",regex:"\\\\$",next:"qqstring"},{token:"string",regex:'"|$',next:"no_regex"},{defaultToken:"string"}],qstring:[{token:"constant.language.escape",regex:s},{token:"string",regex:"\\\\$",next:"qstring"},{token:"string",regex:"'|$",next:"no_regex"},{defaultToken:"string"}]},(!e||!e.noES6)&&this.$rules.no_regex.unshift({regex:"[{}]",onMatch:function(e,t,n){this.next=e=="{"?this.nextState:"";if(e=="{"&&n.length)return n.unshift("start",t),"paren";if(e=="}"&&n.length){n.shift(),this.next=n.shift();if(this.next.indexOf("string")!=-1)return"paren.quasi.end"}return e=="{"?"paren.lparen":"paren.rparen"},nextState:"start"},{token:"string.quasi.start",regex:/`/,push:[{token:"constant.language.escape",regex:s},{token:"paren.quasi.start",regex:/\${/,push:"start"},{token:"string.quasi.end",regex:/`/,next:"pop"},{defaultToken:"string.quasi"}]}),this.embedRules(i,"doc-",[i.getEndRule("no_regex")]),this.normalizeRules()};r.inherits(o,s),t.JavaScriptHighlightRules=o}),ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"],function(e,t,n){"use strict";var r=e("../range").Range,i=function(){};(function(){this.checkOutdent=function(e,t){return/^\s+$/.test(e)?/^\s*\}/.test(t):!1},this.autoOutdent=function(e,t){var n=e.getLine(t),i=n.match(/^(\s*\})/);if(!i)return 0;var s=i[1].length,o=e.findMatchingBracket({row:t,column:s});if(!o||o.row==t)return 0;var u=this.$getIndent(e.getLine(o.row));e.replace(new r(t,0,t,s-1),u)},this.$getIndent=function(e){return e.match(/^\s*/)[0]}}).call(i.prototype),t.MatchingBraceOutdent=i}),ace.define("ace/mode/behaviour/cstyle",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator","ace/lib/lang"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../behaviour").Behaviour,s=e("../../token_iterator").TokenIterator,o=e("../../lib/lang"),u=["text","paren.rparen","punctuation.operator"],a=["text","paren.rparen","punctuation.operator","comment"],f,l={},c=function(e){var t=-1;e.multiSelect&&(t=e.selection.index,l.rangeCount!=e.multiSelect.rangeCount&&(l={rangeCount:e.multiSelect.rangeCount}));if(l[t])return f=l[t];f=l[t]={autoInsertedBrackets:0,autoInsertedRow:-1,autoInsertedLineEnd:"",maybeInsertedBrackets:0,maybeInsertedRow:-1,maybeInsertedLineStart:"",maybeInsertedLineEnd:""}},h=function(){this.add("braces","insertion",function(e,t,n,r,i){var s=n.getCursorPosition(),u=r.doc.getLine(s.row);if(i=="{"){c(n);var a=n.getSelectionRange(),l=r.doc.getTextRange(a);if(l!==""&&l!=="{"&&n.getWrapBehavioursEnabled())return{text:"{"+l+"}",selection:!1};if(h.isSaneInsertion(n,r))return/[\]\}\)]/.test(u[s.column])||n.inMultiSelectMode?(h.recordAutoInsert(n,r,"}"),{text:"{}",selection:[1,1]}):(h.recordMaybeInsert(n,r,"{"),{text:"{",selection:[1,1]})}else if(i=="}"){c(n);var p=u.substring(s.column,s.column+1);if(p=="}"){var d=r.$findOpeningBracket("}",{column:s.column+1,row:s.row});if(d!==null&&h.isAutoInsertedClosing(s,u,i))return h.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}else{if(i=="\n"||i=="\r\n"){c(n);var v="";h.isMaybeInsertedClosing(s,u)&&(v=o.stringRepeat("}",f.maybeInsertedBrackets),h.clearMaybeInsertedClosing());var p=u.substring(s.column,s.column+1);if(p==="}"){var m=r.findMatchingBracket({row:s.row,column:s.column+1},"}");if(!m)return null;var g=this.$getIndent(r.getLine(m.row))}else{if(!v){h.clearMaybeInsertedClosing();return}var g=this.$getIndent(u)}var y=g+r.getTabString();return{text:"\n"+y+"\n"+g+v,selection:[1,y.length,1,y.length]}}h.clearMaybeInsertedClosing()}}),this.add("braces","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="{"){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.end.column,i.end.column+1);if(u=="}")return i.end.column++,i;f.maybeInsertedBrackets--}}),this.add("parens","insertion",function(e,t,n,r,i){if(i=="("){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return{text:"("+o+")",selection:!1};if(h.isSaneInsertion(n,r))return h.recordAutoInsert(n,r,")"),{text:"()",selection:[1,1]}}else if(i==")"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f==")"){var l=r.$findOpeningBracket(")",{column:u.column+1,row:u.row});if(l!==null&&h.isAutoInsertedClosing(u,a,i))return h.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("parens","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="("){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==")")return i.end.column++,i}}),this.add("brackets","insertion",function(e,t,n,r,i){if(i=="["){c(n);var s=n.getSelectionRange(),o=r.doc.getTextRange(s);if(o!==""&&n.getWrapBehavioursEnabled())return{text:"["+o+"]",selection:!1};if(h.isSaneInsertion(n,r))return h.recordAutoInsert(n,r,"]"),{text:"[]",selection:[1,1]}}else if(i=="]"){c(n);var u=n.getCursorPosition(),a=r.doc.getLine(u.row),f=a.substring(u.column,u.column+1);if(f=="]"){var l=r.$findOpeningBracket("]",{column:u.column+1,row:u.row});if(l!==null&&h.isAutoInsertedClosing(u,a,i))return h.popAutoInsertedClosing(),{text:"",selection:[1,1]}}}}),this.add("brackets","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&s=="["){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u=="]")return i.end.column++,i}}),this.add("string_dquotes","insertion",function(e,t,n,r,i){if(i=='"'||i=="'"){c(n);var s=i,o=n.getSelectionRange(),u=r.doc.getTextRange(o);if(u!==""&&u!=="'"&&u!='"'&&n.getWrapBehavioursEnabled())return{text:s+u+s,selection:!1};var a=n.getCursorPosition(),f=r.doc.getLine(a.row),l=f.substring(a.column-1,a.column);if(l=="\\")return null;var p=r.getTokens(o.start.row),d=0,v,m=-1;for(var g=0;go.start.column)break;d+=p[g].value.length}if(!v||m<0&&v.type!=="comment"&&(v.type!=="string"||o.start.column!==v.value.length+d-1&&v.value.lastIndexOf(s)===v.value.length-1)){if(!h.isSaneInsertion(n,r))return;return{text:s+s,selection:[1,1]}}if(v&&v.type==="string"){var y=f.substring(a.column,a.column+1);if(y==s)return{text:"",selection:[1,1]}}}}),this.add("string_dquotes","deletion",function(e,t,n,r,i){var s=r.doc.getTextRange(i);if(!i.isMultiLine()&&(s=='"'||s=="'")){c(n);var o=r.doc.getLine(i.start.row),u=o.substring(i.start.column+1,i.start.column+2);if(u==s)return i.end.column++,i}})};h.isSaneInsertion=function(e,t){var n=e.getCursorPosition(),r=new s(t,n.row,n.column);if(!this.$matchTokenType(r.getCurrentToken()||"text",u)){var i=new s(t,n.row,n.column+1);if(!this.$matchTokenType(i.getCurrentToken()||"text",u))return!1}return r.stepForward(),r.getCurrentTokenRow()!==n.row||this.$matchTokenType(r.getCurrentToken()||"text",a)},h.$matchTokenType=function(e,t){return t.indexOf(e.type||e)>-1},h.recordAutoInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isAutoInsertedClosing(r,i,f.autoInsertedLineEnd[0])||(f.autoInsertedBrackets=0),f.autoInsertedRow=r.row,f.autoInsertedLineEnd=n+i.substr(r.column),f.autoInsertedBrackets++},h.recordMaybeInsert=function(e,t,n){var r=e.getCursorPosition(),i=t.doc.getLine(r.row);this.isMaybeInsertedClosing(r,i)||(f.maybeInsertedBrackets=0),f.maybeInsertedRow=r.row,f.maybeInsertedLineStart=i.substr(0,r.column)+n,f.maybeInsertedLineEnd=i.substr(r.column),f.maybeInsertedBrackets++},h.isAutoInsertedClosing=function(e,t,n){return f.autoInsertedBrackets>0&&e.row===f.autoInsertedRow&&n===f.autoInsertedLineEnd[0]&&t.substr(e.column)===f.autoInsertedLineEnd},h.isMaybeInsertedClosing=function(e,t){return f.maybeInsertedBrackets>0&&e.row===f.maybeInsertedRow&&t.substr(e.column)===f.maybeInsertedLineEnd&&t.substr(0,e.column)==f.maybeInsertedLineStart},h.popAutoInsertedClosing=function(){f.autoInsertedLineEnd=f.autoInsertedLineEnd.substr(1),f.autoInsertedBrackets--},h.clearMaybeInsertedClosing=function(){f&&(f.maybeInsertedBrackets=0,f.maybeInsertedRow=-1)},r.inherits(h,i),t.CstyleBehaviour=h}),ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"],function(e,t,n){"use strict";var r=e("../../lib/oop"),i=e("../../range").Range,s=e("./fold_mode").FoldMode,o=t.FoldMode=function(e){e&&(this.foldingStartMarker=new RegExp(this.foldingStartMarker.source.replace(/\|[^|]*?$/,"|"+e.start)),this.foldingStopMarker=new RegExp(this.foldingStopMarker.source.replace(/\|[^|]*?$/,"|"+e.end)))};r.inherits(o,s),function(){this.foldingStartMarker=/(\{|\[)[^\}\]]*$|^\s*(\/\*)/,this.foldingStopMarker=/^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/,this.getFoldWidgetRange=function(e,t,n,r){var i=e.getLine(n),s=i.match(this.foldingStartMarker);if(s){var o=s.index;if(s[1])return this.openingBracketBlock(e,s[1],n,o);var u=e.getCommentFoldRange(n,o+s[0].length,1);return u&&!u.isMultiLine()&&(r?u=this.getSectionRange(e,n):t!="all"&&(u=null)),u}if(t==="markbegin")return;var s=i.match(this.foldingStopMarker);if(s){var o=s.index+s[0].length;return s[1]?this.closingBracketBlock(e,s[1],n,o):e.getCommentFoldRange(n,o,-1)}},this.getSectionRange=function(e,t){var n=e.getLine(t),r=n.search(/\S/),s=t,o=n.length;t+=1;var u=t,a=e.getLength();while(++tf)break;var l=this.getFoldWidgetRange(e,"all",t);if(l){if(l.start.row<=s)break;if(l.isMultiLine())t=l.end.row;else if(r==f)break}u=t}return new i(s,o,u,e.getLine(u).length)}}.call(o.prototype)}),ace.define("ace/mode/javascript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/javascript_highlight_rules","ace/mode/matching_brace_outdent","ace/range","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"],function(e,t,n){"use strict";var r=e("../lib/oop"),i=e("./text").Mode,s=e("./javascript_highlight_rules").JavaScriptHighlightRules,o=e("./matching_brace_outdent").MatchingBraceOutdent,u=e("../range").Range,a=e("../worker/worker_client").WorkerClient,f=e("./behaviour/cstyle").CstyleBehaviour,l=e("./folding/cstyle").FoldMode,c=function(){this.HighlightRules=s,this.$outdent=new o,this.$behaviour=new f,this.foldingRules=new l};r.inherits(c,i),function(){this.lineCommentStart="//",this.blockComment={start:"/*",end:"*/"},this.getNextLineIndent=function(e,t,n){var r=this.$getIndent(t),i=this.getTokenizer().getLineTokens(t,e),s=i.tokens,o=i.state;if(s.length&&s[s.length-1].type=="comment")return r;if(e=="start"||e=="no_regex"){var u=t.match(/^.*(?:\bcase\b.*\:|[\{\(\[])\s*$/);u&&(r+=n)}else if(e=="doc-start"){if(o=="start"||o=="no_regex")return"";var u=t.match(/^\s*(\/?)\*/);u&&(u[1]&&(r+=" "),r+="* ")}return r},this.checkOutdent=function(e,t,n){return this.$outdent.checkOutdent(t,n)},this.autoOutdent=function(e,t,n){this.$outdent.autoOutdent(t,n)},this.createWorker=function(e){var t=new a(["ace"],"ace/mode/javascript_worker","JavaScriptWorker");return t.attachToDocument(e.getDocument()),t.on("jslint",function(t){e.setAnnotations(t.data)}),t.on("terminate",function(){e.clearAnnotations()}),t},this.$id="ace/mode/javascript"}.call(c.prototype),t.Mode=c}) ================================================ FILE: docs/js/ace/theme-tomorrow.js ================================================ ace.define("ace/theme/tomorrow",["require","exports","module","ace/lib/dom"],function(e,t,n){t.isDark=!1,t.cssClass="ace-tomorrow",t.cssText=".ace-tomorrow .ace_gutter {background: #f6f6f6;color: #4D4D4C}.ace-tomorrow .ace_print-margin {width: 1px;background: #f6f6f6}.ace-tomorrow {background-color: #FFFFFF;color: #4D4D4C}.ace-tomorrow .ace_cursor {color: #AEAFAD}.ace-tomorrow .ace_marker-layer .ace_selection {background: #D6D6D6}.ace-tomorrow.ace_multiselect .ace_selection.ace_start {box-shadow: 0 0 3px 0px #FFFFFF;border-radius: 2px}.ace-tomorrow .ace_marker-layer .ace_step {background: rgb(255, 255, 0)}.ace-tomorrow .ace_marker-layer .ace_bracket {margin: -1px 0 0 -1px;border: 1px solid #D1D1D1}.ace-tomorrow .ace_marker-layer .ace_active-line {background: #EFEFEF}.ace-tomorrow .ace_gutter-active-line {background-color : #dcdcdc}.ace-tomorrow .ace_marker-layer .ace_selected-word {border: 1px solid #D6D6D6}.ace-tomorrow .ace_invisible {color: #D1D1D1}.ace-tomorrow .ace_keyword,.ace-tomorrow .ace_meta,.ace-tomorrow .ace_storage,.ace-tomorrow .ace_storage.ace_type,.ace-tomorrow .ace_support.ace_type {color: #8959A8}.ace-tomorrow .ace_keyword.ace_operator {color: #3E999F}.ace-tomorrow .ace_constant.ace_character,.ace-tomorrow .ace_constant.ace_language,.ace-tomorrow .ace_constant.ace_numeric,.ace-tomorrow .ace_keyword.ace_other.ace_unit,.ace-tomorrow .ace_support.ace_constant,.ace-tomorrow .ace_variable.ace_parameter {color: #F5871F}.ace-tomorrow .ace_constant.ace_other {color: #666969}.ace-tomorrow .ace_invalid {color: #FFFFFF;background-color: #C82829}.ace-tomorrow .ace_invalid.ace_deprecated {color: #FFFFFF;background-color: #8959A8}.ace-tomorrow .ace_fold {background-color: #4271AE;border-color: #4D4D4C}.ace-tomorrow .ace_entity.ace_name.ace_function,.ace-tomorrow .ace_support.ace_function,.ace-tomorrow .ace_variable {color: #4271AE}.ace-tomorrow .ace_support.ace_class,.ace-tomorrow .ace_support.ace_type {color: #C99E00}.ace-tomorrow .ace_heading,.ace-tomorrow .ace_markup.ace_heading,.ace-tomorrow .ace_string {color: #718C00}.ace-tomorrow .ace_entity.ace_name.ace_tag,.ace-tomorrow .ace_entity.ace_other.ace_attribute-name,.ace-tomorrow .ace_meta.ace_tag,.ace-tomorrow .ace_string.ace_regexp,.ace-tomorrow .ace_variable {color: #C82829}.ace-tomorrow .ace_comment {color: #8E908C}.ace-tomorrow .ace_indent-guide {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAE0lEQVQImWP4////f4bdu3f/BwAlfgctduB85QAAAABJRU5ErkJggg==) right repeat-y}";var r=e("../lib/dom");r.importCssString(t.cssText,t.cssClass)}) ================================================ FILE: docs/js/ace/worker-javascript.js ================================================ "no use strict";(function(e){if(typeof e.window!="undefined"&&e.document)return;e.console=function(){var e=Array.prototype.slice.call(arguments,0);postMessage({type:"log",data:e})},e.console.error=e.console.warn=e.console.log=e.console.trace=e.console,e.window=e,e.ace=e,e.onerror=function(e,t,n,r,i){postMessage({type:"error",data:{message:e,file:t,line:n,col:r,stack:i.stack}})},e.normalizeModule=function(t,n){if(n.indexOf("!")!==-1){var r=n.split("!");return e.normalizeModule(t,r[0])+"!"+e.normalizeModule(t,r[1])}if(n.charAt(0)=="."){var i=t.split("/").slice(0,-1).join("/");n=(i?i+"/":"")+n;while(n.indexOf(".")!==-1&&s!=n){var s=n;n=n.replace(/^\.\//,"").replace(/\/\.\//,"/").replace(/[^\/]+\/\.\.\//,"")}}return n},e.require=function(t,n){n||(n=t,t=null);if(!n.charAt)throw new Error("worker.js require() accepts only (parentId, id) as arguments");n=e.normalizeModule(t,n);var r=e.require.modules[n];if(r)return r.initialized||(r.initialized=!0,r.exports=r.factory().exports),r.exports;var i=n.split("/");if(!e.require.tlns)return console.log("unable to load "+n);i[0]=e.require.tlns[i[0]]||i[0];var s=i.join("/")+".js";return e.require.id=n,importScripts(s),e.require(t,n)},e.require.modules={},e.require.tlns={},e.define=function(t,n,r){arguments.length==2?(r=n,typeof t!="string"&&(n=t,t=e.require.id)):arguments.length==1&&(r=t,n=[],t=e.require.id);if(typeof r!="function"){e.require.modules[t]={exports:r,initialized:!0};return}n.length||(n=["require","exports","module"]);var i=function(n){return e.require(t,n)};e.require.modules[t]={exports:{},factory:function(){var e=this,t=r.apply(this,n.map(function(t){switch(t){case"require":return i;case"exports":return e.exports;case"module":return e;default:return i(t)}}));return t&&(e.exports=t),e}}},e.define.amd={},e.initBaseUrls=function(t){require.tlns=t},e.initSender=function(){var n=e.require("ace/lib/event_emitter").EventEmitter,r=e.require("ace/lib/oop"),i=function(){};return function(){r.implement(this,n),this.callback=function(e,t){postMessage({type:"call",id:t,data:e})},this.emit=function(e,t){postMessage({type:"event",name:e,data:t})}}.call(i.prototype),new i};var t=e.main=null,n=e.sender=null;e.onmessage=function(r){var i=r.data;if(i.command){if(!t[i.command])throw new Error("Unknown command:"+i.command);t[i.command].apply(t,i.args)}else if(i.init){initBaseUrls(i.tlns),require("ace/lib/es5-shim"),n=e.sender=initSender();var s=require(i.module)[i.classname];t=e.main=new s(n)}else i.event&&n&&n._signal(i.event,i.data)}})(this),ace.define("ace/lib/oop",["require","exports","module"],function(e,t,n){"use strict";t.inherits=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})},t.mixin=function(e,t){for(var n in t)e[n]=t[n];return e},t.implement=function(e,n){t.mixin(e,n)}}),ace.define("ace/lib/event_emitter",["require","exports","module"],function(e,t,n){"use strict";var r={},i=function(){this.propagationStopped=!0},s=function(){this.defaultPrevented=!0};r._emit=r._dispatchEvent=function(e,t){this._eventRegistry||(this._eventRegistry={}),this._defaultHandlers||(this._defaultHandlers={});var n=this._eventRegistry[e]||[],r=this._defaultHandlers[e];if(!n.length&&!r)return;if(typeof t!="object"||!t)t={};t.type||(t.type=e),t.stopPropagation||(t.stopPropagation=i),t.preventDefault||(t.preventDefault=s),n=n.slice();for(var o=0;o ["+this.end.row+"/"+this.end.column+"]"},this.contains=function(e,t){return this.compare(e,t)==0},this.compareRange=function(e){var t,n=e.end,r=e.start;return t=this.compare(n.row,n.column),t==1?(t=this.compare(r.row,r.column),t==1?2:t==0?1:0):t==-1?-2:(t=this.compare(r.row,r.column),t==-1?-1:t==1?42:0)},this.comparePoint=function(e){return this.compare(e.row,e.column)},this.containsRange=function(e){return this.comparePoint(e.start)==0&&this.comparePoint(e.end)==0},this.intersects=function(e){var t=this.compareRange(e);return t==-1||t==0||t==1},this.isEnd=function(e,t){return this.end.row==e&&this.end.column==t},this.isStart=function(e,t){return this.start.row==e&&this.start.column==t},this.setStart=function(e,t){typeof e=="object"?(this.start.column=e.column,this.start.row=e.row):(this.start.row=e,this.start.column=t)},this.setEnd=function(e,t){typeof e=="object"?(this.end.column=e.column,this.end.row=e.row):(this.end.row=e,this.end.column=t)},this.inside=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)||this.isStart(e,t)?!1:!0:!1},this.insideStart=function(e,t){return this.compare(e,t)==0?this.isEnd(e,t)?!1:!0:!1},this.insideEnd=function(e,t){return this.compare(e,t)==0?this.isStart(e,t)?!1:!0:!1},this.compare=function(e,t){return!this.isMultiLine()&&e===this.start.row?tthis.end.column?1:0:ethis.end.row?1:this.start.row===e?t>=this.start.column?0:-1:this.end.row===e?t<=this.end.column?0:1:0},this.compareStart=function(e,t){return this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.compareEnd=function(e,t){return this.end.row==e&&this.end.column==t?1:this.compare(e,t)},this.compareInside=function(e,t){return this.end.row==e&&this.end.column==t?1:this.start.row==e&&this.start.column==t?-1:this.compare(e,t)},this.clipRows=function(e,t){if(this.end.row>t)var n={row:t+1,column:0};else if(this.end.rowt)var r={row:t+1,column:0};else if(this.start.rowthis.row)return;if(n.start.row==this.row&&n.start.column>this.column)return;var r=this.row,i=this.column,s=n.start,o=n.end;if(t.action==="insertText")if(s.row===r&&s.column<=i){if(s.column!==i||!this.$insertRight)s.row===o.row?i+=o.column-s.column:(i-=s.column,r+=o.row-s.row)}else s.row!==o.row&&s.row=i?i=s.column:i=Math.max(0,i-(o.column-s.column)):s.row!==o.row&&s.row=this.document.getLength()?(n.row=Math.max(0,this.document.getLength()-1),n.column=this.document.getLine(n.row).length):e<0?(n.row=0,n.column=0):(n.row=e,n.column=Math.min(this.document.getLine(n.row).length,Math.max(0,t))),t<0&&(n.column=0),n}}).call(s.prototype)}),ace.define("ace/document",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/range","ace/anchor"],function(e,t,n){"use strict";var r=e("./lib/oop"),i=e("./lib/event_emitter").EventEmitter,s=e("./range").Range,o=e("./anchor").Anchor,u=function(e){this.$lines=[],e.length===0?this.$lines=[""]:Array.isArray(e)?this._insertLines(0,e):this.insert({row:0,column:0},e)};(function(){r.implement(this,i),this.setValue=function(e){var t=this.getLength();this.remove(new s(0,0,t,this.getLine(t-1).length)),this.insert({row:0,column:0},e)},this.getValue=function(){return this.getAllLines().join(this.getNewLineCharacter())},this.createAnchor=function(e,t){return new o(this,e,t)},"aaa".split(/a/).length===0?this.$split=function(e){return e.replace(/\r\n|\r/g,"\n").split("\n")}:this.$split=function(e){return e.split(/\r\n|\r|\n/)},this.$detectNewLine=function(e){var t=e.match(/^.*?(\r\n|\r|\n)/m);this.$autoNewLine=t?t[1]:"\n",this._signal("changeNewLineMode")},this.getNewLineCharacter=function(){switch(this.$newLineMode){case"windows":return"\r\n";case"unix":return"\n";default:return this.$autoNewLine||"\n"}},this.$autoNewLine="",this.$newLineMode="auto",this.setNewLineMode=function(e){if(this.$newLineMode===e)return;this.$newLineMode=e,this._signal("changeNewLineMode")},this.getNewLineMode=function(){return this.$newLineMode},this.isNewLine=function(e){return e=="\r\n"||e=="\r"||e=="\n"},this.getLine=function(e){return this.$lines[e]||""},this.getLines=function(e,t){return this.$lines.slice(e,t+1)},this.getAllLines=function(){return this.getLines(0,this.getLength())},this.getLength=function(){return this.$lines.length},this.getTextRange=function(e){if(e.start.row==e.end.row)return this.getLine(e.start.row).substring(e.start.column,e.end.column);var t=this.getLines(e.start.row,e.end.row);t[0]=(t[0]||"").substring(e.start.column);var n=t.length-1;return e.end.row-e.start.row==n&&(t[n]=t[n].substring(0,e.end.column)),t.join(this.getNewLineCharacter())},this.$clipPosition=function(e){var t=this.getLength();return e.row>=t?(e.row=Math.max(0,t-1),e.column=this.getLine(t-1).length):e.row<0&&(e.row=0),e},this.insert=function(e,t){if(!t||t.length===0)return e;e=this.$clipPosition(e),this.getLength()<=1&&this.$detectNewLine(t);var n=this.$split(t),r=n.splice(0,1)[0],i=n.length==0?null:n.splice(n.length-1,1)[0];return e=this.insertInLine(e,r),i!==null&&(e=this.insertNewLine(e),e=this._insertLines(e.row,n),e=this.insertInLine(e,i||"")),e},this.insertLines=function(e,t){return e>=this.getLength()?this.insert({row:e,column:0},"\n"+t.join("\n")):this._insertLines(Math.max(e,0),t)},this._insertLines=function(e,t){if(t.length==0)return{row:e,column:0};while(t.length>61440){var n=this._insertLines(e,t.slice(0,61440));t=t.slice(61440),e=n.row}var r=[e,0];r.push.apply(r,t),this.$lines.splice.apply(this.$lines,r);var i=new s(e,0,e+t.length,0),o={action:"insertLines",range:i,lines:t};return this._signal("change",{data:o}),i.end},this.insertNewLine=function(e){e=this.$clipPosition(e);var t=this.$lines[e.row]||"";this.$lines[e.row]=t.substring(0,e.column),this.$lines.splice(e.row+1,0,t.substring(e.column,t.length));var n={row:e.row+1,column:0},r={action:"insertText",range:s.fromPoints(e,n),text:this.getNewLineCharacter()};return this._signal("change",{data:r}),n},this.insertInLine=function(e,t){if(t.length==0)return e;var n=this.$lines[e.row]||"";this.$lines[e.row]=n.substring(0,e.column)+t+n.substring(e.column);var r={row:e.row,column:e.column+t.length},i={action:"insertText",range:s.fromPoints(e,r),text:t};return this._signal("change",{data:i}),r},this.remove=function(e){e instanceof s||(e=s.fromPoints(e.start,e.end)),e.start=this.$clipPosition(e.start),e.end=this.$clipPosition(e.end);if(e.isEmpty())return e.start;var t=e.start.row,n=e.end.row;if(e.isMultiLine()){var r=e.start.column==0?t:t+1,i=n-1;e.end.column>0&&this.removeInLine(n,0,e.end.column),i>=r&&this._removeLines(r,i),r!=t&&(this.removeInLine(t,e.start.column,this.getLine(t).length),this.removeNewLine(e.start.row))}else this.removeInLine(t,e.start.column,e.end.column);return e.start},this.removeInLine=function(e,t,n){if(t==n)return;var r=new s(e,t,e,n),i=this.getLine(e),o=i.substring(t,n),u=i.substring(0,t)+i.substring(n,i.length);this.$lines.splice(e,1,u);var a={action:"removeText",range:r,text:o};return this._signal("change",{data:a}),r.start},this.removeLines=function(e,t){return e<0||t>=this.getLength()?this.remove(new s(e,0,t+1,0)):this._removeLines(e,t)},this._removeLines=function(e,t){var n=new s(e,0,t+1,0),r=this.$lines.splice(e,t-e+1),i={action:"removeLines",range:n,nl:this.getNewLineCharacter(),lines:r};return this._signal("change",{data:i}),r},this.removeNewLine=function(e){var t=this.getLine(e),n=this.getLine(e+1),r=new s(e,t.length,e+1,0),i=t+n;this.$lines.splice(e,2,i);var o={action:"removeText",range:r,text:this.getNewLineCharacter()};this._signal("change",{data:o})},this.replace=function(e,t){e instanceof s||(e=s.fromPoints(e.start,e.end));if(t.length==0&&e.isEmpty())return e.start;if(t==this.getTextRange(e))return e.end;this.remove(e);if(t)var n=this.insert(e.start,t);else n=e.start;return n},this.applyDeltas=function(e){for(var t=0;t=0;t--){var n=e[t],r=s.fromPoints(n.range.start,n.range.end);n.action=="insertLines"?this._removeLines(r.start.row,r.end.row-1):n.action=="insertText"?this.remove(r):n.action=="removeLines"?this._insertLines(r.start.row,n.lines):n.action=="removeText"&&this.insert(r.start,n.text)}},this.indexToPosition=function(e,t){var n=this.$lines||this.getAllLines(),r=this.getNewLineCharacter().length;for(var i=t||0,s=n.length;i0){t&1&&(n+=e);if(t>>=1)e+=e}return n};var r=/^\s\s*/,i=/\s\s*$/;t.stringTrimLeft=function(e){return e.replace(r,"")},t.stringTrimRight=function(e){return e.replace(i,"")},t.copyObject=function(e){var t={};for(var n in e)t[n]=e[n];return t},t.copyArray=function(e){var t=[];for(var n=0,r=e.length;n=65&&i<=90||i===95||i>=97&&i<=122;var s=[];for(var i=0;i<128;i++)s[i]=r[i]||i>=48&&i<=57;t.exports={asciiIdentifierStartTable:r,asciiIdentifierPartTable:s}},{}],2:[function(e,t,n){(function(){var e=this,r=e._,i={},s=Array.prototype,o=Object.prototype,u=Function.prototype,a=s.push,f=s.slice,l=s.concat,c=o.toString,h=o.hasOwnProperty,p=s.forEach,d=s.map,v=s.reduce,m=s.reduceRight,g=s.filter,y=s.every,b=s.some,w=s.indexOf,E=s.lastIndexOf,S=Array.isArray,x=Object.keys,T=u.bind,N=function(e){if(e instanceof N)return e;if(!(this instanceof N))return new N(e);this._wrapped=e};typeof n!="undefined"?(typeof t!="undefined"&&t.exports&&(n=t.exports=N),n._=N):e._=N,N.VERSION="1.6.0";var C=N.each=N.forEach=function(e,t,n){if(e==null)return e;if(p&&e.forEach===p)e.forEach(t,n);else if(e.length===+e.length){for(var r=0,s=e.length;r2;e==null&&(e=[]);if(v&&e.reduce===v)return r&&(t=N.bind(t,r)),i?e.reduce(t,n):e.reduce(t);C(e,function(e,s,o){i?n=t.call(r,n,e,s,o):(n=e,i=!0)});if(!i)throw new TypeError(k);return n},N.reduceRight=N.foldr=function(e,t,n,r){var i=arguments.length>2;e==null&&(e=[]);if(m&&e.reduceRight===m)return r&&(t=N.bind(t,r)),i?e.reduceRight(t,n):e.reduceRight(t);var s=e.length;if(s!==+s){var o=N.keys(e);s=o.length}C(e,function(u,a,f){a=o?o[--s]:--s,i?n=t.call(r,n,e[a],a,f):(n=e[a],i=!0)});if(!i)throw new TypeError(k);return n},N.find=N.detect=function(e,t,n){var r;return L(e,function(e,i,s){if(t.call(n,e,i,s))return r=e,!0}),r},N.filter=N.select=function(e,t,n){var r=[];return e==null?r:g&&e.filter===g?e.filter(t,n):(C(e,function(e,i,s){t.call(n,e,i,s)&&r.push(e)}),r)},N.reject=function(e,t,n){return N.filter(e,function(e,r,i){return!t.call(n,e,r,i)},n)},N.every=N.all=function(e,t,n){t||(t=N.identity);var r=!0;return e==null?r:y&&e.every===y?e.every(t,n):(C(e,function(e,s,o){if(!(r=r&&t.call(n,e,s,o)))return i}),!!r)};var L=N.some=N.any=function(e,t,n){t||(t=N.identity);var r=!1;return e==null?r:b&&e.some===b?e.some(t,n):(C(e,function(e,s,o){if(r||(r=t.call(n,e,s,o)))return i}),!!r)};N.contains=N.include=function(e,t){return e==null?!1:w&&e.indexOf===w?e.indexOf(t)!=-1:L(e,function(e){return e===t})},N.invoke=function(e,t){var n=f.call(arguments,2),r=N.isFunction(t);return N.map(e,function(e){return(r?t:e[t]).apply(e,n)})},N.pluck=function(e,t){return N.map(e,N.property(t))},N.where=function(e,t){return N.filter(e,N.matches(t))},N.findWhere=function(e,t){return N.find(e,N.matches(t))},N.max=function(e,t,n){if(!t&&N.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.max.apply(Math,e);var r=-Infinity,i=-Infinity;return C(e,function(e,s,o){var u=t?t.call(n,e,s,o):e;u>i&&(r=e,i=u)}),r},N.min=function(e,t,n){if(!t&&N.isArray(e)&&e[0]===+e[0]&&e.length<65535)return Math.min.apply(Math,e);var r=Infinity,i=Infinity;return C(e,function(e,s,o){var u=t?t.call(n,e,s,o):e;ur||n===void 0)return 1;if(n>>1;n.call(r,e[u])=0;n--)t=[e[n].apply(this,t)];return t[0]}},N.after=function(e,t){return function(){if(--e<1)return t.apply(this,arguments)}},N.keys=function(e){if(!N.isObject(e))return[];if(x)return x(e);var t=[];for(var n in e)N.has(e,n)&&t.push(n);return t},N.values=function(e){var t=N.keys(e),n=t.length,r=new Array(n);for(var i=0;i":">",'"':""","'":"'"}};P.unescape=N.invert(P.escape);var H={escape:new RegExp("["+N.keys(P.escape).join("")+"]","g"),unescape:new RegExp("("+N.keys(P.unescape).join("|")+")","g")};N.each(["escape","unescape"],function(e){N[e]=function(t){return t==null?"":(""+t).replace(H[e],function(t){return P[e][t]})}}),N.result=function(e,t){if(e==null)return void 0;var n=e[t];return N.isFunction(n)?n.call(e):n},N.mixin=function(e){C(N.functions(e),function(t){var n=N[t]=e[t];N.prototype[t]=function(){var e=[this._wrapped];return a.apply(e,arguments),q.call(this,n.apply(N,e))}})};var B=0;N.uniqueId=function(e){var t=++B+"";return e?e+t:t},N.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var j=/(.)^/,F={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},I=/\\|'|\r|\n|\t|\u2028|\u2029/g;N.template=function(e,t,n){var r;n=N.defaults({},n,N.templateSettings);var i=new RegExp([(n.escape||j).source,(n.interpolate||j).source,(n.evaluate||j).source].join("|")+"|$","g"),s=0,o="__p+='";e.replace(i,function(t,n,r,i,u){return o+=e.slice(s,u).replace(I,function(e){return"\\"+F[e]}),n&&(o+="'+\n((__t=("+n+"))==null?'':_.escape(__t))+\n'"),r&&(o+="'+\n((__t=("+r+"))==null?'':__t)+\n'"),i&&(o+="';\n"+i+"\n__p+='"),s=u+t.length,t}),o+="';\n",n.variable||(o="with(obj||{}){\n"+o+"}\n"),o="var __t,__p='',__j=Array.prototype.join,print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{r=new Function(n.variable||"obj","_",o)}catch(u){throw u.source=o,u}if(t)return r(t,N);var a=function(e){return r.call(this,e,N)};return a.source="function("+(n.variable||"obj")+"){\n"+o+"}",a},N.chain=function(e){return N(e).chain()};var q=function(e){return this._chain?N(e).chain():e};N.mixin(N),C(["pop","push","reverse","shift","sort","splice","unshift"],function(e){var t=s[e];N.prototype[e]=function(){var n=this._wrapped;return t.apply(n,arguments),(e=="shift"||e=="splice")&&n.length===0&&delete n[0],q.call(this,n)}}),C(["concat","join","slice"],function(e){var t=s[e];N.prototype[e]=function(){return q.call(this,t.apply(this._wrapped,arguments))}}),N.extend(N.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),typeof define=="function"&&define.amd&&ace.define("underscore",[],function(){return N})}).call(this)},{}],3:[function(e,t,n){var r=e("underscore"),i=e("events"),s=e("./vars.js"),o=e("./messages.js"),u=e("./lex.js").Lexer,a=e("./reg.js"),f=e("./state.js").state,l=e("./style.js"),c=function(){"use strict";function I(e,t){return e=e.trim(),/^[+-]W\d{3}$/g.test(e)?!0:p[e]===undefined&&h[e]===undefined&&t.type!=="jslint"&&!m[e]?(G("E001",t,e),!1):!0}function q(e){return Object.prototype.toString.call(e)==="[object String]"}function R(e,t){return e?!e.identifier||e.value!==t?!1:!0:!1}function U(e){if(!e.reserved)return!1;var t=e.meta;if(t&&t.isFutureReservedWord&&f.option.inES5()){if(!t.es5)return!1;if(t.strictOnly&&!f.option.strict&&!f.directive["use strict"])return!1;if(e.isProperty)return!1}return!0}function z(e,t){return e.replace(/\{([^{}]*)\}/g,function(e,n){var r=t[n];return typeof r=="string"||typeof r=="number"?r:e})}function W(e,t){Object.keys(t).forEach(function(n){if(r.has(c.blacklist,n))return;e[n]=t[n]})}function X(){f.option.esnext&&W(M,s.newEcmaIdentifiers),f.option.couch&&W(M,s.couch),f.option.qunit&&W(M,s.qunit),f.option.rhino&&W(M,s.rhino),f.option.shelljs&&(W(M,s.shelljs),W(M,s.node)),f.option.typed&&W(M,s.typed),f.option.phantom&&W(M,s.phantom),f.option.prototypejs&&W(M,s.prototypejs),f.option.node&&(W(M,s.node),W(M,s.typed)),f.option.devel&&W(M,s.devel),f.option.dojo&&W(M,s.dojo),f.option.browser&&(W(M,s.browser),W(M,s.typed)),f.option.nonstandard&&W(M,s.nonstandard),f.option.jasmine&&W(M,s.jasmine),f.option.jquery&&W(M,s.jquery),f.option.mootools&&W(M,s.mootools),f.option.worker&&W(M,s.worker),f.option.wsh&&W(M,s.wsh),f.option.globalstrict&&f.option.strict!==!1&&(f.option.strict=!0),f.option.yui&&W(M,s.yui),f.option.mocha&&W(M,s.mocha),f.option.inMoz=function(e){return f.option.moz},f.option.inESNext=function(e){return f.option.moz||f.option.esnext},f.option.inES5=function(){return!f.option.es3},f.option.inES3=function(e){return e?!f.option.moz&&!f.option.esnext&&f.option.es3:f.option.es3}}function V(e,t,n){var r=Math.floor(t/f.lines.length*100),i=o.errors[e].desc;throw{name:"JSHintError",line:t,character:n,message:i+" ("+r+"% scanned).",raw:i,code:e}}function $(e,t,n,r){return c.undefs.push([e,t,n,r])}function J(){var e=f.ignoredLines;if(r.isEmpty(e))return;c.errors=r.reject(c.errors,function(t){return e[t.line]})}function K(e,t,n,r,i,s){var u,a,l,h;if(/^W\d{3}$/.test(e)){if(f.ignored[e])return;h=o.warnings[e]}else/E\d{3}/.test(e)?h=o.errors[e]:/I\d{3}/.test(e)&&(h=o.info[e]);return t=t||f.tokens.next,t.id==="(end)"&&(t=f.tokens.curr),a=t.line||0,u=t.from||0,l={id:"(error)",raw:h.desc,code:h.code,evidence:f.lines[a-1]||"",line:a,character:u,scope:c.scope,a:n,b:r,c:i,d:s},l.reason=z(h.desc,l),c.errors.push(l),J(),c.errors.length>=f.option.maxerr&&V("E043",a,u),l}function Q(e,t,n,r,i,s,o){return K(e,{line:t,from:n},r,i,s,o)}function G(e,t,n,r,i,s){K(e,t,n,r,i,s)}function Y(e,t,n,r,i,s,o){return G(e,{line:t,from:n},r,i,s,o)}function Z(e,t){var n;return n={id:"(internal)",elem:e,value:t},c.internals.push(n),n}function et(e,t){t=t||{};var n=t.type,i=t.token,s=t.islet;n==="exception"&&r.has(w["(context)"],e)&&w[e]!==!0&&!f.option.node&&K("W002",f.tokens.next,e),r.has(w,e)&&!w["(global)"]&&(w[e]===!0?f.option.latedef&&(f.option.latedef===!0&&r.contains([w[e],n],"unction")||!r.contains([w[e],n],"unction"))&&K("W003",f.tokens.next,e):((!f.option.shadow||r.contains(["inner","outer"],f.option.shadow))&&n!=="exception"||w["(blockscope)"].getlabel(e))&&K("W004",f.tokens.next,e)),w["(context)"]&&r.has(w["(context)"],e)&&n!=="function"&&f.option.shadow==="outer"&&K("W123",f.tokens.next,e),s?w["(blockscope)"].current.add(e,n,f.tokens.curr):(w["(blockscope)"].shadow(e),w[e]=n,i&&(w["(tokens)"][e]=i),Xt(w,e,{unused:t.unused||!1}),w["(global)"]?(S[e]=w,r.has(x,e)&&(f.option.latedef&&(f.option.latedef===!0&&r.contains([w[e],n],"unction")||!r.contains([w[e],n],"unction"))&&K("W003",f.tokens.next,e),delete x[e])):D[e]=w)}function tt(){var e=f.tokens.next,t=e.body.match(/(-\s+)?[^\s,:]+(?:\s*:\s*(-\s+)?[^\s,]+)?/g)||[],n={};if(e.type==="globals"){t.forEach(function(e){e=e.split(":");var t=(e[0]||"").trim(),r=(e[1]||"").trim();t.charAt(0)==="-"?(t=t.slice(1),r=!1,c.blacklist[t]=t,delete M[t]):n[t]=r==="true"}),W(M,n);for(var i in n)r.has(n,i)&&(g[i]=e)}e.type==="exported"&&t.forEach(function(e){y[e]=!0}),e.type==="members"&&(A=A||{},t.forEach(function(e){var t=e.charAt(0),n=e.charAt(e.length-1);t===n&&(t==='"'||t==="'")&&(e=e.substr(1,e.length-2).replace('\\"','"')),A[e]=!1}));var s=["maxstatements","maxparams","maxdepth","maxcomplexity","maxerr","maxlen","indent"];if(e.type==="jshint"||e.type==="jslint")t.forEach(function(t){t=t.split(":");var n=(t[0]||"").trim(),r=(t[1]||"").trim();if(!I(n,e))return;if(s.indexOf(n)>=0){if(r!=="false"){r=+r;if(typeof r!="number"||!isFinite(r)||r<=0||Math.floor(r)!==r){G("E032",e,t[1].trim());return}f.option[n]=r}else f.option[n]=n==="indent"?4:!1;return}if(n==="validthis"){if(w["(global)"])return void G("E009");if(r!=="true"&&r!=="false")return void G("E002",e);f.option.validthis=r==="true";return}if(n==="quotmark"){switch(r){case"true":case"false":f.option.quotmark=r==="true";break;case"double":case"single":f.option.quotmark=r;break;default:G("E002",e)}return}if(n==="shadow"){switch(r){case"true":f.option.shadow=!0;break;case"outer":f.option.shadow="outer";break;case"false":case"inner":f.option.shadow="inner";break;default:G("E002",e)}return}if(n==="unused"){switch(r){case"true":f.option.unused=!0;break;case"false":f.option.unused=!1;break;case"vars":case"strict":f.option.unused=r;break;default:G("E002",e)}return}if(n==="latedef"){switch(r){case"true":f.option.latedef=!0;break;case"false":f.option.latedef=!1;break;case"nofunc":f.option.latedef="nofunc";break;default:G("E002",e)}return}if(n==="ignore"){switch(r){case"start":f.ignoreLinterErrors=!0;break;case"end":f.ignoreLinterErrors=!1;break;case"line":f.ignoredLines[e.line]=!0,J();break;default:G("E002",e)}return}var i=/^([+-])(W\d{3})$/g.exec(n);if(i){f.ignored[i[2]]=i[1]==="-";return}var o;if(r==="true"||r==="false"){e.type==="jslint"?(o=v[n]||n,f.option[o]=r==="true",d[o]!==undefined&&(f.option[o]=!f.option[o])):f.option[n]=r==="true",n==="newcap"&&(f.option["(explicitNewcap)"]=!0);return}G("E002",e)}),X()}function nt(e){var t=e||0,n=0,r;while(n<=t)r=C[n],r||(r=C[n]=k.token()),n+=1;return r}function rt(t,n){switch(f.tokens.curr.id){case"(number)":f.tokens.next.id==="."&&K("W005",f.tokens.curr);break;case"-":(f.tokens.next.id==="-"||f.tokens.next.id==="--")&&K("W006");break;case"+":(f.tokens.next.id==="+"||f.tokens.next.id==="++")&&K("W007")}if(f.tokens.curr.type==="(string)"||f.tokens.curr.identifier)e=f.tokens.curr.value;t&&f.tokens.next.id!==t&&(n?f.tokens.next.id==="(end)"?G("E019",n,n.id):G("E020",f.tokens.next,t,n.id,n.line,f.tokens.next.value):(f.tokens.next.type!=="(identifier)"||f.tokens.next.value!==t)&&K("W116",f.tokens.next,t,f.tokens.next.value)),f.tokens.prev=f.tokens.curr,f.tokens.curr=f.tokens.next;for(;;){f.tokens.next=C.shift()||k.token(),f.tokens.next||V("E041",f.tokens.curr.line);if(f.tokens.next.id==="(end)"||f.tokens.next.id==="(error)")return;f.tokens.next.check&&f.tokens.next.check();if(f.tokens.next.isSpecial)tt();else if(f.tokens.next.id!=="(endline)")break}}function it(e){return e.infix||!e.identifier&&!!e.led}function st(){var e=f.tokens.curr,t=f.tokens.next;return t.id===";"||t.id==="}"||t.id===":"?!0:it(t)===it(e)||e.id==="yield"&&f.option.inMoz(!0)?e.line!==t.line:!1}function ot(t,n){var i,s=!1,o=!1,u=!1;!n&&f.tokens.next.value==="let"&&nt(0).value==="("&&(f.option.inMoz(!0)||K("W118",f.tokens.next,"let expressions"),u=!0,w["(blockscope)"].stack(),rt("let"),rt("("),f.syntax.let.fud.call(f.syntax.let.fud,!1),rt(")")),f.tokens.next.id==="(end)"&&G("E006",f.tokens.curr);var a=f.option.asi&&f.tokens.prev.line="a"&&t<="z"||t>="A"&&t<="Z")e.identifier=e.reserved=!0;return e}function mt(e,t){var n=ct(e,150);return vt(n),n.nud=typeof t=="function"?t:function(){this.right=ot(150),this.arity="unary";if(this.id==="++"||this.id==="--")f.option.plusplus?K("W016",this,this.id):this.right&&(!this.right.identifier||U(this.right))&&this.right.id!=="."&&this.right.id!=="["&&K("W017",this);return this},n}function gt(e,t){var n=ht(e);return n.type=e,n.nud=t,n}function yt(e,t){var n=gt(e,t);return n.identifier=!0,n.reserved=!0,n}function bt(e,t){var n=gt(e,t&&t.nud||function(){return this});return t=t||{},t.isFutureReservedWord=!0,n.value=e,n.identifier=!0,n.reserved=!0,n.meta=t,n}function wt(e,t){return yt(e,function(){return typeof t=="function"&&t(this),this})}function Et(e,t,n,r){var i=ct(e,n);return vt(i),i.infix=!0,i.led=function(i){return r||ut(f.tokens.prev,f.tokens.curr),e==="in"&&i.id==="!"&&K("W018",i,"!"),typeof t=="function"?t(i,this):(this.left=i,this.right=ot(n),this)},i}function St(e){var t=ct(e,42);return t.led=function(e){return f.option.inESNext()||K("W104",f.tokens.curr,"arrow function syntax (=>)"),ut(f.tokens.prev,f.tokens.curr),this.left=e,this.right=Jt(undefined,undefined,!1,e),this},t}function xt(e,t){var n=ct(e,100);return n.led=function(e){ut(f.tokens.prev,f.tokens.curr);var n=ot(100);return R(e,"NaN")||R(n,"NaN")?K("W019",this):t&&t.apply(this,[e,n]),(!e||!n)&&V("E041",f.tokens.curr.line),e.id==="!"&&K("W018",e,"!"),n.id==="!"&&K("W018",n,"!"),this.left=e,this.right=n,this},n}function Tt(e){return e&&(e.type==="(number)"&&+e.value===0||e.type==="(string)"&&e.value===""||e.type==="null"&&!f.option.eqnull||e.type==="true"||e.type==="false"||e.type==="undefined")}function Nt(e,t){if(f.option.notypeof)return!1;if(!e||!t)return!1;var n=["undefined","object","boolean","number","string","function","xml","object","unknown"];return t.type==="(identifier)"&&t.value==="typeof"&&e.type==="(string)"?!r.contains(n,e.value):!1}function Ct(e){function n(e){if(typeof e!="object")return;return e.right==="prototype"?e:n(e.left)}function r(e){while(!e.identifier&&typeof e.left=="object")e=e.left;if(e.identifier&&t.indexOf(e.value)>=0)return e.value}var t=["Array","ArrayBuffer","Boolean","Collator","DataView","Date","DateTimeFormat","Error","EvalError","Float32Array","Float64Array","Function","Infinity","Intl","Int16Array","Int32Array","Int8Array","Iterator","Number","NumberFormat","Object","RangeError","ReferenceError","RegExp","StopIteration","String","SyntaxError","TypeError","Uint16Array","Uint32Array","Uint8Array","Uint8ClampedArray","URIError"],i=n(e);if(i)return r(i)}function kt(e,t,n){var r=Et(e,typeof t=="function"?t:function(e,t){t.left=e;if(e){if(f.option.freeze){var n=Ct(e);n&&K("W121",e,n)}M[e.value]===!1&&D[e.value]["(global)"]===!0?K("W020",e):e["function"]&&K("W021",e,e.value),w[e.value]==="const"&&G("E013",e,e.value);if(e.id===".")return e.left?e.left.value==="arguments"&&!f.directive["use strict"]&&K("E031",t):K("E031",t),t.right=ot(10),t;if(e.id==="[")return f.tokens.curr.left.first?f.tokens.curr.left.first.forEach(function(e){e&&w[e.value]==="const"&&G("E013",e,e.value)}):e.left?e.left.value==="arguments"&&!f.directive["use strict"]&&K("E031",t):K("E031",t),t.right=ot(10),t;if(e.identifier&&!U(e))return w[e.value]==="exception"&&K("W022",e),t.right=ot(10),t;e===f.syntax["function"]&&K("W023",f.tokens.curr)}G("E031",t)},n);return r.exps=!0,r.assign=!0,r}function Lt(e,t,n){var r=ct(e,n);return vt(r),r.led=typeof t=="function"?t:function(e){return f.option.bitwise&&K("W016",this,this.id),this.left=e,this.right=ot(n),this},r}function At(e){return kt(e,function(e,t){f.option.bitwise&&K("W016",t,t.id);if(e)return e.id==="."||e.id==="["||e.identifier&&!U(e)?(ot(10),t):(e===f.syntax["function"]&&K("W023",f.tokens.curr),t);G("E031",t)},20)}function Ot(e){var t=ct(e,150);return t.led=function(e){return f.option.plusplus?K("W016",this,this.id):(!e.identifier||U(e))&&e.id!=="."&&e.id!=="["&&K("W017",this),this.left=e,this},t}function Mt(e,t){if(!f.tokens.next.identifier)return;rt();var n=f.tokens.curr,r=f.tokens.curr.value;return U(n)?t&&f.option.inES5()?r:e&&r==="undefined"?r:(K("W024",f.tokens.curr,f.tokens.curr.id),r):r}function _t(e,t){var n=Mt(e,t);if(n)return n;f.tokens.curr.id==="function"&&f.tokens.next.id==="("?K("W025"):G("E030",f.tokens.next,f.tokens.next.value)}function Dt(e){var t=0,n;if(f.tokens.next.id!==";"||O)return;for(;;){do n=nt(t),t+=1;while(n.id!="(end)"&&n.id==="(comment)");if(n.reach)return;if(n.id!=="(endline)"){if(n.id==="function"){f.option.latedef===!0&&K("W026",n);break}K("W027",n,n.value,e);break}}}function Pt(){f.tokens.next.id!==";"?f.option.asi||(!f.option.lastsemic||f.tokens.next.id!=="}"||f.tokens.next.line!==f.tokens.curr.line)&&Q("W033",f.tokens.curr.line,f.tokens.curr.character):rt(";")}function Ht(){var e,t=N,n,i=D,s=f.tokens.next;if(s.id===";"){rt(";");return}var o=U(s);o&&s.meta&&s.meta.isFutureReservedWord&&nt().id===":"&&(K("W024",s,s.id),o=!1);if(s.value==="module"&&s.type==="(identifier)"&&nt().type==="(identifier)"){f.option.inESNext()||K("W119",f.tokens.curr,"module"),rt("module");var u=_t();et(u,{type:"unused",token:f.tokens.curr}),rt("from"),rt("(string)"),Pt();return}if(r.has(["[","{"],s.value)&&on().isDestAssign){f.option.inESNext()||K("W104",f.tokens.curr,"destructuring expression"),e=Yt(),e.forEach(function(e){$(w,"W117",e.token,e.id)}),rt("="),Zt(e,ot(10,!0)),rt(";");return}s.identifier&&!o&&nt().id===":"&&(rt(),rt(":"),D=Object.create(i),et(s.value,{type:"label"}),!f.tokens.next.labelled&&f.tokens.next.value!=="{"&&K("W028",f.tokens.next,s.value,f.tokens.next.value),f.tokens.next.label=s.value,s=f.tokens.next);if(s.id==="{"){var a=w["(verb)"]==="case"&&f.tokens.curr.value===":";Ft(!0,!0,!1,!1,a);return}return n=ot(0,!0),n&&(!n.identifier||n.value!=="function")&&n.type!=="(punctuator)"&&!f.directive["use strict"]&&f.option.globalstrict&&f.option.strict&&K("E007"),s.block||(!f.option.expr&&(!n||!n.exps)?K("W030",f.tokens.curr):f.option.nonew&&n&&n.left&&n.id==="("&&n.left.id==="new"&&K("W031",s),Pt()),N=t,D=i,n}function Bt(e){var t=[],n;while(!f.tokens.next.reach&&f.tokens.next.id!=="(end)")f.tokens.next.id===";"?(n=nt(),(!n||n.id!=="("&&n.id!=="[")&&K("W032"),rt(";")):t.push(Ht(e===f.tokens.next.line));return t}function jt(){var e,t,n;for(;;){if(f.tokens.next.id==="(string)"){t=nt(0);if(t.id==="(endline)"){e=1;do n=nt(e),e+=1;while(n.id==="(endline)");if(n.id!==";"){if(n.id!=="(string)"&&n.id!=="(number)"&&n.id!=="(regexp)"&&n.identifier!==!0&&n.id!=="}")break;K("W033",f.tokens.next)}else t=n}else if(t.id==="}")K("W033",t);else if(t.id!==";")break;rt(),f.directive[f.tokens.curr.value]&&K("W034",f.tokens.curr,f.tokens.curr.value),f.tokens.curr.value==="use strict"&&(f.option["(explicitNewcap)"]||(f.option.newcap=!0),f.option.undef=!0),f.directive[f.tokens.curr.value]=!0,t.id===";"&&rt(";");continue}break}}function Ft(e,t,n,i,s){var o,u=T,a=N,l,c=D,h,p,d;T=e;if(!e||!f.option.funcscope)D=Object.create(D);h=f.tokens.next;var v=w["(metrics)"];v.nestedBlockDepth+=1,v.verifyMaxNestedBlockDepthPerFunction();if(f.tokens.next.id==="{"){rt("{"),w["(blockscope)"].stack(),p=f.tokens.curr.line;if(f.tokens.next.id!=="}"){N+=f.option.indent;while(!e&&f.tokens.next.from>N)N+=f.option.indent;if(n){l={};for(d in f.directive)r.has(f.directive,d)&&(l[d]=f.directive[d]);jt(),f.option.strict&&w["(context)"]["(global)"]&&!l["use strict"]&&!f.directive["use strict"]&&K("E007")}o=Bt(p),v.statementCount+=o.length,n&&(f.directive=l),N-=f.option.indent}rt("}",h),w["(blockscope)"].unstack(),N=a}else if(!e)if(n){l={},t&&!i&&!f.option.inMoz(!0)&&G("W118",f.tokens.curr,"function closure expressions");if(!t)for(d in f.directive)r.has(f.directive,d)&&(l[d]=f.directive[d]);ot(10),f.option.strict&&w["(context)"]["(global)"]&&!l["use strict"]&&!f.directive["use strict"]&&K("E007")}else G("E021",f.tokens.next,"{",f.tokens.next.value);else w["(nolet)"]=!0,(!t||f.option.curly)&&K("W116",f.tokens.next,"{",f.tokens.next.value),O=!0,N+=f.option.indent,o=[Ht()],N-=f.option.indent,O=!1,delete w["(nolet)"];switch(w["(verb)"]){case"break":case"continue":case"return":case"throw":if(s)break;default:w["(verb)"]=null}if(!e||!f.option.funcscope)D=c;return T=u,e&&f.option.noempty&&(!o||o.length===0)&&K("W035"),v.nestedBlockDepth-=1,o}function It(e){A&&typeof A[e]!="boolean"&&K("W036",f.tokens.curr,e),typeof L[e]=="number"?L[e]+=1:L[e]=1}function qt(e){var t=e.value,n=Object.getOwnPropertyDescriptor(x,t);n?n.value.push(e.line):x[t]=[e.line]}function Ut(){var e={};e.exps=!0,w["(comparray)"].stack();var t=!1;return f.tokens.next.value!=="for"&&(t=!0,f.option.inMoz(!0)||K("W116",f.tokens.next,"for",f.tokens.next.value),w["(comparray)"].setState("use"),e.right=ot(10)),rt("for"),f.tokens.next.value==="each"&&(rt("each"),f.option.inMoz(!0)||K("W118",f.tokens.curr,"for each")),rt("("),w["(comparray)"].setState("define"),e.left=ot(130),r.contains(["in","of"],f.tokens.next.value)?rt():G("E045",f.tokens.curr),w["(comparray)"].setState("generate"),ot(10),rt(")"),f.tokens.next.value==="if"&&(rt("if"),rt("("),w["(comparray)"].setState("filter"),e.filter=ot(10),rt(")")),t||(w["(comparray)"].setState("use"),e.right=ot(10)),rt("]"),w["(comparray)"].unstack(),e}function zt(){var e=Mt(!1,!0);return e||(f.tokens.next.id==="(string)"?(e=f.tokens.next.value,rt()):f.tokens.next.id==="(number)"&&(e=f.tokens.next.value.toString(),rt())),e==="hasOwnProperty"&&K("W001"),e}function Wt(e){var t,n,i=[],s,o=[],u,a=!1;if(e){if(Array.isArray(e)){for(var l in e){t=e[l];if(t.value==="..."){f.option.inESNext()||K("W104",t,"spread/rest operator");continue}t.value!==","&&(i.push(t.value),et(t.value,{type:"unused",token:t}))}return i}if(e.identifier===!0)return et(e.value,{type:"unused",token:e}),[e]}n=f.tokens.next,rt("(");if(f.tokens.next.id===")"){rt(")");return}for(;;){if(r.contains(["{","["],f.tokens.next.id)){o=Yt();for(u in o)u=o[u],u.id&&(i.push(u.id),et(u.id,{type:"unused",token:u.token}))}else f.tokens.next.value==="..."?(f.option.inESNext()||K("W104",f.tokens.next,"spread/rest operator"),rt("..."),s=_t(!0),i.push(s),et(s,{type:"unused",token:f.tokens.curr})):(s=_t(!0),i.push(s),et(s,{type:"unused",token:f.tokens.curr}));a&&f.tokens.next.id!=="="&&G("E051",f.tokens.current),f.tokens.next.id==="="&&(f.option.inESNext()||K("W119",f.tokens.next,"default parameters"),rt("="),a=!0,ot(10));if(f.tokens.next.id!==",")return rt(")",n),i;lt()}}function Xt(e,t,n){e["(properties)"][t]||(e["(properties)"][t]={unused:!1}),r.extend(e["(properties)"][t],n)}function Vt(e,t,n){return e["(properties)"][t]?e["(properties)"][t][n]||null:null}function $t(e,t,n,i){var s={"(name)":e,"(breakage)":0,"(loopage)":0,"(scope)":n,"(tokens)":{},"(properties)":{},"(catch)":!1,"(global)":!1,"(line)":null,"(character)":null,"(metrics)":null,"(statement)":null,"(context)":null,"(blockscope)":null,"(comparray)":null,"(generator)":null,"(params)":null};return t&&r.extend(s,{"(line)":t.line,"(character)":t.character,"(metrics)":Kt(t)}),r.extend(s,i),s["(context)"]&&(s["(blockscope)"]=s["(context)"]["(blockscope)"],s["(comparray)"]=s["(context)"]["(comparray)"]),s}function Jt(t,n,i,s){var o,u=f.option,a=f.ignored,l=D;return f.option=Object.create(f.option),f.ignored=Object.create(f.ignored),D=Object.create(D),w=$t(t||'"'+e+'"',f.tokens.next,D,{"(statement)":n,"(context)":w,"(generator)":i?!0:null}),o=w,f.tokens.curr.funct=w,E.push(w),t&&et(t,{type:"function"}),w["(params)"]=Wt(s),w["(metrics)"].verifyMaxParametersPerFunction(w["(params)"]),c.undefs=r.filter(c.undefs,function(e){return!r.contains(r.union(s),e[2])}),Ft(!1,!0,!0,s?!0:!1),!f.option.noyield&&i&&w["(generator)"]!=="yielded"&&K("W124",f.tokens.curr),w["(metrics)"].verifyMaxStatementsPerFunction(),w["(metrics)"].verifyMaxComplexityPerFunction(),w["(unusedOption)"]=f.option.unused,D=l,f.option=u,f.ignored=a,w["(last)"]=f.tokens.curr.line,w["(lastcharacter)"]=f.tokens.curr.character,r.map(Object.keys(w),function(e){if(e[0]==="(")return;w["(blockscope)"].unshadow(e)}),w=w["(context)"],o}function Kt(e){return{statementCount:0,nestedBlockDepth:-1,ComplexityCount:1,verifyMaxStatementsPerFunction:function(){f.option.maxstatements&&this.statementCount>f.option.maxstatements&&K("W071",e,this.statementCount)},verifyMaxParametersPerFunction:function(t){t=t||[],f.option.maxparams&&t.length>f.option.maxparams&&K("W072",e,t.length)},verifyMaxNestedBlockDepthPerFunction:function(){f.option.maxdepth&&this.nestedBlockDepth>0&&this.nestedBlockDepth===f.option.maxdepth+1&&K("W073",null,this.nestedBlockDepth)},verifyMaxComplexityPerFunction:function(){var t=f.option.maxcomplexity,n=this.ComplexityCount;t&&n>t&&K("W074",e,n)}}}function Qt(){w["(metrics)"].ComplexityCount+=1}function Gt(e){var t,n;e&&(t=e.id,n=e.paren,t===","&&(e=e.exprs[e.exprs.length-1])&&(t=e.id,n=n||e.paren));switch(t){case"=":case"+=":case"-=":case"*=":case"%=":case"&=":case"|=":case"^=":case"/=":!n&&!f.option.boss&&K("W084")}}function Yt(){var e,t,n=[];f.option.inESNext()||K("W104",f.tokens.curr,"destructuring expression");var i=function(){var e;if(r.contains(["[","{"],f.tokens.next.value)){t=Yt();for(var s in t)s=t[s],n.push({id:s.id,token:s.token})}else f.tokens.next.value===","?n.push({id:null,token:f.tokens.curr}):f.tokens.next.value==="("?(rt("("),i(),rt(")")):(e=_t(),e&&n.push({id:e,token:f.tokens.curr}))};if(f.tokens.next.value==="["){rt("["),i();while(f.tokens.next.value!=="]")rt(","),i();rt("]")}else if(f.tokens.next.value==="{"){rt("{"),e=_t(),f.tokens.next.value===":"?(rt(":"),i()):n.push({id:e,token:f.tokens.curr});while(f.tokens.next.value!=="}")rt(","),e=_t(),f.tokens.next.value===":"?(rt(":"),i()):n.push({id:e,token:f.tokens.curr});rt("}")}return n}function Zt(e,t){var n=t.first;if(!n)return;r.zip(e,Array.isArray(n)?n:[n]).forEach(function(e){var t=e[0],n=e[1];t&&n?t.first=n:t&&t.first&&!n&&K("W080",t.first,t.first.value)})}function rn(e){return f.option.inESNext()||K("W104",f.tokens.curr,"class"),e?(this.name=_t(),et(this.name,{type:"unused",token:f.tokens.curr})):f.tokens.next.identifier&&f.tokens.next.value!=="extends"&&(this.name=_t()),sn(this),this}function sn(e){var t=f.directive["use strict"];f.tokens.next.value==="extends"&&(rt("extends"),e.heritage=ot(10)),f.directive["use strict"]=!0,rt("{"),e.body=f.syntax["{"].nud(!0),f.directive["use strict"]=t}function un(){var e=on();e.notJson?(!f.option.inESNext()&&e.isDestAssign&&K("W104",f.tokens.curr,"destructuring assignment"),Bt()):(f.option.laxbreak=!0,f.jsonMode=!0,fn())}function fn(){function e(){var e={},t=f.tokens.next;rt("{");if(f.tokens.next.id!=="}")for(;;){if(f.tokens.next.id==="(end)")G("E026",f.tokens.next,t.line);else{if(f.tokens.next.id==="}"){K("W094",f.tokens.curr);break}f.tokens.next.id===","?G("E028",f.tokens.next):f.tokens.next.id!=="(string)"&&K("W095",f.tokens.next,f.tokens.next.value)}e[f.tokens.next.value]===!0?K("W075",f.tokens.next,f.tokens.next.value):f.tokens.next.value==="__proto__"&&!f.option.proto||f.tokens.next.value==="__iterator__"&&!f.option.iterator?K("W096",f.tokens.next,f.tokens.next.value):e[f.tokens.next.value]=!0,rt(),rt(":"),fn();if(f.tokens.next.id!==",")break;rt(",")}rt("}")}function t(){var e=f.tokens.next;rt("[");if(f.tokens.next.id!=="]")for(;;){if(f.tokens.next.id==="(end)")G("E027",f.tokens.next,e.line);else{if(f.tokens.next.id==="]"){K("W094",f.tokens.curr);break}f.tokens.next.id===","&&G("E028",f.tokens.next)}fn();if(f.tokens.next.id!==",")break;rt(",")}rt("]")}switch(f.tokens.next.id){case"{":e();break;case"[":t();break;case"true":case"false":case"null":case"(number)":case"(string)":rt();break;case"-":rt("-"),rt("(number)");break;default:G("E003",f.tokens.next)}}var e,t,n={"<":!0,"<=":!0,"==":!0,"===":!0,"!==":!0,"!=":!0,">":!0,">=":!0,"+":!0,"-":!0,"*":!0,"/":!0,"%":!0},h={asi:!0,bitwise:!0,boss:!0,browser:!0,camelcase:!0,couch:!0,curly:!0,debug:!0,devel:!0,dojo:!0,eqeqeq:!0,eqnull:!0,notypeof:!0,es3:!0,es5:!0,esnext:!0,moz:!0,evil:!0,expr:!0,forin:!0,funcscope:!0,globalstrict:!0,immed:!0,iterator:!0,jasmine:!0,jquery:!0,lastsemic:!0,laxbreak:!0,laxcomma:!0,loopfunc:!0,mootools:!0,multistr:!0,freeze:!0,newcap:!0,noarg:!0,node:!0,noempty:!0,nonbsp:!0,nonew:!0,nonstandard:!0,phantom:!0,plusplus:!0,proto:!0,prototypejs:!0,qunit:!0,rhino:!0,shelljs:!0,typed:!0,undef:!0,scripturl:!0,strict:!0,sub:!0,supernew:!0,validthis:!0,withstmt:!0,worker:!0,wsh:!0,yui:!0,mocha:!0,noyield:!0,onecase:!0,regexp:!0,regexdash:!0},p={maxlen:!1,indent:!1,maxerr:!1,predef:!1,globals:!1,quotmark:!1,scope:!1,maxstatements:!1,maxdepth:!1,maxparams:!1,maxcomplexity:!1,shadow:!1,unused:!0,latedef:!1,ignore:!1},d={bitwise:!0,forin:!0,newcap:!0,plusplus:!0,regexp:!0,undef:!0,eqeqeq:!0,strict:!0},v={eqeq:"eqeqeq",windows:"wsh",sloppy:"strict"},m={nomen:!0,onevar:!0,passfail:!0,white:!0,gcl:!0,smarttabs:!0,trailing:!0},g,y,b=["closure","exception","global","label","outer","unused","var"],w,E,S,x,T,N,C,k,L,A,O,M,D,P,H,B,j=[],F=new i.EventEmitter;gt("(number)",function(){return this}),gt("(string)",function(){return this}),gt("(template)",function(){return this}),f.syntax["(identifier)"]={type:"(identifier)",lbp:0,identifier:!0,nud:function(){var t=this.value,n=D[t],r,i;typeof n=="function"?n=undefined:!w["(blockscope)"].current.has(t)&&typeof n=="boolean"&&(r=w,w=E[0],et(t,{type:"var"}),n=w,w=r),i=w["(blockscope)"].getlabel(t);if(w===n||i)switch(i?i[t]["(type)"]:w[t]){case"unused":i?i[t]["(type)"]="var":w[t]="var";break;case"unction":i?i[t]["(type)"]="function":w[t]="function",this["function"]=!0;break;case"const":Xt(w,t,{unused:!1});break;case"function":this["function"]=!0;break;case"label":K("W037",f.tokens.curr,t)}else if(w["(global)"])typeof M[t]!="boolean"&&(e!=="typeof"&&e!=="delete"||f.tokens.next&&(f.tokens.next.value==="."||f.tokens.next.value==="["))&&(w["(comparray)"].check(t)||$(w,"W117",f.tokens.curr,t)),qt(f.tokens.curr);else switch(w[t]){case"closure":case"function":case"var":case"unused":K("W038",f.tokens.curr,t);break;case"label":K("W037",f.tokens.curr,t);break;case"outer":case"global":break;default:if(n===!0)w[t]=!0;else if(n===null)K("W039",f.tokens.curr,t),qt(f.tokens.curr);else if(typeof n!="object")(e!=="typeof"&&e!=="delete"||f.tokens.next&&(f.tokens.next.value==="."||f.tokens.next.value==="["))&&$(w,"W117",f.tokens.curr,t),w[t]=!0,qt(f.tokens.curr);else switch(n[t]){case"function":case"unction":this["function"]=!0,n[t]="closure",w[t]=n["(global)"]?"global":"outer";break;case"var":case"unused":n[t]="closure",w[t]=n["(global)"]?"global":"outer";break;case"const":Xt(n,t,{unused:!1});break;case"closure":w[t]=n["(global)"]?"global":"outer";break;case"label":K("W037",f.tokens.curr,t)}}return this},led:function(){G("E033",f.tokens.next,f.tokens.next.value)}},gt("(regexp)",function(){return this}),ht("(endline)"),ht("(begin)"),ht("(end)").reach=!0,ht("(error)").reach=!0,ht("}").reach=!0,ht(")"),ht("]"),ht('"').reach=!0,ht("'").reach=!0,ht(";"),ht(":").reach=!0,ht("#"),yt("else"),yt("case").reach=!0,yt("catch"),yt("default").reach=!0,yt("finally"),wt("arguments",function(e){f.directive["use strict"]&&w["(global)"]&&K("E008",e)}),wt("eval"),wt("false"),wt("Infinity"),wt("null"),wt("this",function(e){f.directive["use strict"]&&!f.option.validthis&&(w["(statement)"]&&w["(name)"].charAt(0)>"Z"||w["(global)"])&&K("W040",e)}),wt("true"),wt("undefined"),kt("=","assign",20),kt("+=","assignadd",20),kt("-=","assignsub",20),kt("*=","assignmult",20),kt("/=","assigndiv",20).nud=function(){G("E014")},kt("%=","assignmod",20),At("&=","assignbitand",20),At("|=","assignbitor",20),At("^=","assignbitxor",20),At("<<=","assignshiftleft",20),At(">>=","assignshiftright",20),At(">>>=","assignshiftrightunsigned",20),Et(",",function(e,t){var n;t.exprs=[e];if(!lt({peek:!0}))return t;for(;;){if(!(n=ot(10)))break;t.exprs.push(n);if(f.tokens.next.value!==","||!lt())break}return t},10,!0),Et("?",function(e,t){return Qt(),t.left=e,t.right=ot(10),rt(":"),t["else"]=ot(10),t},30);var Rt=40;Et("||",function(e,t){return Qt(),t.left=e,t.right=ot(Rt),t},Rt),Et("&&","and",50),Lt("|","bitor",70),Lt("^","bitxor",80),Lt("&","bitand",90),xt("==",function(e,t){var n=f.option.eqnull&&(e.value==="null"||t.value==="null");switch(!0){case!n&&f.option.eqeqeq:this.from=this.character,K("W116",this,"===","==");break;case Tt(e):K("W041",this,"===",e.value);break;case Tt(t):K("W041",this,"===",t.value);break;case Nt(t,e):K("W122",this,t.value);break;case Nt(e,t):K("W122",this,e.value)}return this}),xt("===",function(e,t){return Nt(t,e)?K("W122",this,t.value):Nt(e,t)&&K("W122",this,e.value),this}),xt("!=",function(e,t){var n=f.option.eqnull&&(e.value==="null"||t.value==="null");return!n&&f.option.eqeqeq?(this.from=this.character,K("W116",this,"!==","!=")):Tt(e)?K("W041",this,"!==",e.value):Tt(t)?K("W041",this,"!==",t.value):Nt(t,e)?K("W122",this,t.value):Nt(e,t)&&K("W122",this,e.value),this}),xt("!==",function(e,t){return Nt(t,e)?K("W122",this,t.value):Nt(e,t)&&K("W122",this,e.value),this}),xt("<"),xt(">"),xt("<="),xt(">="),Lt("<<","shiftleft",120),Lt(">>","shiftright",120),Lt(">>>","shiftrightunsigned",120),Et("in","in",120),Et("instanceof","instanceof",120),Et("+",function(e,t){var n=ot(130);return e&&n&&e.id==="(string)"&&n.id==="(string)"?(e.value+=n.value,e.character=n.character,!f.option.scripturl&&a.javascriptURL.test(e.value)&&K("W050",e),e):(t.left=e,t.right=n,t)},130),mt("+","num"),mt("+++",function(){return K("W007"),this.right=ot(150),this.arity="unary",this}),Et("+++",function(e){return K("W007"),this.left=e,this.right=ot(130),this},130),Et("-","sub",130),mt("-","neg"),mt("---",function(){return K("W006"),this.right=ot(150),this.arity="unary",this}),Et("---",function(e){return K("W006"),this.left=e,this.right=ot(130),this},130),Et("*","mult",140),Et("/","div",140),Et("%","mod",140),Ot("++","postinc"),mt("++","preinc"),f.syntax["++"].exps=!0,Ot("--","postdec"),mt("--","predec"),f.syntax["--"].exps=!0,mt("delete",function(){var e=ot(10);return(!e||e.id!=="."&&e.id!=="[")&&K("W051"),this.first=e,this}).exps=!0,mt("~",function(){return f.option.bitwise&&K("W052",this,"~"),ot(150),this}),mt("...",function(){return f.option.inESNext()||K("W104",this,"spread/rest operator"),f.tokens.next.identifier||G("E030",f.tokens.next,f.tokens.next.value),ot(150),this}),mt("!",function(){return this.right=ot(150),this.arity="unary",this.right||V("E041",this.line||0),n[this.right.id]===!0&&K("W018",this,"!"),this}),mt("typeof","typeof"),mt("new",function(){var e=ot(155),t;if(e&&e.id!=="function")if(e.identifier){e["new"]=!0;switch(e.value){case"Number":case"String":case"Boolean":case"Math":case"JSON":K("W053",f.tokens.prev,e.value);break;case"Function":f.option.evil||K("W054");break;case"Date":case"RegExp":case"this":break;default:e.id!=="function"&&(t=e.value.substr(0,1),f.option.newcap&&(t<"A"||t>"Z")&&!r.has(S,e.value)&&K("W055",f.tokens.curr))}}else e.id!=="."&&e.id!=="["&&e.id!=="("&&K("W056",f.tokens.curr);else f.option.supernew||K("W057",this);return f.tokens.next.id!=="("&&!f.option.supernew&&K("W058",f.tokens.curr,f.tokens.curr.value),this.first=e,this}),f.syntax["new"].exps=!0,mt("void").exps=!0,Et(".",function(e,t){var n=_t(!1,!0);return typeof n=="string"&&It(n),t.left=e,t.right=n,n&&n==="hasOwnProperty"&&f.tokens.next.value==="="&&K("W001"),!e||e.value!=="arguments"||n!=="callee"&&n!=="caller"?!f.option.evil&&e&&e.value==="document"&&(n==="write"||n==="writeln")&&K("W060",e):f.option.noarg?K("W059",e,n):f.directive["use strict"]&&G("E008"),!f.option.evil&&(n==="eval"||n==="execScript")&&K("W061"),t},160,!0),Et("(",function(e,t){f.option.immed&&e&&!e.immed&&e.id==="function"&&K("W062");var n=0,r=[];e&&e.type==="(identifier)"&&e.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)&&"Number String Boolean Date Object Error".indexOf(e.value)===-1&&(e.value==="Math"?K("W063",e):f.option.newcap&&K("W064",e));if(f.tokens.next.id!==")")for(;;){r[r.length]=ot(10),n+=1;if(f.tokens.next.id!==",")break;lt()}return rt(")"),typeof e=="object"&&(f.option.inES3()&&e.value==="parseInt"&&n===1&&K("W065",f.tokens.curr),f.option.evil||(e.value==="eval"||e.value==="Function"||e.value==="execScript"?(K("W061",e),r[0]&&[0].id==="(string)"&&Z(e,r[0].value)):!r[0]||r[0].id!=="(string)"||e.value!=="setTimeout"&&e.value!=="setInterval"?r[0]&&r[0].id==="(string)"&&e.value==="."&&e.left.value==="window"&&(e.right==="setTimeout"||e.right==="setInterval")&&(K("W066",e),Z(e,r[0].value)):(K("W066",e),Z(e,r[0].value))),!e.identifier&&e.id!=="."&&e.id!=="["&&e.id!=="("&&e.id!=="&&"&&e.id!=="||"&&e.id!=="?"&&K("W067",e)),t.left=e,t},155,!0).exps=!0,mt("(",function(){var e,t=[],n,i,s=0,o,u=1;do n=nt(s),n.value==="("?u+=1:n.value===")"&&(u-=1),s+=1,i=nt(s);while((u!==0||n.value!==")")&&i.value!=="=>"&&i.value!==";"&&i.type!=="(end)");f.tokens.next.id==="function"&&(f.tokens.next.immed=!0);var a=[];if(f.tokens.next.id!==")")for(;;){if(i.value==="=>"&&r.contains(["{","["],f.tokens.next.value)){e=f.tokens.next,e.left=Yt(),t.push(e);for(var l in e.left)a.push(e.left[l].token)}else a.push(ot(10));if(f.tokens.next.id!==",")break;lt()}rt(")",this),f.option.immed&&a[0]&&a[0].id==="function"&&f.tokens.next.id!=="("&&(f.tokens.next.id!=="."||nt().value!=="call"&&nt().value!=="apply")&&K("W068",this);if(f.tokens.next.value==="=>")return a;if(!a.length)return;return a.length>1?(o=Object.create(f.syntax[","]),o.exprs=a):o=a[0],o&&(o.paren=!0),o}),St("=>"),Et("[",function(e,t){var n=ot(10),r;return n&&n.type==="(string)"&&(!f.option.evil&&(n.value==="eval"||n.value==="execScript")&&K("W061",t),It(n.value),!f.option.sub&&a.identifier.test(n.value)&&(r=f.syntax[n.value],(!r||!U(r))&&K("W069",f.tokens.prev,n.value))),rt("]",t),n&&n.value==="hasOwnProperty"&&f.tokens.next.value==="="&&K("W001"),t.left=e,t.right=n,t},160,!0),mt("[",function(){var e=on(!0);if(e.isCompArray)return f.option.inESNext()||K("W119",f.tokens.curr,"array comprehension"),Ut();e.isDestAssign&&!f.option.inESNext()&&K("W104",f.tokens.curr,"destructuring assignment");var t=f.tokens.curr.line!==f.tokens.next.line;this.first=[],t&&(N+=f.option.indent,f.tokens.next.from===N+f.option.indent&&(N+=f.option.indent));while(f.tokens.next.id!=="(end)"){while(f.tokens.next.id===",")f.option.inES5()||K("W070"),rt(",");if(f.tokens.next.id==="]")break;this.first.push(ot(10));if(f.tokens.next.id!==",")break;lt({allowTrailing:!0});if(f.tokens.next.id==="]"&&!f.option.inES5(!0)){K("W070",f.tokens.curr);break}}return t&&(N-=f.option.indent),rt("]",this),this},160),function(e){e.nud=function(e){function c(e,t){a[e]&&r.has(a,e)?K("W075",f.tokens.next,i):a[e]={},a[e].basic=!0,a[e].basictkn=t}function h(e,t){a[e]&&r.has(a,e)?(a[e].basic||a[e].setter)&&K("W075",f.tokens.next,i):a[e]={},a[e].setter=!0,a[e].setterToken=t}function p(e){a[e]&&r.has(a,e)?(a[e].basic||a[e].getter)&&K("W075",f.tokens.next,i):a[e]={},a[e].getter=!0,a[e].getterToken=f.tokens.curr}var t,n,i,s,o,u,a={},l="";t=f.tokens.curr.line!==f.tokens.next.line,t&&(N+=f.option.indent,f.tokens.next.from===N+f.option.indent&&(N+=f.option.indent));for(;;){if(f.tokens.next.id==="}")break;e&&f.tokens.next.value==="static"&&(rt("static"),l="static ");if(f.tokens.next.value==="get"&&nt().id!==":")rt("get"),f.option.inES5(!e)||G("E034"),i=zt(),!i&&!f.option.inESNext()&&G("E035"),e&&i==="constructor"&&G("E049",f.tokens.next,"class getter method",i),i&&p(l+i),o=f.tokens.next,n=Jt(),s=n["(params)"],i&&s&&K("W076",o,s[0],i);else if(f.tokens.next.value==="set"&&nt().id!==":")rt("set"),f.option.inES5(!e)||G("E034"),i=zt(),!i&&!f.option.inESNext()&&G("E035"),e&&i==="constructor"&&G("E049",f.tokens.next,"class setter method",i),i&&h(l+i,f.tokens.next),o=f.tokens.next,n=Jt(),s=n["(params)"],i&&(!s||s.length!==1)&&K("W077",o,i);else{u=!1,f.tokens.next.value==="*"&&f.tokens.next.type==="(punctuator)"&&(f.option.inESNext()||K("W104",f.tokens.next,"generator functions"),rt("*"),u=!0),i=zt(),c(l+i,f.tokens.next);if(typeof i!="string")break;f.tokens.next.value==="("?(f.option.inESNext()||K("W104",f.tokens.curr,"concise methods"),Jt(i,undefined,u)):e||(rt(":"),ot(10))}e&&i==="prototype"&&G("E049",f.tokens.next,"class method",i),It(i);if(e){l="";continue}if(f.tokens.next.id!==",")break;lt({allowTrailing:!0,property:!0}),f.tokens.next.id===","?K("W070",f.tokens.curr):f.tokens.next.id==="}"&&!f.option.inES5(!0)&&K("W070",f.tokens.curr)}t&&(N-=f.option.indent),rt("}",this);if(f.option.inES5())for(var d in a)r.has(a,d)&&a[d].setter&&!a[d].getter&&K("W078",a[d].setterToken);return this},e.fud=function(){G("E036",f.tokens.curr)}}(ht("{"));var en=pt("const",function(e){var t,n,i;f.option.inESNext()||K("W104",f.tokens.curr,"const"),this.first=[];for(;;){var s=[];r.contains(["{","["],f.tokens.next.value)?(t=Yt(),i=!1):(t=[{id:_t(),token:f.tokens.curr}],i=!0);for(var o in t)t.hasOwnProperty(o)&&(o=t[o],w[o.id]==="const"&&K("E011",null,o.id),w["(global)"]&&M[o.id]===!1&&K("W079",o.token,o.id),o.id&&(et(o.id,{token:o.token,type:"const",unused:!0}),s.push(o.token)));if(e)break;this.first=this.first.concat(s),f.tokens.next.id!=="="&&K("E012",f.tokens.curr,f.tokens.curr.value),f.tokens.next.id==="="&&(rt("="),f.tokens.next.id==="undefined"&&K("W080",f.tokens.prev,f.tokens.prev.value),nt(0).id==="="&&f.tokens.next.identifier&&K("W120",f.tokens.next,f.tokens.next.value),n=ot(10),i?t[0].first=n:Zt(s,n));if(f.tokens.next.id!==",")break;lt()}return this});en.exps=!0;var tn=pt("var",function(e){var t,n,i;this.first=[];for(;;){var s=[];r.contains(["{","["],f.tokens.next.value)?(t=Yt(),n=!1):(t=[{id:_t(),token:f.tokens.curr}],n=!0);for(var o in t)t.hasOwnProperty(o)&&(o=t[o],f.option.inESNext()&&w[o.id]==="const"&&K("E011",null,o.id),w["(global)"]&&M[o.id]===!1&&K("W079",o.token,o.id),o.id&&(et(o.id,{type:"unused",token:o.token}),s.push(o.token)));if(e)break;this.first=this.first.concat(s),f.tokens.next.id==="="&&(rt("="),f.tokens.next.id==="undefined"&&K("W080",f.tokens.prev,f.tokens.prev.value),nt(0).id==="="&&f.tokens.next.identifier&&K("W120",f.tokens.next,f.tokens.next.value),i=ot(10),n?t[0].first=i:Zt(s,i));if(f.tokens.next.id!==",")break;lt()}return this});tn.exps=!0;var nn=pt("let",function(e){var t,n,i,s;f.option.inESNext()||K("W104",f.tokens.curr,"let"),f.tokens.next.value==="("?(f.option.inMoz(!0)||K("W118",f.tokens.next,"let block"),rt("("),w["(blockscope)"].stack(),s=!0):w["(nolet)"]&&G("E048",f.tokens.curr),this.first=[];for(;;){var o=[];r.contains(["{","["],f.tokens.next.value)?(t=Yt(),n=!1):(t=[{id:_t(),token:f.tokens.curr.value}],n=!0);for(var u in t)t.hasOwnProperty(u)&&(u=t[u],f.option.inESNext()&&w[u.id]==="const"&&K("E011",null,u.id),w["(global)"]&&M[u.id]===!1&&K("W079",u.token,u.id),u.id&&!w["(nolet)"]&&(et(u.id,{type:"unused",token:u.token,islet:!0}),o.push(u.token)));if(e)break;this.first=this.first.concat(o),f.tokens.next.id==="="&&(rt("="),f.tokens.next.id==="undefined"&&K("W080",f.tokens.prev,f.tokens.prev.value),nt(0).id==="="&&f.tokens.next.identifier&&K("W120",f.tokens.next,f.tokens.next.value),i=ot(10),n?t[0].first=i:Zt(o,i));if(f.tokens.next.id!==",")break;lt()}return s&&(rt(")"),Ft(!0,!0),this.block=!0,w["(blockscope)"].unstack()),this});nn.exps=!0,dt("class",function(){return rn.call(this,!0)}),dt("function",function(){var e=!1;f.tokens.next.value==="*"&&(rt("*"),f.option.inESNext(!0)?e=!0:K("W119",f.tokens.curr,"function*")),T&&K("W082",f.tokens.curr);var t=_t();return w[t]==="const"&&K("E011",null,t),et(t,{type:"unction",token:f.tokens.curr}),Jt(t,{statement:!0},e),f.tokens.next.id==="("&&f.tokens.next.line===f.tokens.curr.line&&G("E039"),this}),mt("function",function(){var e=!1;f.tokens.next.value==="*"&&(f.option.inESNext()||K("W119",f.tokens.curr,"function*"),rt("*"),e=!0);var t=Mt();return Jt(t,undefined,e),!f.option.loopfunc&&w["(loopage)"]&&K("W083"),this}),dt("if",function(){var e=f.tokens.next;return Qt(),f.condition=!0,rt("("),Gt(ot(0)),rt(")",e),f.condition=!1,Ft(!0,!0),f.tokens.next.id==="else"&&(rt("else"),f.tokens.next.id==="if"||f.tokens.next.id==="switch"?Ht(!0):Ft(!0,!0)),this}),dt("try",function(){function t(){var e=D,t;rt("catch"),rt("("),D=Object.create(e),t=f.tokens.next.value,f.tokens.next.type!=="(identifier)"&&(t=null,K("E030",f.tokens.next,t)),rt(),w=$t("(catch)",f.tokens.next,D,{"(context)":w,"(breakage)":w["(breakage)"],"(loopage)":w["(loopage)"],"(statement)":!1,"(catch)":!0}),t&&et(t,{type:"exception"}),f.tokens.next.value==="if"&&(f.option.inMoz(!0)||K("W118",f.tokens.curr,"catch filter"),rt("if"),ot(0)),rt(")"),f.tokens.curr.funct=w,E.push(w),Ft(!1),D=e,w["(last)"]=f.tokens.curr.line,w["(lastcharacter)"]=f.tokens.curr.character,w=w["(context)"]}var e;Ft(!0);while(f.tokens.next.id==="catch")Qt(),e&&!f.option.inMoz(!0)&&K("W118",f.tokens.next,"multiple catch blocks"),t(),e=!0;if(f.tokens.next.id==="finally"){rt("finally"),Ft(!0);return}return e||G("E021",f.tokens.next,"catch",f.tokens.next.value),this}),dt("while",function(){var e=f.tokens.next;return w["(breakage)"]+=1,w["(loopage)"]+=1,Qt(),rt("("),Gt(ot(0)),rt(")",e),Ft(!0,!0),w["(breakage)"]-=1,w["(loopage)"]-=1,this}).labelled=!0,dt("with",function(){var e=f.tokens.next;return f.directive["use strict"]?G("E010",f.tokens.curr):f.option.withstmt||K("W085",f.tokens.curr),rt("("),ot(0),rt(")",e),Ft(!0,!0),this}),dt("switch",function(){var e=f.tokens.next,t=!1,n=!1;w["(breakage)"]+=1,rt("("),Gt(ot(0)),rt(")",e),e=f.tokens.next,rt("{"),f.tokens.next.from===N&&(n=!0),n||(N+=f.option.indent),this.cases=[];for(;;)switch(f.tokens.next.id){case"case":switch(w["(verb)"]){case"yield":case"break":case"case":case"continue":case"return":case"switch":case"throw":break;default:a.fallsThrough.test(f.lines[f.tokens.next.line-2])||K("W086",f.tokens.curr,"case")}rt("case"),this.cases.push(ot(0)),Qt(),t=!0,rt(":"),w["(verb)"]="case";break;case"default":switch(w["(verb)"]){case"yield":case"break":case"continue":case"return":case"throw":break;default:this.cases.length&&(a.fallsThrough.test(f.lines[f.tokens.next.line-2])||K("W086",f.tokens.curr,"default"))}rt("default"),t=!0,rt(":");break;case"}":n||(N-=f.option.indent),rt("}",e),w["(breakage)"]-=1,w["(verb)"]=undefined;return;case"(end)":G("E023",f.tokens.next,"}");return;default:N+=f.option.indent;if(t)switch(f.tokens.curr.id){case",":G("E040");return;case":":t=!1,Bt();break;default:G("E025",f.tokens.curr);return}else{if(f.tokens.curr.id!==":"){G("E021",f.tokens.next,"case",f.tokens.next.value);return}rt(":"),G("E024",f.tokens.curr,":"),Bt()}N-=f.option.indent}}).labelled=!0,pt("debugger",function(){return f.option.debug||K("W087",this),this}).exps=!0,function(){var e=pt("do",function(){w["(breakage)"]+=1,w["(loopage)"]+=1,Qt(),this.first=Ft(!0,!0),rt("while");var e=f.tokens.next;return rt("("),Gt(ot(0)),rt(")",e),w["(breakage)"]-=1,w["(loopage)"]-=1,this});e.labelled=!0,e.exps=!0}(),dt("for",function(){var e,t=f.tokens.next,n=!1,i=null;t.value==="each"&&(i=t,rt("each"),f.option.inMoz(!0)||K("W118",f.tokens.curr,"for each")),w["(breakage)"]+=1,w["(loopage)"]+=1,Qt(),rt("(");var s,o=0,u=["in","of"];do s=nt(o),++o;while(!r.contains(u,s.value)&&s.value!==";"&&s.type!=="(end)");if(r.contains(u,s.value)){!f.option.inESNext()&&s.value==="of"&&G("W104",s,"for of");if(f.tokens.next.id==="var")rt("var"),f.syntax["var"].fud.call(f.syntax["var"].fud,!0);else if(f.tokens.next.id==="let")rt("let"),n=!0,w["(blockscope)"].stack(),f.syntax.let.fud.call(f.syntax.let.fud,!0);else if(!f.tokens.next.identifier)G("E030",f.tokens.next,f.tokens.next.type),rt();else{switch(w[f.tokens.next.value]){case"unused":w[f.tokens.next.value]="var";break;case"var":break;default:w["(blockscope)"].getlabel(f.tokens.next.value)||K("W088",f.tokens.next,f.tokens.next.value)}rt()}rt(s.value),ot(20),rt(")",t),e=Ft(!0,!0),f.option.forin&&e&&(e.length>1||typeof e[0]!="object"||e[0].value!=="if")&&K("W089",this),w["(breakage)"]-=1,w["(loopage)"]-=1}else{i&&G("E045",i);if(f.tokens.next.id!==";")if(f.tokens.next.id==="var")rt("var"),f.syntax["var"].fud.call(f.syntax["var"].fud);else if(f.tokens.next.id==="let")rt("let"),n=!0,w["(blockscope)"].stack(),f.syntax.let.fud.call(f.syntax.let.fud);else for(;;){ot(0,"for");if(f.tokens.next.id!==",")break;lt()}at(f.tokens.curr),rt(";"),f.tokens.next.id!==";"&&Gt(ot(0)),at(f.tokens.curr),rt(";"),f.tokens.next.id===";"&&G("E021",f.tokens.next,")",";");if(f.tokens.next.id!==")")for(;;){ot(0,"for");if(f.tokens.next.id!==",")break;lt()}rt(")",t),Ft(!0,!0),w["(breakage)"]-=1,w["(loopage)"]-=1}return n&&w["(blockscope)"].unstack(),this}).labelled=!0,pt("break",function(){var e=f.tokens.next.value;return w["(breakage)"]===0&&K("W052",f.tokens.next,this.value),f.option.asi||at(this),f.tokens.next.id!==";"&&!f.tokens.next.reach&&f.tokens.curr.line===f.tokens.next.line&&(w[e]!=="label"?K("W090",f.tokens.next,e):D[e]!==w&&K("W091",f.tokens.next,e),this.first=f.tokens.next,rt()),Dt("break"),this}).exps=!0,pt("continue",function(){var e=f.tokens.next.value;return w["(breakage)"]===0&&K("W052",f.tokens.next,this.value),f.option.asi||at(this),f.tokens.next.id!==";"&&!f.tokens.next.reach?f.tokens.curr.line===f.tokens.next.line&&(w[e]!=="label"?K("W090",f.tokens.next,e):D[e]!==w&&K("W091",f.tokens.next,e),this.first=f.tokens.next,rt()):w["(loopage)"]||K("W052",f.tokens.next,this.value),Dt("continue"),this}).exps=!0,pt("return",function(){return this.line===f.tokens.next.line?f.tokens.next.id!==";"&&!f.tokens.next.reach&&(this.first=ot(0),this.first&&this.first.type==="(punctuator)"&&this.first.value==="="&&!this.first.paren&&!f.option.boss&&Q("W093",this.first.line,this.first.character)):f.tokens.next.type==="(punctuator)"&&["[","{","+","-"].indexOf(f.tokens.next.value)>-1&&at(this),Dt("return"),this}).exps=!0,function(e){e.exps=!0,e.lbp=25}(mt("yield",function(){var e=f.tokens.prev;return f.option.inESNext(!0)&&!w["(generator)"]?("(catch)"!==w["(name)"]||!w["(context)"]["(generator)"])&&G("E046",f.tokens.curr,"yield"):f.option.inESNext()||K("W104",f.tokens.curr,"yield"),w["(generator)"]="yielded",this.line===f.tokens.next.line||!f.option.inMoz(!0)?(f.tokens.next.id!==";"&&!f.tokens.next.reach&&f.tokens.next.nud&&(ut(f.tokens.curr,f.tokens.next),this.first=ot(10),this.first.type==="(punctuator)"&&this.first.value==="="&&!this.first.paren&&!f.option.boss&&Q("W093",this.first.line,this.first.character)),f.option.inMoz(!0)&&f.tokens.next.id!==")"&&(e.lbp>30||!e.assign&&!st()||e.id==="yield")&&G("E050",this)):f.option.asi||at(this),this})),pt("throw",function(){return at(this),this.first=ot(20),Dt("throw"),this}).exps=!0,pt("import",function(){f.option.inESNext()||K("W119",f.tokens.curr,"import");if(f.tokens.next.type==="(string)")return rt("(string)"),this;if(f.tokens.next.identifier)this.name=_t(),et(this.name,{type:"unused",token:f.tokens.curr});else{rt("{");for(;;){if(f.tokens.next.value==="}"){rt("}");break}var e;f.tokens.next.type==="default"?(e="default",rt("default")):e=_t(),f.tokens.next.value==="as"&&(rt("as"),e=_t()),et(e,{type:"unused",token:f.tokens.curr});if(f.tokens.next.value!==","){if(f.tokens.next.value==="}"){rt("}");break}G("E024",f.tokens.next,f.tokens.next.value);break}rt(",")}}return rt("from"),rt("(string)"),this}).exps=!0,pt("export",function(){f.option.inESNext()||K("W119",f.tokens.curr,"export");if(f.tokens.next.type==="default"){rt("default");if(f.tokens.next.id==="function"||f.tokens.next.id==="class")this.block=!0;return this.exportee=ot(10),this}if(f.tokens.next.value==="{"){rt("{");for(;;){y[_t()]=!0;if(f.tokens.next.value!==","){if(f.tokens.next.value==="}"){rt("}");break}G("E024",f.tokens.next,f.tokens.next.value);break}rt(",")}return this}return f.tokens.next.id==="var"?(rt("var"),y[f.tokens.next.value]=!0,f.syntax["var"].fud.call(f.syntax["var"].fud)):f.tokens.next.id==="let"?(rt("let"),y[f.tokens.next.value]=!0,f.syntax.let.fud.call(f.syntax.let.fud)):f.tokens.next.id==="const"?(rt("const"),y[f.tokens.next.value]=!0,f.syntax["const"].fud.call(f.syntax["const"].fud)):f.tokens.next.id==="function"?(this.block=!0,rt("function"),y[f.tokens.next.value]=!0,f.syntax["function"].fud()):f.tokens.next.id==="class"?(this.block=!0,rt("class"),y[f.tokens.next.value]=!0,f.syntax["class"].fud()):G("E024",f.tokens.next,f.tokens.next.value),this}).exps=!0,bt("abstract"),bt("boolean"),bt("byte"),bt("char"),bt("class",{es5:!0,nud:rn}),bt("double"),bt("enum",{es5:!0}),bt("export",{es5:!0}),bt("extends",{es5:!0}),bt("final"),bt("float"),bt("goto"),bt("implements",{es5:!0,strictOnly:!0}),bt("import",{es5:!0}),bt("int"),bt("interface",{es5:!0,strictOnly:!0}),bt("long"),bt("native"),bt("package",{es5:!0,strictOnly:!0}),bt("private",{es5:!0,strictOnly:!0}),bt("protected",{es5:!0,strictOnly:!0}),bt("public",{es5:!0,strictOnly:!0}),bt("short"),bt("static",{es5:!0,strictOnly:!0}),bt("super",{es5:!0}),bt("synchronized"),bt("transient"),bt("volatile");var on=function(){var e,t,n=-1,i=0,s={};r.contains(["[","{"],f.tokens.curr.value)&&(i+=1);do{e=n===-1?f.tokens.next:nt(n),t=nt(n+1),n+=1,r.contains(["[","{"],e.value)?i+=1:r.contains(["]","}"],e.value)&&(i-=1);if(e.identifier&&e.value==="for"&&i===1){s.isCompArray=!0,s.notJson=!0;break}if(r.contains(["}","]"],e.value)&&t.value==="="&&i===0){s.isDestAssign=!0,s.notJson=!0;break}e.value===";"&&(s.isBlock=!0,s.notJson=!0)}while(i>0&&e.id!=="(end)"&&n<15);return s},an=function(){function i(e){var t=n.variables.filter(function(t){if(t.value===e)return t.undef=!1,e}).length;return t!==0}function s(e){var t=n.variables.filter(function(t){if(t.value===e&&!t.undef)return t.unused===!0&&(t.unused=!1),e}).length;return t===0}var e=function(){this.mode="use",this.variables=[]},t=[],n;return{stack:function(){n=new e,t.push(n)},unstack:function(){n.variables.filter(function(e){e.unused&&K("W098",e.token,e.value),e.undef&&$(e.funct,"W117",e.token,e.value)}),t.splice(-1,1),n=t[t.length-1]},setState:function(e){r.contains(["use","define","generate","filter"],e)&&(n.mode=e)},check:function(e){if(!n)return;return n&&n.mode==="use"?(s(e)&&n.variables.push({funct:w,token:f.tokens.curr,value:e,undef:!0,unused:!1}),!0):n&&n.mode==="define"?(i(e)||n.variables.push({funct:w,token:f.tokens.curr,value:e,undef:!1,unused:!0}),!0):n&&n.mode==="generate"?($(w,"W117",f.tokens.curr,e),!0):n&&n.mode==="filter"?(s(e)&&$(w,"W117",f.tokens.curr,e),!0):!1}}},ln=function(){function n(){for(var t in e)if(e[t]["(type)"]==="unused"&&f.option.unused){var n=e[t]["(token)"],r=n.line,i=n.character;Q("W098",r,i,t)}}var e={},t=[e];return{stack:function(){e={},t.push(e)},unstack:function(){n(),t.splice(t.length-1,1),e=r.last(t)},getlabel:function(e){for(var n=t.length-1;n>=0;--n)if(r.has(t[n],e)&&!t[n][e]["(shadowed)"])return t[n]},shadow:function(e){for(var n=t.length-1;n>=0;n--)r.has(t[n],e)&&(t[n][e]["(shadowed)"]=!0)},unshadow:function(e){for(var n=t.length-1;n>=0;n--)r.has(t[n],e)&&(t[n][e]["(shadowed)"]=!1)},current:{has:function(t){return r.has(e,t)},add:function(t,n,r){e[t]={"(type)":n,"(token)":r,"(shadowed)":!1}}}}},cn=function(e,n,i){function v(e,t){if(!e)return;!Array.isArray(e)&&typeof e=="object"&&(e=Object.keys(e)),e.forEach(t)}var o,a,l,h,p={},d={};n=r.clone(n),f.reset(),n&&n.scope?c.scope=n.scope:(c.errors=[],c.undefs=[],c.internals=[],c.blacklist={},c.scope="(main)"),M=Object.create(null),W(M,s.ecmaIdentifiers),W(M,s.reservedVars),W(M,i||{}),g=Object.create(null),y=Object.create(null);if(n){v(n.predef||null,function(e){var t,r;e[0]==="-"?(t=e.slice(1),c.blacklist[t]=t):(r=Object.getOwnPropertyDescriptor(n.predef,e),M[e]=r?r.value:!1)}),v(n.exported||null,function(e){y[e]=!0}),delete n.predef,delete n.exported,h=Object.keys(n);for(l=0;l0&&(e.implieds=t),B.length>0&&(e.urls=B),l=Object.keys(D),l.length>0&&(e.globals=l);for(o=1;o0&&(e.unused=H),n=[];for(a in L)if(typeof L[a]=="number"){e.member=L;break}return e},cn.jshint=cn,cn}();typeof n=="object"&&n&&(n.JSHINT=c)},{"./lex.js":4,"./messages.js":5,"./reg.js":6,"./state.js":7,"./style.js":8,"./vars.js":9,events:10,underscore:2}],4:[function(e,t,n){"use strict";function c(){var e=[];return{push:function(t){e.push(t)},check:function(){for(var t=0;t"&&t===">"&&n===">"&&r==="="?{type:l.Punctuator,value:">>>="}:e==="="&&t==="="&&n==="="?{type:l.Punctuator,value:"==="}:e==="!"&&t==="="&&n==="="?{type:l.Punctuator,value:"!=="}:e===">"&&t===">"&&n===">"?{type:l.Punctuator,value:">>>"}:e==="<"&&t==="<"&&n==="="?{type:l.Punctuator,value:"<<="}:e===">"&&t===">"&&n==="="?{type:l.Punctuator,value:">>="}:e==="="&&t===">"?{type:l.Punctuator,value:e+t}:e===t&&"+-<>&|".indexOf(e)>=0?{type:l.Punctuator,value:e+t}:"<>=!+-*%&|^".indexOf(e)>=0?t==="="?{type:l.Punctuator,value:e+t}:{type:l.Punctuator,value:e}:e==="/"?t==="="&&/\/=(?!(\S*\/[gim]?))/.test(this.input)?{type:l.Punctuator,value:"/="}:{type:l.Punctuator,value:"/"}:null},scanComments:function(){function s(e,t,n){var r=["jshint","jslint","members","member","globals","global","exported"],i=!1,s=e+t,o="plain";return n=n||{},n.isMultiline&&(s+="*/"),r.forEach(function(n){if(i)return;if(e==="//"&&n!=="jshint")return;t.substr(0,n.length)===n&&(i=!0,e+=n,t=t.substr(n.length)),!i&&t.charAt(0)===" "&&t.substr(1,n.length)===n&&(i=!0,e=e+" "+n,t=t.substr(n.length+1));if(!i)return;switch(n){case"member":o="members";break;case"global":o="globals";break;default:o=n}}),{type:l.Comment,commentType:o,value:s,body:t,isSpecial:i,isMultiline:n.isMultiline||!1,isMalformed:n.isMalformed||!1}}var e=this.peek(),t=this.peek(1),n=this.input.substr(2),r=this.line,i=this.char;if(e==="*"&&t==="/")return this.trigger("error",{code:"E018",line:r,character:i}),this.skip(2),null;if(e!=="/"||t!=="*"&&t!=="/")return null;if(t==="/")return this.skip(this.input.length),s("//",n);var o="";if(t==="*"){this.inComment=!0,this.skip(2);while(this.peek()!=="*"||this.peek(1)!=="/")if(this.peek()===""){o+="\n";if(!this.nextLine())return this.trigger("error",{code:"E017",line:r,character:i}),this.inComment=!1,s("/*",o,{isMultiline:!0,isMalformed:!0})}else o+=this.peek(),this.skip();return this.skip(2),this.inComment=!1,s("/*",o,{isMultiline:!0})}},scanKeyword:function(){var e=/^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input),t=["if","in","do","var","for","new","try","let","this","else","case","void","with","enum","while","break","catch","throw","const","yield","class","super","return","typeof","delete","switch","export","import","default","finally","extends","function","continue","debugger","instanceof"];return e&&t.indexOf(e[0])>=0?{type:l.Keyword,value:e[0]}:null},scanIdentifier:function(){function i(e){return e>256}function s(e){return e>256}function o(e){return/^[0-9a-fA-F]$/.test(e)}var e="",t=0,n,r,u=function(){t+=1;if(this.peek(t)!=="u")return null;var e=this.peek(t+1),n=this.peek(t+2),r=this.peek(t+3),i=this.peek(t+4),u;return o(e)&&o(n)&&o(r)&&o(i)?(u=parseInt(e+n+r+i,16),f[u]||s(u)?(t+=5,"\\u"+e+n+r+i):null):null}.bind(this),c=function(){var e=this.peek(t),n=e.charCodeAt(0);return n===92?u():n<128?a[n]?(t+=1,e):null:i(n)?(t+=1,e):null}.bind(this),h=function(){var e=this.peek(t),n=e.charCodeAt(0);return n===92?u():n<128?f[n]?(t+=1,e):null:s(n)?(t+=1,e):null}.bind(this);r=c();if(r===null)return null;e=r;for(;;){r=h();if(r===null)break;e+=r}switch(e){case"true":case"false":n=l.BooleanLiteral;break;case"null":n=l.NullLiteral;break;default:n=l.Identifier}return{type:n,value:e}},scanNumericLiteral:function(){function s(e){return/^[0-9]$/.test(e)}function o(e){return/^[0-7]$/.test(e)}function u(e){return/^[0-9a-fA-F]$/.test(e)}function a(e){return e==="$"||e==="_"||e==="\\"||e>="a"&&e<="z"||e>="A"&&e<="Z"}var e=0,t="",n=this.input.length,r=this.peek(e),i;if(r!=="."&&!s(r))return null;if(r!=="."){t=this.peek(e),e+=1,r=this.peek(e);if(t==="0"){if(r==="x"||r==="X"){e+=1,t+=r;while(e"]});if(u==="\\"){this.skip(),u=this.peek();switch(u){case"'":this.triggerAsync("warning",{code:"W114",line:this.line,character:this.char,data:["\\'"]},e,function(){return o.jsonMode});break;case"b":u="\\b";break;case"f":u="\\f";break;case"n":u="\\n";break;case"r":u="\\r";break;case"t":u="\\t";break;case"0":u="\\0";var f=parseInt(this.peek(1),10);this.triggerAsync("warning",{code:"W115",line:this.line,character:this.char},e,function(){return f>=0&&f<=7&&o.directive["use strict"]});break;case"u":u=String.fromCharCode(parseInt(this.input.substr(1,4),16)),a=5;break;case"v":this.triggerAsync("warning",{code:"W114",line:this.line,character:this.char,data:["\\v"]},e,function(){return o.jsonMode}),u=" ";break;case"x":var c=parseInt(this.input.substr(1,2),16);this.triggerAsync("warning",{code:"W114",line:this.line,character:this.char,data:["\\x-"]},e,function(){return o.jsonMode}),u=String.fromCharCode(c),a=3;break;case"\\":u="\\\\";break;case'"':u='\\"';break;case"/":break;case"":s=!0,u="";break;case"!":if(n.slice(n.length-2)==="<")break;default:this.trigger("warning",{code:"W044",line:this.line,character:this.char})}}n+=u,this.skip(a)}return this.skip(),{type:l.StringLiteral,value:n,isUnclosed:!1,quote:t}},scanRegExp:function(){var e=0,t=this.input.length,n=this.peek(),r=n,i="",s=[],o=!1,u=!1,a,f=function(){n<" "&&(o=!0,this.trigger("warning",{code:"W048",line:this.line,character:this.char})),n==="<"&&(o=!0,this.trigger("warning",{code:"W049",line:this.line,character:this.char,data:[n]}))}.bind(this);if(!this.prereg||n!=="/")return null;e+=1,a=!1;while(e=this.getLines().length)return!1;this.input=this.getLines()[this.line],this.line+=1,this.char=1,this.from=1;var t=this.input.trim(),n=function(){return r.some(arguments,function(e){return t.indexOf(e)===0})},i=function(){return r.some(arguments,function(e){return t.indexOf(e,t.length-e.length)!==-1})};o.ignoreLinterErrors===!0&&!n("/*","//")&&!i("*/")&&(this.input=""),e=this.scanNonBreakingSpaces(),e>=0&&this.trigger("warning",{code:"W125",line:this.line,character:e+1}),this.input=this.input.replace(/\t/g,o.tab),e=this.scanUnsafeChars(),e>=0&&this.trigger("warning",{code:"W100",line:this.line,character:e});if(o.option.maxlen&&o.option.maxlen-1&&!n.name.match(/^[A-Z0-9_]*$/)&&e.warn("W106",{line:n.line,"char":n.from,data:[n.name]})}),e.on("String",function(n){var r=e.getOption("quotmark"),i;if(!r)return;r==="single"&&n.quote!=="'"&&(i="W109"),r==="double"&&n.quote!=='"'&&(i="W108"),r===!0&&(e.getCache("quotmark")||e.setCache("quotmark",n.quote),e.getCache("quotmark")!==n.quote&&(i="W110")),i&&e.warn(i,{line:n.line,"char":n.char})}),e.on("Number",function(n){n.value.charAt(0)==="."&&e.warn("W008",{line:n.line,"char":n.char,data:[n.value]}),n.value.substr(n.value.length-1)==="."&&e.warn("W047",{line:n.line,"char":n.char,data:[n.value]}),/^00+/.test(n.value)&&e.warn("W046",{line:n.line,"char":n.char,data:[n.value]})}),e.on("String",function(n){var r=/^(?:javascript|jscript|ecmascript|vbscript|livescript)\s*:/i;if(e.getOption("scripturl"))return;r.test(n.value)&&e.warn("W107",{line:n.line,"char":n.char})})}},{}],9:[function(e,t,n){"use strict";n.reservedVars={arguments:!1,NaN:!1},n.ecmaIdentifiers={Array:!1,Boolean:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,eval:!1,EvalError:!1,Function:!1,hasOwnProperty:!1,isFinite:!1,isNaN:!1,JSON:!1,Math:!1,Number:!1,Object:!1,parseInt:!1,parseFloat:!1,RangeError:!1,ReferenceError:!1,RegExp:!1,String:!1,SyntaxError:!1,TypeError:!1,URIError:!1},n.newEcmaIdentifiers={Set:!1,Map:!1,WeakMap:!1,WeakSet:!1,Proxy:!1,Promise:!1},n.browser={Audio:!1,Blob:!1,addEventListener:!1,applicationCache:!1,atob:!1,blur:!1,btoa:!1,CanvasGradient:!1,CanvasPattern:!1,CanvasRenderingContext2D:!1,clearInterval:!1,clearTimeout:!1,close:!1,closed:!1,CustomEvent:!1,DOMParser:!1,defaultStatus:!1,document:!1,Element:!1,ElementTimeControl:!1,event:!1,FileReader:!1,FormData:!1,focus:!1,frames:!1,getComputedStyle:!1,HTMLElement:!1,HTMLAnchorElement:!1,HTMLBaseElement:!1,HTMLBlockquoteElement:!1,HTMLBodyElement:!1,HTMLBRElement:!1,HTMLButtonElement:!1,HTMLCanvasElement:!1,HTMLDirectoryElement:!1,HTMLDivElement:!1,HTMLDListElement:!1,HTMLFieldSetElement:!1,HTMLFontElement:!1,HTMLFormElement:!1,HTMLFrameElement:!1,HTMLFrameSetElement:!1,HTMLHeadElement:!1,HTMLHeadingElement:!1,HTMLHRElement:!1,HTMLHtmlElement:!1,HTMLIFrameElement:!1,HTMLImageElement:!1,HTMLInputElement:!1,HTMLIsIndexElement:!1,HTMLLabelElement:!1,HTMLLayerElement:!1,HTMLLegendElement:!1,HTMLLIElement:!1,HTMLLinkElement:!1,HTMLMapElement:!1,HTMLMenuElement:!1,HTMLMetaElement:!1,HTMLModElement:!1,HTMLObjectElement:!1,HTMLOListElement:!1,HTMLOptGroupElement:!1,HTMLOptionElement:!1,HTMLParagraphElement:!1,HTMLParamElement:!1,HTMLPreElement:!1,HTMLQuoteElement:!1,HTMLScriptElement:!1,HTMLSelectElement:!1,HTMLStyleElement:!1,HTMLTableCaptionElement:!1,HTMLTableCellElement:!1,HTMLTableColElement:!1,HTMLTableElement:!1,HTMLTableRowElement:!1,HTMLTableSectionElement:!1,HTMLTextAreaElement:!1,HTMLTitleElement:!1,HTMLUListElement:!1,HTMLVideoElement:!1,history:!1,Image:!1,length:!1,localStorage:!1,location:!1,matchMedia:!1,MessageChannel:!1,MessageEvent:!1,MessagePort:!1,MouseEvent:!1,moveBy:!1,moveTo:!1,MutationObserver:!1,name:!1,Node:!1,NodeFilter:!1,NodeList:!1,navigator:!1,onbeforeunload:!0,onblur:!0,onerror:!0,onfocus:!0,onload:!0,onresize:!0,onunload:!0,open:!1,openDatabase:!1,opener:!1,Option:!1,parent:!1,print:!1,removeEventListener:!1,resizeBy:!1,resizeTo:!1,screen:!1,scroll:!1,scrollBy:!1,scrollTo:!1,sessionStorage:!1,setInterval:!1,setTimeout:!1,SharedWorker:!1,status:!1,SVGAElement:!1,SVGAltGlyphDefElement:!1,SVGAltGlyphElement:!1,SVGAltGlyphItemElement:!1,SVGAngle:!1,SVGAnimateColorElement:!1,SVGAnimateElement:!1,SVGAnimateMotionElement:!1,SVGAnimateTransformElement:!1,SVGAnimatedAngle:!1,SVGAnimatedBoolean:!1,SVGAnimatedEnumeration:!1,SVGAnimatedInteger:!1,SVGAnimatedLength:!1,SVGAnimatedLengthList:!1,SVGAnimatedNumber:!1,SVGAnimatedNumberList:!1,SVGAnimatedPathData:!1,SVGAnimatedPoints:!1,SVGAnimatedPreserveAspectRatio:!1,SVGAnimatedRect:!1,SVGAnimatedString:!1,SVGAnimatedTransformList:!1,SVGAnimationElement:!1,SVGCSSRule:!1,SVGCircleElement:!1,SVGClipPathElement:!1,SVGColor:!1,SVGColorProfileElement:!1,SVGColorProfileRule:!1,SVGComponentTransferFunctionElement:!1,SVGCursorElement:!1,SVGDefsElement:!1,SVGDescElement:!1,SVGDocument:!1,SVGElement:!1,SVGElementInstance:!1,SVGElementInstanceList:!1,SVGEllipseElement:!1,SVGExternalResourcesRequired:!1,SVGFEBlendElement:!1,SVGFEColorMatrixElement:!1,SVGFEComponentTransferElement:!1,SVGFECompositeElement:!1,SVGFEConvolveMatrixElement:!1,SVGFEDiffuseLightingElement:!1,SVGFEDisplacementMapElement:!1,SVGFEDistantLightElement:!1,SVGFEFloodElement:!1,SVGFEFuncAElement:!1,SVGFEFuncBElement:!1,SVGFEFuncGElement:!1,SVGFEFuncRElement:!1,SVGFEGaussianBlurElement:!1,SVGFEImageElement:!1,SVGFEMergeElement:!1,SVGFEMergeNodeElement:!1,SVGFEMorphologyElement:!1,SVGFEOffsetElement:!1,SVGFEPointLightElement:!1,SVGFESpecularLightingElement:!1,SVGFESpotLightElement:!1,SVGFETileElement:!1,SVGFETurbulenceElement:!1,SVGFilterElement:!1,SVGFilterPrimitiveStandardAttributes:!1,SVGFitToViewBox:!1,SVGFontElement:!1,SVGFontFaceElement:!1,SVGFontFaceFormatElement:!1,SVGFontFaceNameElement:!1,SVGFontFaceSrcElement:!1,SVGFontFaceUriElement:!1,SVGForeignObjectElement:!1,SVGGElement:!1,SVGGlyphElement:!1,SVGGlyphRefElement:!1,SVGGradientElement:!1,SVGHKernElement:!1,SVGICCColor:!1,SVGImageElement:!1,SVGLangSpace:!1,SVGLength:!1,SVGLengthList:!1,SVGLineElement:!1,SVGLinearGradientElement:!1,SVGLocatable:!1,SVGMPathElement:!1,SVGMarkerElement:!1,SVGMaskElement:!1,SVGMatrix:!1,SVGMetadataElement:!1,SVGMissingGlyphElement:!1,SVGNumber:!1,SVGNumberList:!1,SVGPaint:!1,SVGPathElement:!1,SVGPathSeg:!1,SVGPathSegArcAbs:!1,SVGPathSegArcRel:!1,SVGPathSegClosePath:!1,SVGPathSegCurvetoCubicAbs:!1,SVGPathSegCurvetoCubicRel:!1,SVGPathSegCurvetoCubicSmoothAbs:!1,SVGPathSegCurvetoCubicSmoothRel:!1,SVGPathSegCurvetoQuadraticAbs:!1,SVGPathSegCurvetoQuadraticRel:!1,SVGPathSegCurvetoQuadraticSmoothAbs:!1,SVGPathSegCurvetoQuadraticSmoothRel:!1,SVGPathSegLinetoAbs:!1,SVGPathSegLinetoHorizontalAbs:!1,SVGPathSegLinetoHorizontalRel:!1,SVGPathSegLinetoRel:!1,SVGPathSegLinetoVerticalAbs:!1,SVGPathSegLinetoVerticalRel:!1,SVGPathSegList:!1,SVGPathSegMovetoAbs:!1,SVGPathSegMovetoRel:!1,SVGPatternElement:!1,SVGPoint:!1,SVGPointList:!1,SVGPolygonElement:!1,SVGPolylineElement:!1,SVGPreserveAspectRatio:!1,SVGRadialGradientElement:!1,SVGRect:!1,SVGRectElement:!1,SVGRenderingIntent:!1,SVGSVGElement:!1,SVGScriptElement:!1,SVGSetElement:!1,SVGStopElement:!1,SVGStringList:!1,SVGStylable:!1,SVGStyleElement:!1,SVGSwitchElement:!1,SVGSymbolElement:!1,SVGTRefElement:!1,SVGTSpanElement:!1,SVGTests:!1,SVGTextContentElement:!1,SVGTextElement:!1,SVGTextPathElement:!1,SVGTextPositioningElement:!1,SVGTitleElement:!1,SVGTransform:!1,SVGTransformList:!1,SVGTransformable:!1,SVGURIReference:!1,SVGUnitTypes:!1,SVGUseElement:!1,SVGVKernElement:!1,SVGViewElement:!1,SVGViewSpec:!1,SVGZoomAndPan:!1,TimeEvent:!1,top:!1,URL:!1,WebSocket:!1,window:!1,Worker:!1,XMLHttpRequest:!1,XMLSerializer:!1,XPathEvaluator:!1,XPathException:!1,XPathExpression:!1,XPathNamespace:!1,XPathNSResolver:!1,XPathResult:!1},n.devel={alert:!1,confirm:!1,console:!1,Debug:!1,opera:!1,prompt:!1},n.worker={importScripts:!0,postMessage:!0,self:!0},n.nonstandard={escape:!1,unescape:!1},n.couch={require:!1,respond:!1,getRow:!1,emit:!1,send:!1,start:!1,sum:!1,log:!1,exports:!1,module:!1,provides:!1},n.node={__filename:!1,__dirname:!1,GLOBAL:!1,global:!1,module:!1,require:!1,Buffer:!0,console:!0,exports:!0,process:!0,setTimeout:!0,clearTimeout:!0,setInterval:!0,clearInterval:!0,setImmediate:!0,clearImmediate:!0},n.phantom={phantom:!0,require:!0,WebPage:!0,console:!0,exports:!0},n.qunit={asyncTest:!1,deepEqual:!1,equal:!1,expect:!1,module:!1,notDeepEqual:!1,notEqual:!1,notPropEqual:!1,notStrictEqual:!1,ok:!1,propEqual:!1,QUnit:!1,raises:!1,start:!1,stop:!1,strictEqual:!1,test:!1,"throws":!1},n.rhino={defineClass:!1,deserialize:!1,gc:!1,help:!1,importClass:!1,importPackage:!1,java:!1,load:!1,loadClass:!1,Packages:!1,print:!1,quit:!1,readFile:!1,readUrl:!1,runCommand:!1,seal:!1,serialize:!1,spawn:!1,sync:!1,toint32:!1,version:!1},n.shelljs={target:!1,echo:!1,exit:!1,cd:!1,pwd:!1,ls:!1,find:!1,cp:!1,rm:!1,mv:!1,mkdir:!1,test:!1,cat:!1,sed:!1,grep:!1,which:!1,dirs:!1,pushd:!1,popd:!1,env:!1,exec:!1,chmod:!1,config:!1,error:!1,tempdir:!1},n.typed={ArrayBuffer:!1,ArrayBufferView:!1,DataView:!1,Float32Array:!1,Float64Array:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1},n.wsh={ActiveXObject:!0,Enumerator:!0,GetObject:!0,ScriptEngine:!0,ScriptEngineBuildVersion:!0,ScriptEngineMajorVersion:!0,ScriptEngineMinorVersion:!0,VBArray:!0,WSH:!0,WScript:!0,XDomainRequest:!0},n.dojo={dojo:!1,dijit:!1,dojox:!1,define:!1,require:!1},n.jquery={$:!1,jQuery:!1},n.mootools={$:!1,$$:!1,Asset:!1,Browser:!1,Chain:!1,Class:!1,Color:!1,Cookie:!1,Core:!1,Document:!1,DomReady:!1,DOMEvent:!1,DOMReady:!1,Drag:!1,Element:!1,Elements:!1,Event:!1,Events:!1,Fx:!1,Group:!1,Hash:!1,HtmlTable:!1,IFrame:!1,IframeShim:!1,InputValidator:!1,instanceOf:!1,Keyboard:!1,Locale:!1,Mask:!1,MooTools:!1,Native:!1,Options:!1,OverText:!1,Request:!1,Scroller:!1,Slick:!1,Slider:!1,Sortables:!1,Spinner:!1,Swiff:!1,Tips:!1,Type:!1,typeOf:!1,URI:!1,Window:!1},n.prototypejs={$:!1,$$:!1,$A:!1,$F:!1,$H:!1,$R:!1,$break:!1,$continue:!1,$w:!1,Abstract:!1,Ajax:!1,Class:!1,Enumerable:!1,Element:!1,Event:!1,Field:!1,Form:!1,Hash:!1,Insertion:!1,ObjectRange:!1,PeriodicalExecuter:!1,Position:!1,Prototype:!1,Selector:!1,Template:!1,Toggle:!1,Try:!1,Autocompleter:!1,Builder:!1,Control:!1,Draggable:!1,Draggables:!1,Droppables:!1,Effect:!1,Sortable:!1,SortableObserver:!1,Sound:!1,Scriptaculous:!1},n.yui={YUI:!1,Y:!1,YUI_config:!1},n.mocha={describe:!1,it:!1,before:!1,after:!1,beforeEach:!1,afterEach:!1,suite:!1,test:!1,setup:!1,teardown:!1},n.jasmine={jasmine:!1,describe:!1,it:!1,xit:!1,beforeEach:!1,afterEach:!1,setFixtures:!1,loadFixtures:!1,spyOn:!1,expect:!1,runs:!1,waitsFor:!1,waits:!1}},{}],10:[function(e,t,n){function r(){this._events=this._events||{},this._maxListeners=this._maxListeners||undefined}function i(e){return typeof e=="function"}function s(e){return typeof e=="number"}function o(e){return typeof e=="object"&&e!==null}function u(e){return e===void 0}t.exports=r,r.EventEmitter=r,r.prototype._events=undefined,r.prototype._maxListeners=undefined,r.defaultMaxListeners=10,r.prototype.setMaxListeners=function(e){if(!s(e)||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},r.prototype.emit=function(e){var t,n,r,s,a,f;this._events||(this._events={});if(e==="error")if(!this._events.error||o(this._events.error)&&!this._events.error.length)throw t=arguments[1],t instanceof Error?t:TypeError('Uncaught, unspecified "error" event.');n=this._events[e];if(u(n))return!1;if(i(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:r=arguments.length,s=new Array(r-1);for(a=1;a0&&this._events[e].length>n&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),console.trace())}return this},r.prototype.on=r.prototype.addListener,r.prototype.once=function(e,t){function r(){this.removeListener(e,r),n||(n=!0,t.apply(this,arguments))}if(!i(t))throw TypeError("listener must be a function");var n=!1;return r.listener=t,this.on(e,r),this},r.prototype.removeListener=function(e,t){var n,r,s,u;if(!i(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;n=this._events[e],s=n.length,r=-1;if(n===t||i(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(u=s;u-->0;)if(n[u]===t||n[u].listener&&n[u].listener===t){r=u;break}if(r<0)return this;n.length===1?(n.length=0,delete this._events[e]):n.splice(r,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},r.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return arguments.length===0?this._events={}:this._events[e]&&delete this._events[e],this;if(arguments.length===0){for(t in this._events){if(t==="removeListener")continue;this.removeAllListeners(t)}return this.removeAllListeners("removeListener"),this._events={},this}n=this._events[e];if(i(n))this.removeListener(e,n);else while(n.length)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},r.prototype.listeners=function(e){var t;return!this._events||!this._events[e]?t=[]:i(this._events[e])?t=[this._events[e]]:t=this._events[e].slice(),t},r.listenerCount=function(e,t){var n;return!e._events||!e._events[t]?n=0:i(e._events[t])?n=1:n=e._events[t].length,n}},{}]},{},[3])(3)}),ace.define("ace/mode/javascript_worker",["require","exports","module","ace/lib/oop","ace/worker/mirror","ace/mode/javascript/jshint"],function(require,exports,module){"use strict";function startRegex(e){return RegExp("^("+e.join("|")+")")}var oop=require("../lib/oop"),Mirror=require("../worker/mirror").Mirror,lint=require("./javascript/jshint").JSHINT,disabledWarningsRe=startRegex(["Bad for in variable '(.+)'.",'Missing "use strict"']),errorsRe=startRegex(["Unexpected","Expected ","Confusing (plus|minus)","\\{a\\} unterminated regular expression","Unclosed ","Unmatched ","Unbegun comment","Bad invocation","Missing space after","Missing operator at"]),infoRe=startRegex(["Expected an assignment","Bad escapement of EOL","Unexpected comma","Unexpected space","Missing radix parameter.","A leading decimal point can","\\['{a}'\\] is better written in dot notation.","'{a}' used out of scope"]),JavaScriptWorker=exports.JavaScriptWorker=function(e){Mirror.call(this,e),this.setTimeout(500),this.setOptions()};oop.inherits(JavaScriptWorker,Mirror),function(){this.setOptions=function(e){this.options=e||{esnext:!0,moz:!0,devel:!0,browser:!0,node:!0,laxcomma:!0,laxbreak:!0,lastsemic:!0,onevar:!1,passfail:!1,maxerr:100,expr:!0,multistr:!0,globalstrict:!0},this.doc.getValue()&&this.deferredUpdate.schedule(100)},this.changeOptions=function(e){oop.mixin(this.options,e),this.doc.getValue()&&this.deferredUpdate.schedule(100)},this.isValidJS=function(str){try{eval("throw 0;"+str)}catch(e){if(e===0)return!0}return!1},this.onUpdate=function(){var e=this.doc.getValue();e=e.replace(/^#!.*\n/,"\n");if(!e){this.sender.emit("jslint",[]);return}var t=[],n=this.isValidJS(e)?"warning":"error";lint(e,this.options);var r=lint.errors,i=!1;for(var s=0;s0||-1)*Math.floor(Math.abs(e))),e}function B(e){var t=typeof e;return e===null||t==="undefined"||t==="boolean"||t==="number"||t==="string"}function j(e){var t,n,r;if(B(e))return e;n=e.valueOf;if(typeof n=="function"){t=n.call(e);if(B(t))return t}r=e.toString;if(typeof r=="function"){t=r.call(e);if(B(t))return t}throw new TypeError}Function.prototype.bind||(Function.prototype.bind=function(t){var n=this;if(typeof n!="function")throw new TypeError("Function.prototype.bind called on incompatible "+n);var i=u.call(arguments,1),s=function(){if(this instanceof s){var e=n.apply(this,i.concat(u.call(arguments)));return Object(e)===e?e:this}return n.apply(t,i.concat(u.call(arguments)))};return n.prototype&&(r.prototype=n.prototype,s.prototype=new r,r.prototype=null),s});var i=Function.prototype.call,s=Array.prototype,o=Object.prototype,u=s.slice,a=i.bind(o.toString),f=i.bind(o.hasOwnProperty),l,c,h,p,d;if(d=f(o,"__defineGetter__"))l=i.bind(o.__defineGetter__),c=i.bind(o.__defineSetter__),h=i.bind(o.__lookupGetter__),p=i.bind(o.__lookupSetter__);if([1,2].splice(0).length!=2)if(!function(){function e(e){var t=new Array(e+2);return t[0]=t[1]=0,t}var t=[],n;t.splice.apply(t,e(20)),t.splice.apply(t,e(26)),n=t.length,t.splice(5,0,"XXX"),n+1==t.length;if(n+1==t.length)return!0}())Array.prototype.splice=function(e,t){var n=this.length;e>0?e>n&&(e=n):e==void 0?e=0:e<0&&(e=Math.max(n+e,0)),e+ta)for(h=l;h--;)this[f+h]=this[a+h];if(s&&e===c)this.length=c,this.push.apply(this,i);else{this.length=c+s;for(h=0;h>>0;if(a(t)!="[object Function]")throw new TypeError;while(++s>>0,s=Array(i),o=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var u=0;u>>0,s=[],o,u=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var f=0;f>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0,s=arguments[1];if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");for(var o=0;o>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduce of empty array with no initial value");var s=0,o;if(arguments.length>=2)o=arguments[1];else do{if(s in r){o=r[s++];break}if(++s>=i)throw new TypeError("reduce of empty array with no initial value")}while(!0);for(;s>>0;if(a(t)!="[object Function]")throw new TypeError(t+" is not a function");if(!i&&arguments.length==1)throw new TypeError("reduceRight of empty array with no initial value");var s,o=i-1;if(arguments.length>=2)s=arguments[1];else do{if(o in r){s=r[o--];break}if(--o<0)throw new TypeError("reduceRight of empty array with no initial value")}while(!0);do o in this&&(s=t.call(void 0,s,r[o],o,n));while(o--);return s});if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1)Array.prototype.indexOf=function(t){var n=g&&a(this)=="[object String]"?this.split(""):F(this),r=n.length>>>0;if(!r)return-1;var i=0;arguments.length>1&&(i=H(arguments[1])),i=i>=0?i:Math.max(0,r+i);for(;i>>0;if(!r)return-1;var i=r-1;arguments.length>1&&(i=Math.min(i,H(arguments[1]))),i=i>=0?i:r-Math.abs(i);for(;i>=0;i--)if(i in n&&t===n[i])return i;return-1};Object.getPrototypeOf||(Object.getPrototypeOf=function(t){return t.__proto__||(t.constructor?t.constructor.prototype:o)});if(!Object.getOwnPropertyDescriptor){var y="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function(t,n){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(y+t);if(!f(t,n))return;var r,i,s;r={enumerable:!0,configurable:!0};if(d){var u=t.__proto__;t.__proto__=o;var i=h(t,n),s=p(t,n);t.__proto__=u;if(i||s)return i&&(r.get=i),s&&(r.set=s),r}return r.value=t[n],r}}Object.getOwnPropertyNames||(Object.getOwnPropertyNames=function(t){return Object.keys(t)});if(!Object.create){var b;Object.prototype.__proto__===null?b=function(){return{__proto__:null}}:b=function(){var e={};for(var t in e)e[t]=null;return e.constructor=e.hasOwnProperty=e.propertyIsEnumerable=e.isPrototypeOf=e.toLocaleString=e.toString=e.valueOf=e.__proto__=null,e},Object.create=function(t,n){var r;if(t===null)r=b();else{if(typeof t!="object")throw new TypeError("typeof prototype["+typeof t+"] != 'object'");var i=function(){};i.prototype=t,r=new i,r.__proto__=t}return n!==void 0&&Object.defineProperties(r,n),r}}if(Object.defineProperty){var E=w({}),S=typeof document=="undefined"||w(document.createElement("div"));if(!E||!S)var x=Object.defineProperty}if(!Object.defineProperty||x){var T="Property description must be an object: ",N="Object.defineProperty called on non-object: ",C="getters & setters can not be defined on this javascript engine";Object.defineProperty=function(t,n,r){if(typeof t!="object"&&typeof t!="function"||t===null)throw new TypeError(N+t);if(typeof r!="object"&&typeof r!="function"||r===null)throw new TypeError(T+r);if(x)try{return x.call(Object,t,n,r)}catch(i){}if(f(r,"value"))if(d&&(h(t,n)||p(t,n))){var s=t.__proto__;t.__proto__=o,delete t[n],t[n]=r.value,t.__proto__=s}else t[n]=r.value;else{if(!d)throw new TypeError(C);f(r,"get")&&l(t,n,r.get),f(r,"set")&&c(t,n,r.set)}return t}}Object.defineProperties||(Object.defineProperties=function(t,n){for(var r in n)f(n,r)&&Object.defineProperty(t,r,n[r]);return t}),Object.seal||(Object.seal=function(t){return t}),Object.freeze||(Object.freeze=function(t){return t});try{Object.freeze(function(){})}catch(k){Object.freeze=function(t){return function(n){return typeof n=="function"?n:t(n)}}(Object.freeze)}Object.preventExtensions||(Object.preventExtensions=function(t){return t}),Object.isSealed||(Object.isSealed=function(t){return!1}),Object.isFrozen||(Object.isFrozen=function(t){return!1}),Object.isExtensible||(Object.isExtensible=function(t){if(Object(t)===t)throw new TypeError;var n="";while(f(t,n))n+="?";t[n]=!0;var r=f(t,n);return delete t[n],r});if(!Object.keys){var L=!0,A=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],O=A.length;for(var M in{toString:null})L=!1;Object.keys=function I(e){if(typeof e!="object"&&typeof e!="function"||e===null)throw new TypeError("Object.keys called on a non-object");var I=[];for(var t in e)f(e,t)&&I.push(t);if(L)for(var n=0,r=O;n 0) { bindThis(fn[key], target[key], argThis); } }); })(Chart.prototype, this, this); } var asHalfPixel = function(n) { return Math.ceil(n) + 0.5 }; var ceil10 = function(v) { return Math.ceil(v / 10) * 10 }; var diffDomain = function(d) { return d[1] - d[0] }; var getOption = function(options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue }; var getPathBox = function(path) { var box = getBBox(path), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], minX = items[0].x, minY = Math.min(items[0].y, items[1].y); return { x: minX, y: minY, width: box.width, height: box.height } }; var getBBox = function(element) { try { return element.getBBox() } catch (ignore) { // Firefox will throw an exception if getBBox() is called whereas the // element is rendered with display:none // See https://github.com/c3js/c3/issues/2692 // The previous code was using `getBoundingClientRect` which was returning // everything at 0 in this case so let's reproduce this behavior here. return { x: 0, y: 0, width: 0, height: 0 } } }; var hasValue = function(dict, value) { var found = false; Object.keys(dict).forEach(function(key) { if (dict[key] === value) { found = true; } }); return found }; var isArray = function(o) { return Array.isArray(o) }; var isDefined = function(v) { return typeof v !== 'undefined' }; var isEmpty = function(o) { return ( typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0) ) }; var isFunction = function(o) { return typeof o === 'function' }; var isNumber = function(o) { return typeof o === 'number' }; var isString = function(o) { return typeof o === 'string' }; var isUndefined = function(v) { return typeof v === 'undefined' }; var isValue = function(v) { return v || v === 0 }; var notEmpty = function(o) { return !isEmpty(o) }; var sanitise = function(str) { return typeof str === 'string' ? str.replace(//g, '>') : str }; var flattenArray = function(arr) { return Array.isArray(arr) ? [].concat(...arr) : [] }; /** * Returns whether the point is within the given box. * * @param {Array} point An [x,y] coordinate * @param {Object} box An object with {x, y, width, height} keys * @param {Number} sensitivity An offset to ease check on very small boxes */ var isWithinBox = function(point, box, sensitivity = 0) { const xStart = box.x - sensitivity; const xEnd = box.x + box.width + sensitivity; const yStart = box.y + box.height + sensitivity; const yEnd = box.y - sensitivity; return ( xStart < point[0] && point[0] < xEnd && yEnd < point[1] && point[1] < yStart ) }; /** * Returns Internet Explorer version number (or false if no Internet Explorer used). * * @param string agent Optional parameter to specify user agent */ var getIEVersion = function(agent) { // https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie if (typeof agent === 'undefined') { agent = window.navigator.userAgent; } let pos = agent.indexOf('MSIE '); // up to IE10 if (pos > 0) { return parseInt(agent.substring(pos + 5, agent.indexOf('.', pos)), 10) } pos = agent.indexOf('Trident/'); // IE11 if (pos > 0) { pos = agent.indexOf('rv:'); return parseInt(agent.substring(pos + 3, agent.indexOf('.', pos)), 10) } return false }; /** * Returns whether the used browser is Internet Explorer. * * @param {Number} version Optional parameter to specify IE version */ var isIE = function(version) { const ver = getIEVersion(); if (typeof version === 'undefined') { return !!ver } return version === ver }; function AxisInternal(component, params) { var internal = this; internal.component = component; internal.params = params || {}; internal.d3 = component.d3; internal.scale = internal.d3.scaleLinear(); internal.range; internal.orient = 'bottom'; internal.innerTickSize = 6; internal.outerTickSize = this.params.withOuterTick ? 6 : 0; internal.tickPadding = 3; internal.tickValues = null; internal.tickFormat; internal.tickArguments; internal.tickOffset = 0; internal.tickCulling = true; internal.tickCentered; internal.tickTextCharSize; internal.tickTextRotate = internal.params.tickTextRotate; internal.tickLength; internal.axis = internal.generateAxis(); } AxisInternal.prototype.axisX = function(selection, x, tickOffset) { selection.attr('transform', function(d) { return 'translate(' + Math.ceil(x(d) + tickOffset) + ', 0)' }); }; AxisInternal.prototype.axisY = function(selection, y) { selection.attr('transform', function(d) { return 'translate(0,' + Math.ceil(y(d)) + ')' }); }; AxisInternal.prototype.scaleExtent = function(domain) { var start = domain[0], stop = domain[domain.length - 1]; return start < stop ? [start, stop] : [stop, start] }; AxisInternal.prototype.generateTicks = function(scale) { var internal = this; var i, domain, ticks = []; if (scale.ticks) { return scale.ticks.apply(scale, internal.tickArguments) } domain = scale.domain(); for (i = Math.ceil(domain[0]); i < domain[1]; i++) { ticks.push(i); } if (ticks.length > 0 && ticks[0] > 0) { ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); } return ticks }; AxisInternal.prototype.copyScale = function() { var internal = this; var newScale = internal.scale.copy(), domain; if (internal.params.isCategory) { domain = internal.scale.domain(); newScale.domain([domain[0], domain[1] - 1]); } return newScale }; AxisInternal.prototype.textFormatted = function(v) { var internal = this, formatted = internal.tickFormat ? internal.tickFormat(v) : v; return typeof formatted !== 'undefined' ? formatted : '' }; AxisInternal.prototype.updateRange = function() { var internal = this; internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()); return internal.range }; AxisInternal.prototype.updateTickTextCharSize = function(tick) { var internal = this; if (internal.tickTextCharSize) { return internal.tickTextCharSize } var size = { h: 11.5, w: 5.5 }; tick .select('text') .text(function(d) { return internal.textFormatted(d) }) .each(function(d) { var box = getBBox(this), text = internal.textFormatted(d), h = box.height, w = text ? box.width / text.length : undefined; if (h && w) { size.h = h; size.w = w; } }) .text(''); internal.tickTextCharSize = size; return size }; AxisInternal.prototype.isVertical = function() { return this.orient === 'left' || this.orient === 'right' }; AxisInternal.prototype.tspanData = function(d, i, scale) { var internal = this; var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d)); if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) { splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax); } return splitted.map(function(s) { return { index: i, splitted: s, length: splitted.length } }) }; AxisInternal.prototype.splitTickText = function(d, scale) { var internal = this, tickText = internal.textFormatted(d), maxWidth = internal.params.tickWidth, subtext, spaceIndex, textWidth, splitted = []; if (Object.prototype.toString.call(tickText) === '[object Array]') { return tickText } if (!maxWidth || maxWidth <= 0) { maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110; } function split(splitted, text) { spaceIndex = undefined; for (var i = 1; i < text.length; i++) { if (text.charAt(i) === ' ') { spaceIndex = i; } subtext = text.substr(0, i + 1); textWidth = internal.tickTextCharSize.w * subtext.length; // if text width gets over tick width, split by space index or crrent index if (maxWidth < textWidth) { return split( splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i) ) } } return splitted.concat(text) } return split(splitted, tickText + '') }; AxisInternal.prototype.ellipsify = function(splitted, max) { if (splitted.length <= max) { return splitted } var ellipsified = splitted.slice(0, max); var remaining = 3; for (var i = max - 1; i >= 0; i--) { var available = ellipsified[i].length; ellipsified[i] = ellipsified[i] .substr(0, available - remaining) .padEnd(available, '.'); remaining -= available; if (remaining <= 0) { break } } return ellipsified }; AxisInternal.prototype.updateTickLength = function() { var internal = this; internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding; }; AxisInternal.prototype.lineY2 = function(d) { var internal = this, tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset); return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0 }; AxisInternal.prototype.textY = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength }; AxisInternal.prototype.textTransform = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 'rotate(' + rotate + ')' : '' }; AxisInternal.prototype.textTextAnchor = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? (rotate > 0 ? 'start' : 'end') : 'middle' }; AxisInternal.prototype.tspanDx = function() { var internal = this, rotate = internal.tickTextRotate; return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0 }; AxisInternal.prototype.tspanDy = function(d, i) { var internal = this, dy = internal.tickTextCharSize.h; if (i === 0) { if (internal.isVertical()) { dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3); } else { dy = '.71em'; } } return dy }; AxisInternal.prototype.generateAxis = function() { var internal = this, d3 = internal.d3, params = internal.params; function axis(g, transition) { var self; g.each(function() { var g = (axis.g = d3.select(this)); var scale0 = this.__chart__ || internal.scale, scale1 = (this.__chart__ = internal.copyScale()); var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), ticks = g.selectAll('.tick').data(ticksValues, scale1), tickEnter = ticks .enter() .insert('g', '.domain') .attr('class', 'tick') .style('opacity', 1e-6), // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. tickExit = ticks.exit().remove(), tickUpdate = ticks.merge(tickEnter), tickTransform, tickX, tickY; if (params.isCategory) { internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); tickX = internal.tickCentered ? 0 : internal.tickOffset; tickY = internal.tickCentered ? internal.tickOffset : 0; } else { internal.tickOffset = tickX = 0; } internal.updateRange(); internal.updateTickLength(); internal.updateTickTextCharSize(g.select('.tick')); var lineUpdate = tickUpdate .select('line') .merge(tickEnter.append('line')), textUpdate = tickUpdate.select('text').merge(tickEnter.append('text')); var tspans = tickUpdate .selectAll('text') .selectAll('tspan') .data(function(d, i) { return internal.tspanData(d, i, scale1) }), tspanEnter = tspans.enter().append('tspan'), tspanUpdate = tspanEnter.merge(tspans).text(function(d) { return d.splitted }); tspans.exit().remove(); var path = g.selectAll('.domain').data([0]), pathUpdate = path .enter() .append('path') .merge(path) .attr('class', 'domain'); // TODO: each attr should be one function and change its behavior by internal.orient, probably switch (internal.orient) { case 'bottom': { tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return internal.lineY2(d, i) }); textUpdate .attr('x', 0) .attr('y', function(d, i) { return internal.textY(d, i) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }); tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }); pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + internal.outerTickSize ); break } case 'top': { // TODO: rotated tick text tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return -1 * internal.lineY2(d, i) }); textUpdate .attr('x', 0) .attr('y', function(d, i) { return ( -1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2) ) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }); tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }); pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + -internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + -internal.outerTickSize ); break } case 'left': { tickTransform = internal.axisY; lineUpdate .attr('x2', -internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', -internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'end'); tspanUpdate .attr('x', -internal.tickLength) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }); pathUpdate.attr( 'd', 'M' + -internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + -internal.outerTickSize ); break } case 'right': { tickTransform = internal.axisY; lineUpdate .attr('x2', internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'start'); tspanUpdate.attr('x', internal.tickLength).attr('dy', function(d, i) { return internal.tspanDy(d, i) }); pathUpdate.attr( 'd', 'M' + internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + internal.outerTickSize ); break } } if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2; scale0 = scale1 = function(d) { return x(d) + dx }; } else if (scale0.rangeBand) { scale0 = scale1; } else { tickExit.call(tickTransform, scale1, internal.tickOffset); } tickEnter.call(tickTransform, scale0, internal.tickOffset); self = (transition ? tickUpdate.transition(transition) : tickUpdate) .style('opacity', 1) .call(tickTransform, scale1, internal.tickOffset); }); return self } axis.scale = function(x) { if (!arguments.length) { return internal.scale } internal.scale = x; return axis }; axis.orient = function(x) { if (!arguments.length) { return internal.orient } internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + '' : 'bottom'; return axis }; axis.tickFormat = function(format) { if (!arguments.length) { return internal.tickFormat } internal.tickFormat = format; return axis }; axis.tickCentered = function(isCentered) { if (!arguments.length) { return internal.tickCentered } internal.tickCentered = isCentered; return axis }; axis.tickOffset = function() { return internal.tickOffset }; axis.tickInterval = function() { var interval, length; if (params.isCategory) { interval = internal.tickOffset * 2; } else { length = axis.g .select('path.domain') .node() .getTotalLength() - internal.outerTickSize * 2; interval = length / axis.g.selectAll('line').size(); } return interval === Infinity ? 0 : interval }; axis.ticks = function() { if (!arguments.length) { return internal.tickArguments } internal.tickArguments = arguments; return axis }; axis.tickCulling = function(culling) { if (!arguments.length) { return internal.tickCulling } internal.tickCulling = culling; return axis }; axis.tickValues = function(x) { if (typeof x === 'function') { internal.tickValues = function() { return x(internal.scale.domain()) }; } else { if (!arguments.length) { return internal.tickValues } internal.tickValues = x; } return axis }; return axis }; var CLASS = { target: 'c3-target', chart: 'c3-chart', chartLine: 'c3-chart-line', chartLines: 'c3-chart-lines', chartBar: 'c3-chart-bar', chartBars: 'c3-chart-bars', chartText: 'c3-chart-text', chartTexts: 'c3-chart-texts', chartArc: 'c3-chart-arc', chartArcs: 'c3-chart-arcs', chartArcsTitle: 'c3-chart-arcs-title', chartArcsBackground: 'c3-chart-arcs-background', chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', selectedCircle: 'c3-selected-circle', selectedCircles: 'c3-selected-circles', eventRect: 'c3-event-rect', eventRects: 'c3-event-rects', eventRectsSingle: 'c3-event-rects-single', eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', regions: 'c3-regions', title: 'c3-title', tooltipContainer: 'c3-tooltip-container', tooltip: 'c3-tooltip', tooltipName: 'c3-tooltip-name', shape: 'c3-shape', shapes: 'c3-shapes', line: 'c3-line', lines: 'c3-lines', bar: 'c3-bar', bars: 'c3-bars', circle: 'c3-circle', circles: 'c3-circles', arc: 'c3-arc', arcLabelLine: 'c3-arc-label-line', arcs: 'c3-arcs', area: 'c3-area', areas: 'c3-areas', empty: 'c3-empty', text: 'c3-text', texts: 'c3-texts', gaugeValue: 'c3-gauge-value', grid: 'c3-grid', gridLines: 'c3-grid-lines', xgrid: 'c3-xgrid', xgrids: 'c3-xgrids', xgridLine: 'c3-xgrid-line', xgridLines: 'c3-xgrid-lines', xgridFocus: 'c3-xgrid-focus', ygrid: 'c3-ygrid', ygrids: 'c3-ygrids', ygridLine: 'c3-ygrid-line', ygridLines: 'c3-ygrid-lines', colorScale: 'c3-colorscale', stanfordElements: 'c3-stanford-elements', stanfordLine: 'c3-stanford-line', stanfordLines: 'c3-stanford-lines', stanfordRegion: 'c3-stanford-region', stanfordRegions: 'c3-stanford-regions', stanfordText: 'c3-stanford-text', stanfordTexts: 'c3-stanford-texts', axis: 'c3-axis', axisX: 'c3-axis-x', axisXLabel: 'c3-axis-x-label', axisY: 'c3-axis-y', axisYLabel: 'c3-axis-y-label', axisY2: 'c3-axis-y2', axisY2Label: 'c3-axis-y2-label', legendBackground: 'c3-legend-background', legendItem: 'c3-legend-item', legendItemEvent: 'c3-legend-item-event', legendItemTile: 'c3-legend-item-tile', legendItemHidden: 'c3-legend-item-hidden', legendItemFocused: 'c3-legend-item-focused', dragarea: 'c3-dragarea', EXPANDED: '_expanded_', SELECTED: '_selected_', INCLUDED: '_included_' }; class Axis { constructor(owner) { this.owner = owner; this.d3 = owner.d3; this.internal = AxisInternal; } } Axis.prototype.init = function init() { var $$ = this.owner, config = $$.config, main = $$.main; $$.axes.x = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisX) .attr('clip-path', config.axis_x_inner ? '' : $$.clipPathForXAxis) .attr('transform', $$.getTranslate('x')) .style('visibility', config.axis_x_show ? 'visible' : 'hidden'); $$.axes.x .append('text') .attr('class', CLASS.axisXLabel) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .style('text-anchor', this.textAnchorForXAxisLabel.bind(this)); $$.axes.y = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY) .attr('clip-path', config.axis_y_inner ? '' : $$.clipPathForYAxis) .attr('transform', $$.getTranslate('y')) .style('visibility', config.axis_y_show ? 'visible' : 'hidden'); $$.axes.y .append('text') .attr('class', CLASS.axisYLabel) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForYAxisLabel.bind(this)); $$.axes.y2 = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY2) // clip-path? .attr('transform', $$.getTranslate('y2')) .style('visibility', config.axis_y2_show ? 'visible' : 'hidden'); $$.axes.y2 .append('text') .attr('class', CLASS.axisY2Label) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForY2AxisLabel.bind(this)); }; Axis.prototype.getXAxis = function getXAxis( scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { var $$ = this.owner, config = $$.config, axisParams = { isCategory: $$.isCategorized(), withOuterTick: withOuterTick, tickMultiline: config.axis_x_tick_multiline, tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0, tickWidth: config.axis_x_tick_width, tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, withoutTransition: withoutTransition }, axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient); if ($$.isTimeSeries() && tickValues && typeof tickValues !== 'function') { tickValues = tickValues.map(function(v) { return $$.parseDate(v) }); } // Set tick axis.tickFormat(tickFormat).tickValues(tickValues); if ($$.isCategorized()) { axis.tickCentered(config.axis_x_tick_centered); if (isEmpty(config.axis_x_tick_culling)) { config.axis_x_tick_culling = false; } } return axis }; Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues( targets, axis ) { var $$ = this.owner, config = $$.config, tickValues; if (config.axis_x_tick_fit || config.axis_x_tick_count) { tickValues = this.generateTickValues( $$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries() ); } if (axis) { axis.tickValues(tickValues); } else { $$.xAxis.tickValues(tickValues); $$.subXAxis.tickValues(tickValues); } return tickValues }; Axis.prototype.getYAxis = function getYAxis( axisId, scale, orient, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { const $$ = this.owner; const config = $$.config; let tickFormat = config[`axis_${axisId}_tick_format`]; if (!tickFormat && $$.isAxisNormalized(axisId)) { tickFormat = x => `${x}%`; } const axis = new this.internal(this, { withOuterTick: withOuterTick, withoutTransition: withoutTransition, tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate }).axis .scale(scale) .orient(orient); if (tickFormat) { axis.tickFormat(tickFormat); } if ($$.isTimeSeriesY()) { axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval); } else { axis.tickValues(tickValues); } return axis }; Axis.prototype.getId = function getId(id) { var config = this.owner.config; return id in config.data_axes ? config.data_axes[id] : 'y' }; Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { // #2251 previously set any negative values to a whole number, // however both should be truncated according to the users format specification var $$ = this.owner, config = $$.config; let format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function(v) { return v }; if (config.axis_x_tick_format) { if (isFunction(config.axis_x_tick_format)) { format = config.axis_x_tick_format; } else if ($$.isTimeSeries()) { format = function(date) { return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : '' }; } } return isFunction(format) ? function(v) { return format.call($$, v) } : format }; Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { return tickValues ? tickValues : axis ? axis.tickValues() : undefined }; Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { return this.getTickValues( this.owner.config.axis_x_tick_values, this.owner.xAxis ) }; Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { return this.getTickValues( this.owner.config.axis_y_tick_values, this.owner.yAxis ) }; Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { return this.getTickValues( this.owner.config.axis_y2_tick_values, this.owner.y2Axis ) }; Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId( axisId ) { var $$ = this.owner, config = $$.config, option; if (axisId === 'y') { option = config.axis_y_label; } else if (axisId === 'y2') { option = config.axis_y2_label; } else if (axisId === 'x') { option = config.axis_x_label; } return option }; Axis.prototype.getLabelText = function getLabelText(axisId) { var option = this.getLabelOptionByAxisId(axisId); return isString(option) ? option : option ? option.text : null }; Axis.prototype.setLabelText = function setLabelText(axisId, text) { var $$ = this.owner, config = $$.config, option = this.getLabelOptionByAxisId(axisId); if (isString(option)) { if (axisId === 'y') { config.axis_y_label = text; } else if (axisId === 'y2') { config.axis_y2_label = text; } else if (axisId === 'x') { config.axis_x_label = text; } } else if (option) { option.text = text; } }; Axis.prototype.getLabelPosition = function getLabelPosition( axisId, defaultPosition ) { var option = this.getLabelOptionByAxisId(axisId), position = option && typeof option === 'object' && option.position ? option.position : defaultPosition; return { isInner: position.indexOf('inner') >= 0, isOuter: position.indexOf('outer') >= 0, isLeft: position.indexOf('left') >= 0, isCenter: position.indexOf('center') >= 0, isRight: position.indexOf('right') >= 0, isTop: position.indexOf('top') >= 0, isMiddle: position.indexOf('middle') >= 0, isBottom: position.indexOf('bottom') >= 0 } }; Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { return this.getLabelPosition( 'x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right' ) }; Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { return this.getLabelPosition( 'y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) }; Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { return this.getLabelPosition( 'y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) }; Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition() }; Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { return this.getLabelText('x') }; Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { return this.getLabelText('y') }; Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { return this.getLabelText('y2') }; Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { var $$ = this.owner; if (forHorizontal) { return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width } else { return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0 } }; Axis.prototype.dxForAxisLabel = function dxForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? '0.5em' : position.isRight ? '-0.5em' : '0' } else { return position.isTop ? '-0.5em' : position.isBottom ? '0.5em' : '0' } }; Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end' } else { return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end' } }; Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { return this.xForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { return this.dxForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { var $$ = this.owner, config = $$.config, position = this.getXAxisLabelPosition(); if (config.axis_rotated) { return position.isInner ? '1.2em' : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x')) } else { return position.isInner ? '-0.5em' : $$.getHorizontalAxisHeight('x') - 10 } }; Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { var $$ = this.owner, position = this.getYAxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '-0.5em' : '3em' } else { return position.isInner ? '1.2em' : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10) } }; Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { var $$ = this.owner, position = this.getY2AxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '1.2em' : '-2.2em' } else { return position.isInner ? '-0.5em' : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15) } }; Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( !$$.config.axis_rotated, this.getXAxisLabelPosition() ) }; Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getYAxisLabelPosition() ) }; Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getY2AxisLabelPosition() ) }; Axis.prototype.getMaxTickWidth = function getMaxTickWidth( id, withoutRecompute ) { var $$ = this.owner, maxWidth = 0, targetsToShow, scale, axis, dummy, svg; if (withoutRecompute && $$.currentMaxTickWidths[id]) { return $$.currentMaxTickWidths[id] } if ($$.svg) { targetsToShow = $$.filterTargetsToShow($$.data.targets); if (id === 'y') { scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); axis = this.getYAxis( id, scale, $$.yOrient, $$.yAxisTickValues, false, true, true ); } else if (id === 'y2') { scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); axis = this.getYAxis( id, scale, $$.y2Orient, $$.y2AxisTickValues, false, true, true ); } else { scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); axis = this.getXAxis( scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true ); this.updateXAxisTickValues(targetsToShow, axis); } dummy = $$.d3 .select('body') .append('div') .classed('c3', true) ;(svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0)), svg .append('g') .call(axis) .each(function() { $$.d3 .select(this) .selectAll('text') .each(function() { var box = getBBox(this); if (maxWidth < box.width) { maxWidth = box.width; } }); dummy.remove(); }); } $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; return $$.currentMaxTickWidths[id] }; Axis.prototype.updateLabels = function updateLabels(withTransition) { var $$ = this.owner; var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label) ;(withTransition ? axisXLabel.transition() : axisXLabel) .attr('x', this.xForXAxisLabel.bind(this)) .attr('dx', this.dxForXAxisLabel.bind(this)) .attr('dy', this.dyForXAxisLabel.bind(this)) .text(this.textForXAxisLabel.bind(this)) ;(withTransition ? axisYLabel.transition() : axisYLabel) .attr('x', this.xForYAxisLabel.bind(this)) .attr('dx', this.dxForYAxisLabel.bind(this)) .attr('dy', this.dyForYAxisLabel.bind(this)) .text(this.textForYAxisLabel.bind(this)) ;(withTransition ? axisY2Label.transition() : axisY2Label) .attr('x', this.xForY2AxisLabel.bind(this)) .attr('dx', this.dxForY2AxisLabel.bind(this)) .attr('dy', this.dyForY2AxisLabel.bind(this)) .text(this.textForY2AxisLabel.bind(this)); }; Axis.prototype.getPadding = function getPadding( padding, key, defaultValue, domainLength ) { var p = typeof padding === 'number' ? padding : padding[key]; if (!isValue(p)) { return defaultValue } if (padding.unit === 'ratio') { return padding[key] * domainLength } // assume padding is pixels if unit is not specified return this.convertPixelsToAxisPadding(p, domainLength) }; Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding( pixels, domainLength ) { var $$ = this.owner, length = $$.config.axis_rotated ? $$.width : $$.height; return domainLength * (pixels / length) }; Axis.prototype.generateTickValues = function generateTickValues( values, tickCount, forTimeSeries ) { var tickValues = values, targetCount, start, end, count, interval, i, tickValue; if (tickCount) { targetCount = isFunction(tickCount) ? tickCount() : tickCount; // compute ticks according to tickCount if (targetCount === 1) { tickValues = [values[0]]; } else if (targetCount === 2) { tickValues = [values[0], values[values.length - 1]]; } else if (targetCount > 2) { count = targetCount - 2; start = values[0]; end = values[values.length - 1]; interval = (end - start) / (count + 1); // re-construct unique values tickValues = [start]; for (i = 0; i < count; i++) { tickValue = +start + interval * (i + 1); tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); } tickValues.push(end); } } if (!forTimeSeries) { tickValues = tickValues.sort(function(a, b) { return a - b }); } return tickValues }; Axis.prototype.generateTransitions = function generateTransitions(duration) { var $$ = this.owner, axes = $$.axes; return { axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx } }; Axis.prototype.redraw = function redraw(duration, isHidden) { var $$ = this.owner, transition = duration ? $$.d3.transition().duration(duration) : null; $$.axes.x.style('opacity', isHidden ? 0 : 1).call($$.xAxis, transition); $$.axes.y.style('opacity', isHidden ? 0 : 1).call($$.yAxis, transition); $$.axes.y2.style('opacity', isHidden ? 0 : 1).call($$.y2Axis, transition); $$.axes.subx.style('opacity', isHidden ? 0 : 1).call($$.subXAxis, transition); }; var c3 = { version: '0.7.18', chart: { fn: Chart.prototype, internal: { fn: ChartInternal.prototype, axis: { fn: Axis.prototype, internal: { fn: AxisInternal.prototype } } } }, generate: function(config) { return new Chart(config) } }; ChartInternal.prototype.beforeInit = function() { // can do something }; ChartInternal.prototype.afterInit = function() { // can do something }; ChartInternal.prototype.init = function() { var $$ = this, config = $$.config; $$.initParams(); if (config.data_url) { $$.convertUrlToData( config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData ); } else if (config.data_json) { $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); } else if (config.data_rows) { $$.initWithData($$.convertRowsToData(config.data_rows)); } else if (config.data_columns) { $$.initWithData($$.convertColumnsToData(config.data_columns)); } else { throw Error('url or json or rows or columns is required.') } }; ChartInternal.prototype.initParams = function() { var $$ = this, d3 = $$.d3, config = $$.config; // MEMO: clipId needs to be unique because it conflicts when multiple charts exist $$.clipId = 'c3-' + new Date().valueOf() + '-clip'; $$.clipIdForXAxis = $$.clipId + '-xaxis'; $$.clipIdForYAxis = $$.clipId + '-yaxis'; $$.clipIdForGrid = $$.clipId + '-grid'; $$.clipIdForSubchart = $$.clipId + '-subchart'; $$.clipPath = $$.getClipPath($$.clipId); $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis); $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid); $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart); $$.dragStart = null; $$.dragging = false; $$.flowing = false; $$.cancelClick = false; $$.mouseover = undefined; $$.transiting = false; $$.color = $$.generateColor(); $$.levelColor = $$.generateLevelColor(); $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)( $$.config.data_xFormat ); $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat; $$.defaultAxisTimeFormat = function(date) { if (date.getMilliseconds()) { return d3.timeFormat('.%L')(date) } if (date.getSeconds()) { return d3.timeFormat(':%S')(date) } if (date.getMinutes()) { return d3.timeFormat('%I:%M')(date) } if (date.getHours()) { return d3.timeFormat('%I %p')(date) } if (date.getDay() && date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getMonth()) { return d3.timeFormat('%-m/%-d')(date) } return d3.timeFormat('%Y/%-m/%-d')(date) }; $$.hiddenTargetIds = []; $$.hiddenLegendIds = []; $$.focusedTargetIds = []; $$.defocusedTargetIds = []; $$.xOrient = config.axis_rotated ? config.axis_x_inner ? 'right' : 'left' : config.axis_x_inner ? 'top' : 'bottom'; $$.yOrient = config.axis_rotated ? config.axis_y_inner ? 'top' : 'bottom' : config.axis_y_inner ? 'right' : 'left'; $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? 'bottom' : 'top' : config.axis_y2_inner ? 'left' : 'right'; $$.subXOrient = config.axis_rotated ? 'left' : 'bottom'; $$.isLegendRight = config.legend_position === 'right'; $$.isLegendInset = config.legend_position === 'inset'; $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; $$.legendStep = 0; $$.legendItemWidth = 0; $$.legendItemHeight = 0; $$.currentMaxTickWidths = { x: 0, y: 0, y2: 0 }; $$.rotated_padding_left = 30; $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; $$.rotated_padding_top = 5; $$.withoutFadeIn = {}; $$.intervalForObserveInserted = undefined; $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js }; ChartInternal.prototype.initChartElements = function() { if (this.initBar) { this.initBar(); } if (this.initLine) { this.initLine(); } if (this.initArc) { this.initArc(); } if (this.initGauge) { this.initGauge(); } if (this.initText) { this.initText(); } }; ChartInternal.prototype.initWithData = function(data) { var $$ = this, d3 = $$.d3, config = $$.config; var defs, main, binding = true; $$.axis = new Axis($$); if (!config.bindto) { $$.selectChart = d3.selectAll([]); } else if (typeof config.bindto.node === 'function') { $$.selectChart = config.bindto; } else { $$.selectChart = d3.select(config.bindto); } if ($$.selectChart.empty()) { $$.selectChart = d3 .select(document.createElement('div')) .style('opacity', 0); $$.observeInserted($$.selectChart); binding = false; } $$.selectChart.html('').classed('c3', true); // Init data as targets $$.data.xs = {}; $$.data.targets = $$.convertDataToTargets(data); if (config.data_filter) { $$.data.targets = $$.data.targets.filter(config.data_filter); } // Set targets to hide if needed if (config.data_hide) { $$.addHiddenTargetIds( config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide ); } if (config.legend_hide) { $$.addHiddenLegendIds( config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide ); } if ($$.isStanfordGraphType()) { $$.initStanfordData(); } // Init sizes and scales $$.updateSizes(); $$.updateScales(); // Set domains for each scale $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); $$.y.domain($$.getYDomain($$.data.targets, 'y')); $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); $$.subX.domain($$.x.domain()); $$.subY.domain($$.y.domain()); $$.subY2.domain($$.y2.domain()); // Save original x domain for zoom update $$.orgXDomain = $$.x.domain(); /*-- Basic Elements --*/ // Define svgs $$.svg = $$.selectChart .append('svg') .style('overflow', 'hidden') .on('mouseenter', function() { return config.onmouseover.call($$) }) .on('mouseleave', function() { return config.onmouseout.call($$) }); if ($$.config.svg_classname) { $$.svg.attr('class', $$.config.svg_classname); } // Define defs defs = $$.svg.append('defs'); $$.clipChart = $$.appendClip(defs, $$.clipId); $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); $$.updateSvgSize(); // Define regions main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main')); if ($$.initPie) { $$.initPie(); } if ($$.initDragZoom) { $$.initDragZoom(); } if (config.subchart_show && $$.initSubchart) { $$.initSubchart(); } if ($$.initTooltip) { $$.initTooltip(); } if ($$.initLegend) { $$.initLegend(); } if ($$.initTitle) { $$.initTitle(); } if ($$.initZoom) { $$.initZoom(); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // Update selection based on size and scale // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart. if (config.subchart_show && $$.initSubchartBrush) { $$.initSubchartBrush(); } /*-- Main Region --*/ // text when empty main .append('text') .attr('class', CLASS.text + ' ' + CLASS.empty) .attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers. .attr('dominant-baseline', 'middle'); // vertical centering of text at y position in all browsers, except IE. // Regions $$.initRegion(); // Grids $$.initGrid(); // Define g for chart area main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.chart); // Grid lines if (config.grid_lines_front) { $$.initGridLines(); } $$.initStanfordElements(); // Cover whole with rects for events $$.initEventRect(); // Define g for chart $$.initChartElements(); // Add Axis $$.axis.init(); // Set targets $$.updateTargets($$.data.targets); // Set default extent if defined if (config.axis_x_selection) { $$.brush.selectionAsValue($$.getDefaultSelection()); } // Draw with targets if (binding) { $$.updateDimension(); $$.config.oninit.call($$); $$.redraw({ withTransition: false, withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransitionForAxis: false }); } // Bind to resize event $$.bindResize(); // Bind to window focus event $$.bindWindowFocus(); // export element of the chart $$.api.element = $$.selectChart.node(); }; ChartInternal.prototype.smoothLines = function(el, type) { var $$ = this; if (type === 'grid') { el.each(function() { var g = $$.d3.select(this), x1 = g.attr('x1'), x2 = g.attr('x2'), y1 = g.attr('y1'), y2 = g.attr('y2'); g.attr({ x1: Math.ceil(x1), x2: Math.ceil(x2), y1: Math.ceil(y1), y2: Math.ceil(y2) }); }); } }; ChartInternal.prototype.updateSizes = function() { var $$ = this, config = $$.config; var legendHeight = $$.legend ? $$.getLegendHeight() : 0, legendWidth = $$.legend ? $$.getLegendWidth() : 0, legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, hasArc = $$.hasArcType(), xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + xAxisHeight : 0; $$.currentWidth = $$.getCurrentWidth(); $$.currentHeight = $$.getCurrentHeight(); // for main $$.margin = config.axis_rotated ? { top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) } : { top: 4 + $$.getCurrentPaddingTop(), // for top tick text right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: hasArc ? 0 : $$.getCurrentPaddingLeft() }; // for subchart $$.margin2 = config.axis_rotated ? { top: $$.margin.top, right: NaN, bottom: 20 + legendHeightForBottom, left: $$.rotated_padding_left } : { top: $$.currentHeight - subchartHeight - legendHeightForBottom, right: NaN, bottom: xAxisHeight + legendHeightForBottom, left: $$.margin.left }; // for legend $$.margin3 = { top: 0, right: NaN, bottom: 0, left: 0 }; if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); } $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; if ($$.width < 0) { $$.width = 0; } if ($$.height < 0) { $$.height = 0; } $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; if ($$.width2 < 0) { $$.width2 = 0; } if ($$.height2 < 0) { $$.height2 = 0; } // for arc $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); if ($$.hasType('gauge') && !config.gauge_fullCircle) { $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); } if ($$.updateRadius) { $$.updateRadius(); } if ($$.isLegendRight && hasArc) { $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; } }; ChartInternal.prototype.updateTargets = function(targets) { var $$ = this, config = $$.config; /*-- Main --*/ //-- Text --// $$.updateTargetsForText(targets); //-- Bar --// $$.updateTargetsForBar(targets); //-- Line --// $$.updateTargetsForLine(targets); //-- Arc --// if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); } /*-- Sub --*/ if (config.subchart_show && $$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); } // Fade-in each chart $$.showTargets(); }; ChartInternal.prototype.showTargets = function() { var $$ = this; $$.svg .selectAll('.' + CLASS.target) .filter(function(d) { return $$.isTargetToShow(d.id) }) .transition() .duration($$.config.transition_duration) .style('opacity', 1); }; ChartInternal.prototype.redraw = function(options, transitions) { var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config; var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType); var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis; var hideAxis = $$.hasArcType(); var drawArea, drawBar, drawLine, xForText, yForText; var duration, durationForExit, durationForAxis; var transitionsToWait, waitForDraw, flow, transition; var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom; var xv = $$.xv.bind($$), cx, cy; options = options || {}; withY = getOption(options, 'withY', true); withSubchart = getOption(options, 'withSubchart', true); withTransition = getOption(options, 'withTransition', true); withTransform = getOption(options, 'withTransform', false); withUpdateXDomain = getOption(options, 'withUpdateXDomain', false); withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', false); withTrimXDomain = getOption(options, 'withTrimXDomain', true); withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain); withLegend = getOption(options, 'withLegend', false); withEventRect = getOption(options, 'withEventRect', true); withDimension = getOption(options, 'withDimension', true); withTransitionForExit = getOption( options, 'withTransitionForExit', withTransition ); withTransitionForAxis = getOption( options, 'withTransitionForAxis', withTransition ); duration = withTransition ? config.transition_duration : 0; durationForExit = withTransitionForExit ? duration : 0; durationForAxis = withTransitionForAxis ? duration : 0; transitions = transitions || $$.axis.generateTransitions(durationForAxis); // update legend and transform each g if (withLegend && config.legend_show) { $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); } else if (withDimension) { // need to update dimension (e.g. axis.y.tick.values) because y tick values should change // no need to update axis in it because they will be updated in redraw() $$.updateDimension(true); } // MEMO: needed for grids calculation if ($$.isCategorized() && targetsToShow.length === 0) { $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); } if (targetsToShow.length) { $$.updateXDomain( targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain ); if (!config.axis_x_tick_values) { tickValues = $$.axis.updateXAxisTickValues(targetsToShow); } } else { $$.xAxis.tickValues([]); $$.subXAxis.tickValues([]); } if (config.zoom_rescale && !options.flow) { xDomainForZoom = $$.x.orgDomain(); } $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); if (!config.axis_y_tick_values && config.axis_y_tick_count) { $$.yAxis.tickValues( $$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count) ); } if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { $$.y2Axis.tickValues( $$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count) ); } // axes $$.axis.redraw(durationForAxis, hideAxis); // Update axis label $$.axis.updateLabels(withTransition); // show/hide if manual culling needed if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if (config.axis_x_tick_culling && tickValues) { for (i = 1; i < tickValues.length; i++) { if (tickValues.length / i < config.axis_x_tick_culling_max) { intervalForCulling = i; break } } $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function(e) { var index = tickValues.indexOf(e); if (index >= 0) { d3.select(this).style( 'display', index % intervalForCulling ? 'none' : 'block' ); } }); } else { $$.svg .selectAll('.' + CLASS.axisX + ' .tick text') .style('display', 'block'); } } // setup drawer - MEMO: these must be called after axis updated drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); // update circleY based on updated parameters $$.updateCircleY(); // generate circle x/y functions depending on updated params cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); // Update sub domain if (withY) { $$.subY.domain($$.getYDomain(targetsToShow, 'y')); $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); } // xgrid focus $$.updateXgridFocus(); // Data empty label positioning and text. main .select('text.' + CLASS.text + '.' + CLASS.empty) .attr('x', $$.width / 2) .attr('y', $$.height / 2) .text(config.data_empty_label_text) .transition() .style('opacity', targetsToShow.length ? 0 : 1); // event rect if (withEventRect) { $$.redrawEventRect(); } // grid $$.updateGrid(duration); $$.updateStanfordElements(duration); // rect for regions $$.updateRegion(duration); // bars $$.updateBar(durationForExit); // lines, areas and circles $$.updateLine(durationForExit); $$.updateArea(durationForExit); $$.updateCircle(cx, cy); // text if ($$.hasDataLabel()) { $$.updateText(xForText, yForText, durationForExit); } // title if ($$.redrawTitle) { $$.redrawTitle(); } // arc if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } // subchart if (config.subchart_show && $$.redrawSubchart) { $$.redrawSubchart( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // circles for select main .selectAll('.' + CLASS.selectedCircles) .filter($$.isBarType.bind($$)) .selectAll('circle') .remove(); if (options.flow) { flow = $$.generateFlow({ targets: targetsToShow, flow: options.flow, duration: options.flow.duration, drawBar: drawBar, drawLine: drawLine, drawArea: drawArea, cx: cx, cy: cy, xv: xv, xForText: xForText, yForText: yForText }); } if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. // transition should be derived from one transition transition = d3.transition().duration(duration); transitionsToWait = [] ;[ $$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition) ].forEach(function(transitions) { transitions.forEach(function(transition) { transitionsToWait.push(transition); }); }); // Wait for end of transitions to call flow and onrendered callback waitForDraw = $$.generateWait(); transitionsToWait.forEach(function(t) { waitForDraw.add(t); }); waitForDraw(function() { if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } }); } else { $$.redrawBar(drawBar); $$.redrawLine(drawLine); $$.redrawArea(drawArea); $$.redrawCircle(cx, cy); $$.redrawText(xForText, yForText, options.flow); $$.redrawRegion(); $$.redrawGrid(); if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } } // update fadein condition $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = true; }); }; ChartInternal.prototype.updateAndRedraw = function(options) { var $$ = this, config = $$.config, transitions; options = options || {}; // same with redraw options.withTransition = getOption(options, 'withTransition', true); options.withTransform = getOption(options, 'withTransform', false); options.withLegend = getOption(options, 'withLegend', false); // NOT same with redraw options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true); options.withUpdateOrgXDomain = getOption( options, 'withUpdateOrgXDomain', true ); options.withTransitionForExit = false; options.withTransitionForTransform = getOption( options, 'withTransitionForTransform', options.withTransition ); // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) $$.updateSizes(); // MEMO: called in updateLegend in redraw if withLegend if (!(options.withLegend && config.legend_show)) { transitions = $$.axis.generateTransitions( options.withTransitionForAxis ? config.transition_duration : 0 ); // Update scales $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(options.withTransitionForTransform, transitions); } // Draw with new sizes & scales $$.redraw(options, transitions); }; ChartInternal.prototype.redrawWithoutRescale = function() { this.redraw({ withY: false, withSubchart: false, withEventRect: false, withTransitionForAxis: false }); }; ChartInternal.prototype.isTimeSeries = function() { return this.config.axis_x_type === 'timeseries' }; ChartInternal.prototype.isCategorized = function() { return this.config.axis_x_type.indexOf('categor') >= 0 }; ChartInternal.prototype.isCustomX = function() { var $$ = this, config = $$.config; return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)) }; ChartInternal.prototype.isTimeSeriesY = function() { return this.config.axis_y_type === 'timeseries' }; ChartInternal.prototype.getTranslate = function(target) { var $$ = this, config = $$.config, x, y; if (target === 'main') { x = asHalfPixel($$.margin.left); y = asHalfPixel($$.margin.top); } else if (target === 'context') { x = asHalfPixel($$.margin2.left); y = asHalfPixel($$.margin2.top); } else if (target === 'legend') { x = $$.margin3.left; y = $$.margin3.top; } else if (target === 'x') { x = 0; y = config.axis_rotated ? 0 : $$.height; } else if (target === 'y') { x = 0; y = config.axis_rotated ? $$.height : 0; } else if (target === 'y2') { x = config.axis_rotated ? 0 : $$.width; y = config.axis_rotated ? 1 : 0; } else if (target === 'subx') { x = 0; y = config.axis_rotated ? 0 : $$.height2; } else if (target === 'arc') { x = $$.arcWidth / 2; y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label } return 'translate(' + x + ',' + y + ')' }; ChartInternal.prototype.initialOpacity = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0 }; ChartInternal.prototype.initialOpacityForCircle = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0 }; ChartInternal.prototype.opacityForCircle = function(d) { var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show; var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0; return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0 }; ChartInternal.prototype.opacityForText = function() { return this.hasDataLabel() ? 1 : 0 }; ChartInternal.prototype.xx = function(d) { return d ? this.x(d.x) : null }; ChartInternal.prototype.xvCustom = function(d, xyValue) { var $$ = this, value = xyValue ? d[xyValue] : d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)) }; ChartInternal.prototype.yvCustom = function(d, xyValue) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y, value = xyValue ? d[xyValue] : d.value; return Math.ceil(yScale(value)) }; ChartInternal.prototype.xv = function(d) { var $$ = this, value = d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)) }; ChartInternal.prototype.yv = function(d) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; return Math.ceil(yScale(d.value)) }; ChartInternal.prototype.subxx = function(d) { return d ? this.subX(d.x) : null }; ChartInternal.prototype.transformMain = function(withTransition, transitions) { var $$ = this, xAxis, yAxis, y2Axis; if (transitions && transitions.axisX) { xAxis = transitions.axisX; } else { xAxis = $$.main.select('.' + CLASS.axisX); if (withTransition) { xAxis = xAxis.transition(); } } if (transitions && transitions.axisY) { yAxis = transitions.axisY; } else { yAxis = $$.main.select('.' + CLASS.axisY); if (withTransition) { yAxis = yAxis.transition(); } } if (transitions && transitions.axisY2) { y2Axis = transitions.axisY2; } else { y2Axis = $$.main.select('.' + CLASS.axisY2); if (withTransition) { y2Axis = y2Axis.transition(); } } (withTransition ? $$.main.transition() : $$.main).attr( 'transform', $$.getTranslate('main') ); xAxis.attr('transform', $$.getTranslate('x')); yAxis.attr('transform', $$.getTranslate('y')); y2Axis.attr('transform', $$.getTranslate('y2')); $$.main .select('.' + CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); }; ChartInternal.prototype.transformAll = function(withTransition, transitions) { var $$ = this; $$.transformMain(withTransition, transitions); if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); } if ($$.legend) { $$.transformLegend(withTransition); } }; ChartInternal.prototype.updateSvgSize = function() { var $$ = this, brush = $$.svg.select(`.${CLASS.brush} .overlay`); $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); $$.svg .selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]) .select('rect') .attr('width', $$.width) .attr('height', $$.height); $$.svg .select('#' + $$.clipIdForXAxis) .select('rect') .attr('x', $$.getXAxisClipX.bind($$)) .attr('y', $$.getXAxisClipY.bind($$)) .attr('width', $$.getXAxisClipWidth.bind($$)) .attr('height', $$.getXAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForYAxis) .select('rect') .attr('x', $$.getYAxisClipX.bind($$)) .attr('y', $$.getYAxisClipY.bind($$)) .attr('width', $$.getYAxisClipWidth.bind($$)) .attr('height', $$.getYAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForSubchart) .select('rect') .attr('width', $$.width) .attr('height', (brush.size() && brush.attr('height')) || 0); // MEMO: parent div's height will be bigger than svg when $$.selectChart.style('max-height', $$.currentHeight + 'px'); }; ChartInternal.prototype.updateDimension = function(withoutAxis) { var $$ = this; if (!withoutAxis) { if ($$.config.axis_rotated) { $$.axes.x.call($$.xAxis); $$.axes.subx.call($$.subXAxis); } else { $$.axes.y.call($$.yAxis); $$.axes.y2.call($$.y2Axis); } } $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); $$.transformAll(false); }; ChartInternal.prototype.observeInserted = function(selection) { var $$ = this, observer; if (typeof MutationObserver === 'undefined') { window.console.error('MutationObserver not defined.'); return } observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.previousSibling) { observer.disconnect(); // need to wait for completion of load because size calculation requires the actual sizes determined after that completion $$.intervalForObserveInserted = window.setInterval(function() { // parentNode will NOT be null when completed if (selection.node().parentNode) { window.clearInterval($$.intervalForObserveInserted); $$.updateDimension(); if ($$.brush) { $$.brush.update(); } $$.config.oninit.call($$); $$.redraw({ withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransition: false, withTransitionForTransform: false, withLegend: true }); selection.transition().style('opacity', 1); } }, 10); } }); }); observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }); }; /** * Binds handlers to the window resize event. */ ChartInternal.prototype.bindResize = function() { var $$ = this, config = $$.config; $$.resizeFunction = $$.generateResize(); // need to call .remove $$.resizeFunction.add(function() { config.onresize.call($$); }); if (config.resize_auto) { $$.resizeFunction.add(function() { if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } $$.resizeTimeout = window.setTimeout(function() { delete $$.resizeTimeout; $$.updateAndRedraw({ withUpdateXDomain: false, withUpdateOrgXDomain: false, withTransition: false, withTransitionForTransform: false, withLegend: true }); if ($$.brush) { $$.brush.update(); } }, 100); }); } $$.resizeFunction.add(function() { config.onresized.call($$); }); $$.resizeIfElementDisplayed = function() { // if element not displayed skip it if ($$.api == null || !$$.api.element.offsetParent) { return } $$.resizeFunction(); }; if (window.attachEvent) { window.attachEvent('onresize', $$.resizeIfElementDisplayed); } else if (window.addEventListener) { window.addEventListener('resize', $$.resizeIfElementDisplayed, false); } else { // fallback to this, if this is a very old browser var wrapper = window.onresize; if (!wrapper) { // create a wrapper that will call all charts wrapper = $$.generateResize(); } else if (!wrapper.add || !wrapper.remove) { // there is already a handler registered, make sure we call it too wrapper = $$.generateResize(); wrapper.add(window.onresize); } // add this graph to the wrapper, we will be removed if the user calls destroy wrapper.add($$.resizeFunction); window.onresize = function() { // if element not displayed skip it if (!$$.api.element.offsetParent) { return } wrapper(); }; } }; /** * Binds handlers to the window focus event. */ ChartInternal.prototype.bindWindowFocus = function() { if (this.windowFocusHandler) { // The handler is already set return } this.windowFocusHandler = () => { this.redraw(); }; window.addEventListener('focus', this.windowFocusHandler); }; /** * Unbinds from the window focus event. */ ChartInternal.prototype.unbindWindowFocus = function() { window.removeEventListener('focus', this.windowFocusHandler); delete this.windowFocusHandler; }; ChartInternal.prototype.generateResize = function() { var resizeFunctions = []; function callResizeFunctions() { resizeFunctions.forEach(function(f) { f(); }); } callResizeFunctions.add = function(f) { resizeFunctions.push(f); }; callResizeFunctions.remove = function(f) { for (var i = 0; i < resizeFunctions.length; i++) { if (resizeFunctions[i] === f) { resizeFunctions.splice(i, 1); break } } }; return callResizeFunctions }; ChartInternal.prototype.endall = function(transition, callback) { var n = 0; transition .each(function() { ++n; }) .on('end', function() { if (!--n) { callback.apply(this, arguments); } }); }; ChartInternal.prototype.generateWait = function() { var $$ = this; var transitionsToWait = [], f = function(callback) { var timer = setInterval(function() { if (!$$.isTabVisible()) { return } var done = 0; transitionsToWait.forEach(function(t) { if (t.empty()) { done += 1; return } try { t.transition(); } catch (e) { done += 1; } }); if (done === transitionsToWait.length) { clearInterval(timer); if (callback) { callback(); } } }, 50); }; f.add = function(transition) { transitionsToWait.push(transition); }; return f }; ChartInternal.prototype.parseDate = function(date) { var $$ = this, parsedDate; if (date instanceof Date) { parsedDate = date; } else if (typeof date === 'string') { parsedDate = $$.dataTimeParse(date); } else if (typeof date === 'object') { parsedDate = new Date(+date); } else if (typeof date === 'number' && !isNaN(date)) { parsedDate = new Date(+date); } if (!parsedDate || isNaN(+parsedDate)) { window.console.error("Failed to parse x '" + date + "' to Date object"); } return parsedDate }; ChartInternal.prototype.isTabVisible = function() { return !document.hidden }; ChartInternal.prototype.getPathBox = getPathBox; ChartInternal.prototype.CLASS = CLASS; /* jshint ignore:start */ (function() { if (!('SVGPathSeg' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) { this.pathSegType = type; this.pathSegTypeAsLetter = typeAsLetter; this._owningPathSegList = owningPathSegList; }; window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; window.SVGPathSeg.PATHSEG_UNKNOWN = 0; window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; window.SVGPathSeg.PATHSEG_LINETO_REL = 5; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; window.SVGPathSeg.PATHSEG_ARC_ABS = 10; window.SVGPathSeg.PATHSEG_ARC_REL = 11; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; // Notify owning PathSegList on any changes so they can be synchronized back to the path element. window.SVGPathSeg.prototype._segmentChanged = function() { if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this); }; window.SVGPathSegClosePath = function(owningPathSegList) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList ); }; window.SVGPathSegClosePath.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegClosePath.prototype.toString = function() { return '[object SVGPathSegClosePath]' }; window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter }; window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined) }; window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegMovetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegMovetoAbs.prototype.toString = function() { return '[object SVGPathSegMovetoAbs]' }; window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegMovetoRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegMovetoRel.prototype.toString = function() { return '[object SVGPathSegMovetoRel]' }; window.SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegLinetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoAbs.prototype.toString = function() { return '[object SVGPathSegLinetoAbs]' }; window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegLinetoRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoRel.prototype.toString = function() { return '[object SVGPathSegLinetoRel]' }; window.SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y) }; Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicAbs = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicAbs]' }; window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) }; Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicRel = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicRel]' }; window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) }; Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticAbs = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticAbs]' }; window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs( undefined, this._x, this._y, this._x1, this._y1 ) }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticRel = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList ); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticRel]' }; window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel( undefined, this._x, this._y, this._x1, this._y1 ) }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegArcAbs = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList ); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegArcAbs.prototype.toString = function() { return '[object SVGPathSegArcAbs]' }; window.SVGPathSegArcAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) }; Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcRel = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList ); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegArcRel.prototype.toString = function() { return '[object SVGPathSegArcRel]' }; window.SVGPathSegArcRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) }; Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList ); this._x = x; }; window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalAbs]' }; window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x }; window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x) }; Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList ); this._x = x; }; window.SVGPathSegLinetoHorizontalRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalRel]' }; window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x }; window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x) }; Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList ); this._y = y; }; window.SVGPathSegLinetoVerticalAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalAbs]' }; window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y }; window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y) }; Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList ); this._y = y; }; window.SVGPathSegLinetoVerticalRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalRel]' }; window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y }; window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y) }; Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothAbs = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList ); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothAbs]' }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs( undefined, this._x, this._y, this._x2, this._y2 ) }; Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoCubicSmoothRel = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList ); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothRel]' }; window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) }; window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel( undefined, this._x, this._y, this._x2, this._y2 ) }; Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticSmoothAbs = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]' }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs( undefined, this._x, this._y ) }; Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); window.SVGPathSegCurvetoQuadraticSmoothRel = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList ); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ); window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothRel]' }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel( undefined, this._x, this._y ) }; Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x; this._segmentChanged(); }, enumerable: true } ); Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y; this._segmentChanged(); }, enumerable: true } ); // Add createSVGPathSeg* functions to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined) }; window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicAbs( undefined, x, y, x1, y1, x2, y2 ) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicRel( undefined, x, y, x1, y1, x2, y2 ) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1) }; window.SVGPathElement.prototype.createSVGPathSegArcAbs = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcAbs( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) }; window.SVGPathElement.prototype.createSVGPathSegArcRel = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcRel( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function( x ) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x) }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function( x ) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x) }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function( y ) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y) }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function( y ) { return new window.SVGPathSegLinetoVerticalRel(undefined, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y) }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y) }; if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { // Add getPathSegAtLength to SVGPathElement. // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. window.SVGPathElement.prototype.getPathSegAtLength = function(distance) { if (distance === undefined || !isFinite(distance)) throw 'Invalid arguments.' var measurementElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); measurementElement.setAttribute('d', this.getAttribute('d')); var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; // If the path is empty, return 0. if (lastPathSegment <= 0) return 0 do { measurementElement.pathSegList.removeItem(lastPathSegment); if (distance > measurementElement.getTotalLength()) break lastPathSegment--; } while (lastPathSegment > 0) return lastPathSegment }; } } if (!('SVGPathSegList' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList window.SVGPathSegList = function(pathElement) { this._pathElement = pathElement; this._list = this._parsePath(this._pathElement.getAttribute('d')); // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { attributes: true, attributeFilter: ['d'] }; this._pathElementMutationObserver = new MutationObserver( this._updateListFromPathMutations.bind(this) ); this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ); }; window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { get: function() { this._checkPathSynchronizedToList(); return this._list.length }, enumerable: true }); // Add the pathSegList accessors to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { get: function() { if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this); return this._pathSegList }, enumerable: true }); // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. Object.defineProperty( window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); Object.defineProperty( window.SVGPathElement.prototype, 'animatedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); Object.defineProperty( window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ); // Process any pending mutations to the path element and update the list as needed. // This should be the first call of all public functions and is needed because // MutationObservers are not synchronous so we can have pending asynchronous mutations. window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() { this._updateListFromPathMutations( this._pathElementMutationObserver.takeRecords() ); }; window.SVGPathSegList.prototype._updateListFromPathMutations = function( mutationRecords ) { if (!this._pathElement) return var hasPathMutations = false; mutationRecords.forEach(function(record) { if (record.attributeName == 'd') hasPathMutations = true; }); if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute('d')); }; // Serialize the list and update the path's 'd' attribute. window.SVGPathSegList.prototype._writeListToPath = function() { this._pathElementMutationObserver.disconnect(); this._pathElement.setAttribute( 'd', window.SVGPathSegList._pathSegArrayAsString(this._list) ); this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ); }; // When a path segment changes the list needs to be synchronized back to the path element. window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) { this._writeListToPath(); }; window.SVGPathSegList.prototype.clear = function() { this._checkPathSynchronizedToList(); this._list.forEach(function(pathSeg) { pathSeg._owningPathSegList = null; }); this._list = []; this._writeListToPath(); }; window.SVGPathSegList.prototype.initialize = function(newItem) { this._checkPathSynchronizedToList(); this._list = [newItem]; newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype._checkValidIndex = function(index) { if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw 'INDEX_SIZE_ERR' }; window.SVGPathSegList.prototype.getItem = function(index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); return this._list[index] }; window.SVGPathSegList.prototype.insertItemBefore = function( newItem, index ) { this._checkPathSynchronizedToList(); // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. if (index > this.numberOfItems) index = this.numberOfItems; if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.splice(index, 0, newItem); newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype.replaceItem = function(newItem, index) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._checkValidIndex(index); this._list[index] = newItem; newItem._owningPathSegList = this; this._writeListToPath(); return newItem }; window.SVGPathSegList.prototype.removeItem = function(index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); var item = this._list[index]; this._list.splice(index, 1); this._writeListToPath(); return item }; window.SVGPathSegList.prototype.appendItem = function(newItem) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.push(newItem); newItem._owningPathSegList = this; // TODO: Optimize this to just append to the existing attribute. this._writeListToPath(); return newItem }; window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) { var string = ''; var first = true; pathSegArray.forEach(function(pathSeg) { if (first) { first = false; string += pathSeg._asPathString(); } else { string += ' ' + pathSeg._asPathString(); } }); return string }; // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. window.SVGPathSegList.prototype._parsePath = function(string) { if (!string || string.length == 0) return [] var owningPathSegList = this; var Builder = function() { this.pathSegList = []; }; Builder.prototype.appendSegment = function(pathSeg) { this.pathSegList.push(pathSeg); }; var Source = function(string) { this._string = string; this._currentIndex = 0; this._endIndex = this._string.length; this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; this._skipOptionalSpaces(); }; Source.prototype._isCurrentSpace = function() { var character = this._string[this._currentIndex]; return ( character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f') ) }; Source.prototype._skipOptionalSpaces = function() { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) this._currentIndex++; return this._currentIndex < this._endIndex }; Source.prototype._skipOptionalSpacesOrDelimiter = function() { if ( this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ',' ) return false if (this._skipOptionalSpaces()) { if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ',' ) { this._currentIndex++; this._skipOptionalSpaces(); } } return this._currentIndex < this._endIndex }; Source.prototype.hasMoreData = function() { return this._currentIndex < this._endIndex }; Source.prototype.peekSegmentType = function() { var lookahead = this._string[this._currentIndex]; return this._pathSegTypeFromChar(lookahead) }; Source.prototype._pathSegTypeFromChar = function(lookahead) { switch (lookahead) { case 'Z': case 'z': return window.SVGPathSeg.PATHSEG_CLOSEPATH case 'M': return window.SVGPathSeg.PATHSEG_MOVETO_ABS case 'm': return window.SVGPathSeg.PATHSEG_MOVETO_REL case 'L': return window.SVGPathSeg.PATHSEG_LINETO_ABS case 'l': return window.SVGPathSeg.PATHSEG_LINETO_REL case 'C': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS case 'c': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL case 'Q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS case 'q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL case 'A': return window.SVGPathSeg.PATHSEG_ARC_ABS case 'a': return window.SVGPathSeg.PATHSEG_ARC_REL case 'H': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS case 'h': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL case 'V': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS case 'v': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL case 'S': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS case 's': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL case 'T': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS case 't': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL default: return window.SVGPathSeg.PATHSEG_UNKNOWN } }; Source.prototype._nextCommandHelper = function( lookahead, previousCommand ) { // Check for remaining coordinates in the current command. if ( (lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH ) { if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL return previousCommand } return window.SVGPathSeg.PATHSEG_UNKNOWN }; Source.prototype.initialCommandIsMoveTo = function() { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) return true var command = this.peekSegmentType(); // Path must start with moveTo. return ( command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL ) }; // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF Source.prototype._parseNumber = function() { var exponent = 0; var integer = 0; var frac = 1; var decimal = 0; var sign = 1; var expsign = 1; var startIndex = this._currentIndex; this._skipOptionalSpaces(); // Read the sign. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '+' ) this._currentIndex++; else if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '-' ) { this._currentIndex++; sign = -1; } if ( this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) != '.') ) // The first character of a number must be one of [0-9+-.]. return undefined // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex; while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) this._currentIndex++; // Advance to first non-digit. if (this._currentIndex != startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1; var multiplier = 1; while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); multiplier *= 10; } } // Read the decimals. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '.' ) { this._currentIndex++; // There must be a least one digit following the . if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { frac *= 10; decimal += (this._string.charAt(this._currentIndex) - '0') / frac; this._currentIndex += 1; } } // Read the exponent part. if ( this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == 'e' || this._string.charAt(this._currentIndex) == 'E') && this._string.charAt(this._currentIndex + 1) != 'x' && this._string.charAt(this._currentIndex + 1) != 'm' ) { this._currentIndex++; // Read the sign of the exponent. if (this._string.charAt(this._currentIndex) == '+') { this._currentIndex++; } else if (this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; expsign = -1; } // There must be an exponent. if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { exponent *= 10; exponent += this._string.charAt(this._currentIndex) - '0'; this._currentIndex++; } } var number = integer + decimal; number *= sign; if (exponent) number *= Math.pow(10, expsign * exponent); if (startIndex == this._currentIndex) return undefined this._skipOptionalSpacesOrDelimiter(); return number }; Source.prototype._parseArcFlag = function() { if (this._currentIndex >= this._endIndex) return undefined var flag = false; var flagChar = this._string.charAt(this._currentIndex++); if (flagChar == '0') flag = false; else if (flagChar == '1') flag = true; else return undefined this._skipOptionalSpacesOrDelimiter(); return flag }; Source.prototype.parseSegment = function() { var lookahead = this._string[this._currentIndex]; var command = this._pathSegTypeFromChar(lookahead); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { // Possibly an implicit command. Not allowed if this is the first command. if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null command = this._nextCommandHelper(lookahead, this._previousCommand); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null } else { this._currentIndex++; } this._previousCommand = command; switch (command) { case window.SVGPathSeg.PATHSEG_MOVETO_REL: return new window.SVGPathSegMovetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_MOVETO_ABS: return new window.SVGPathSegMovetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_REL: return new window.SVGPathSegLinetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_ABS: return new window.SVGPathSegLinetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: return new window.SVGPathSegLinetoHorizontalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: return new window.SVGPathSegLinetoHorizontalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: return new window.SVGPathSegLinetoVerticalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: return new window.SVGPathSegLinetoVerticalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CLOSEPATH: this._skipOptionalSpaces(); return new window.SVGPathSegClosePath(owningPathSegList) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothRel( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothAbs( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticRel( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticAbs( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new window.SVGPathSegCurvetoQuadraticSmoothRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new window.SVGPathSegCurvetoQuadraticSmoothAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_ARC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) case window.SVGPathSeg.PATHSEG_ARC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) default: throw 'Unknown path seg type.' } }; var builder = new Builder(); var source = new Source(string); if (!source.initialCommandIsMoveTo()) return [] while (source.hasMoreData()) { var pathSeg = source.parseSegment(); if (!pathSeg) return [] builder.appendSegment(pathSeg); } return builder.pathSegList }; } })(); // String.padEnd polyfill for IE11 // // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== 'undefined' ? padString : ' '); if (this.length > targetLength) { return String(this) } else { targetLength = targetLength - this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed } return String(this) + padString.slice(0, targetLength) } }; } // Object.assign polyfill for IE11 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object') } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to }, writable: true, configurable: true }); } /* jshint ignore:end */ Chart.prototype.axis = function() {}; Chart.prototype.axis.labels = function(labels) { var $$ = this.internal; if (arguments.length) { Object.keys(labels).forEach(function(axisId) { $$.axis.setLabelText(axisId, labels[axisId]); }); $$.axis.updateLabels(); } // TODO: return some values? }; Chart.prototype.axis.max = function(max) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof max === 'object') { if (isValue(max.x)) { config.axis_x_max = max.x; } if (isValue(max.y)) { config.axis_y_max = max.y; } if (isValue(max.y2)) { config.axis_y2_max = max.y2; } } else { config.axis_y_max = config.axis_y2_max = max; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_max, y: config.axis_y_max, y2: config.axis_y2_max } } }; Chart.prototype.axis.min = function(min) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof min === 'object') { if (isValue(min.x)) { config.axis_x_min = min.x; } if (isValue(min.y)) { config.axis_y_min = min.y; } if (isValue(min.y2)) { config.axis_y2_min = min.y2; } } else { config.axis_y_min = config.axis_y2_min = min; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_min, y: config.axis_y_min, y2: config.axis_y2_min } } }; Chart.prototype.axis.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.axis.max(range.max); } if (isDefined(range.min)) { this.axis.min(range.min); } } else { return { max: this.axis.max(), min: this.axis.min() } } }; Chart.prototype.axis.types = function(types) { const $$ = this.internal; if (types === undefined) { return { y: $$.config.axis_y_type, y2: $$.config.axis_y2_type } } else { if (isDefined(types.y)) { $$.config.axis_y_type = types.y; } if (isDefined(types.y2)) { $$.config.axis_y2_type = types.y2; } $$.updateScales(); $$.redraw(); } }; Chart.prototype.category = function(i, category) { var $$ = this.internal, config = $$.config; if (arguments.length > 1) { config.axis_x_categories[i] = category; $$.redraw(); } return config.axis_x_categories[i] }; Chart.prototype.categories = function(categories) { var $$ = this.internal, config = $$.config; if (!arguments.length) { return config.axis_x_categories } config.axis_x_categories = categories; $$.redraw(); return config.axis_x_categories }; Chart.prototype.resize = function(size) { var $$ = this.internal, config = $$.config; config.size_width = size ? size.width : null; config.size_height = size ? size.height : null; this.flush(); }; Chart.prototype.flush = function() { var $$ = this.internal; $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); }; Chart.prototype.destroy = function() { var $$ = this.internal; window.clearInterval($$.intervalForObserveInserted); if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } if (window.detachEvent) { window.detachEvent('onresize', $$.resizeIfElementDisplayed); } else if (window.removeEventListener) { window.removeEventListener('resize', $$.resizeIfElementDisplayed); } else { var wrapper = window.onresize; // check if no one else removed our wrapper and remove our resizeFunction from it if (wrapper && wrapper.add && wrapper.remove) { wrapper.remove($$.resizeFunction); } } // Removes the inner resize functions $$.resizeFunction.remove(); // Unbinds from the window focus event $$.unbindWindowFocus(); $$.selectChart.classed('c3', false).html(''); // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. Object.keys($$).forEach(function(key) { $$[key] = null; }); return null }; // TODO: fix Chart.prototype.color = function(id) { var $$ = this.internal; return $$.color(id) // more patterns }; Chart.prototype.data = function(targetIds) { var targets = this.internal.data.targets; return typeof targetIds === 'undefined' ? targets : targets.filter(function(t) { return [].concat(targetIds).indexOf(t.id) >= 0 }) }; Chart.prototype.data.shown = function(targetIds) { return this.internal.filterTargetsToShow(this.data(targetIds)) }; /** * Get values of the data loaded in the chart. * * @param {String|Array} targetId This API returns the value of specified target. * @param flat * @return {Array} Data values */ Chart.prototype.data.values = function(targetId, flat = true) { let values = null; if (targetId) { const targets = this.data(targetId); if (targets && isArray(targets)) { values = targets.reduce((ret, v) => { const dataValue = v.values.map(d => d.value); if (flat) { ret = ret.concat(dataValue); } else { ret.push(dataValue); } return ret }, []); } } return values }; Chart.prototype.data.names = function(names) { this.internal.clearLegendItemTextBoxCache(); return this.internal.updateDataAttributes('names', names) }; Chart.prototype.data.colors = function(colors) { return this.internal.updateDataAttributes('colors', colors) }; Chart.prototype.data.axes = function(axes) { return this.internal.updateDataAttributes('axes', axes) }; Chart.prototype.data.stackNormalized = function(normalized) { if (normalized === undefined) { return this.internal.isStackNormalized() } this.internal.config.data_stack_normalize = !!normalized; this.internal.redraw(); }; Chart.prototype.donut = function() {}; Chart.prototype.donut.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.donut_padAngle } this.internal.config.donut_padAngle = padAngle; this.flush(); }; Chart.prototype.flow = function(args) { var $$ = this.internal, targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; if (args.json) { data = $$.convertJsonToData(args.json, args.keys); } else if (args.rows) { data = $$.convertRowsToData(args.rows); } else if (args.columns) { data = $$.convertColumnsToData(args.columns); } else { return } targets = $$.convertDataToTargets(data, true); // Update/Add data $$.data.targets.forEach(function(t) { var found = false, i, j; for (i = 0; i < targets.length; i++) { if (t.id === targets[i].id) { found = true; if (t.values[t.values.length - 1]) { tail = t.values[t.values.length - 1].index + 1; } length = targets[i].values.length; for (j = 0; j < length; j++) { targets[i].values[j].index = tail + j; if (!$$.isTimeSeries()) { targets[i].values[j].x = tail + j; } } t.values = t.values.concat(targets[i].values); targets.splice(i, 1); break } } if (!found) { notfoundIds.push(t.id); } }); // Append null for not found targets $$.data.targets.forEach(function(t) { var i, j; for (i = 0; i < notfoundIds.length; i++) { if (t.id === notfoundIds[i]) { tail = t.values[t.values.length - 1].index + 1; for (j = 0; j < length; j++) { t.values.push({ id: t.id, index: tail + j, x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, value: null }); } } } }); // Generate null values for new target if ($$.data.targets.length) { targets.forEach(function(t) { var i, missing = []; for (i = $$.data.targets[0].values[0].index; i < tail; i++) { missing.push({ id: t.id, index: i, x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, value: null }); } t.values.forEach(function(v) { v.index += tail; if (!$$.isTimeSeries()) { v.x += tail; } }); t.values = missing.concat(t.values); }); } $$.data.targets = $$.data.targets.concat(targets); // add remained // check data count because behavior needs to change when it's only one dataCount = $$.getMaxDataCount(); baseTarget = $$.data.targets[0]; baseValue = baseTarget.values[0]; // Update length to flow if needed if (isDefined(args.to)) { length = 0; to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; baseTarget.values.forEach(function(v) { if (v.x < to) { length++; } }); } else if (isDefined(args.length)) { length = args.length; } // If only one data, update the domain to flow from left edge of the chart if (!orgDataCount) { if ($$.isTimeSeries()) { if (baseTarget.values.length > 1) { diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; } else { diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; } } else { diff = 1; } domain = [baseValue.x - diff, baseValue.x]; $$.updateXDomain(null, true, true, false, domain); } else if (orgDataCount === 1) { if ($$.isTimeSeries()) { diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; $$.updateXDomain(null, true, true, false, domain); } } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ flow: { index: baseValue.index, length: length, duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, done: args.done, orgDataCount: orgDataCount }, withLegend: true, withTransition: orgDataCount > 1, withTrimXDomain: false, withUpdateXAxis: true }); }; ChartInternal.prototype.generateFlow = function(args) { var $$ = this, config = $$.config, d3 = $$.d3; return function() { var targets = args.targets, flow = args.flow, drawBar = args.drawBar, drawLine = args.drawLine, drawArea = args.drawArea, cx = args.cx, cy = args.cy, xv = args.xv, xForText = args.xForText, yForText = args.yForText, duration = args.duration; var translateX, scaleX = 1, transform, flowIndex = flow.index, flowLength = flow.length, flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, flowIndex + flowLength ), orgDomain = $$.x.domain(), domain, durationForFlow = flow.duration || duration, done = flow.done || function() {}, wait = $$.generateWait(); var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle; // set flag $$.flowing = true; // remove head data after rendered $$.data.targets.forEach(function(d) { d.values.splice(0, flowLength); }); // update x domain to generate axis elements for flow domain = $$.updateXDomain(targets, true, true); // update elements related to x scale if ($$.updateXGrid) { $$.updateXGrid(true); } xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid xgridLines = $$.xgridLines || d3.selectAll([]); mainRegion = $$.mainRegion || d3.selectAll([]); mainText = $$.mainText || d3.selectAll([]); mainBar = $$.mainBar || d3.selectAll([]); mainLine = $$.mainLine || d3.selectAll([]); mainArea = $$.mainArea || d3.selectAll([]); mainCircle = $$.mainCircle || d3.selectAll([]); // generate transform to flow if (!flow.orgDataCount) { // if empty if ($$.data.targets[0].values.length !== 1) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, $$.data.targets[0].values.length - 1 ); translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } else { translateX = diffDomain(domain) / 2; } } } else if ( flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x) ) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } } scaleX = diffDomain(orgDomain) / diffDomain(domain); transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; $$.hideXGridFocus(); var flowTransition = d3 .transition() .ease(d3.easeLinear) .duration(durationForFlow); wait.add($$.xAxis($$.axes.x, flowTransition)); wait.add(mainBar.transition(flowTransition).attr('transform', transform)); wait.add(mainLine.transition(flowTransition).attr('transform', transform)); wait.add(mainArea.transition(flowTransition).attr('transform', transform)); wait.add(mainCircle.transition(flowTransition).attr('transform', transform)); wait.add(mainText.transition(flowTransition).attr('transform', transform)); wait.add( mainRegion .filter($$.isRegionOnX) .transition(flowTransition) .attr('transform', transform) ); wait.add(xgrid.transition(flowTransition).attr('transform', transform)); wait.add(xgridLines.transition(flowTransition).attr('transform', transform)); wait(function() { var i, shapes = [], texts = []; // remove flowed elements if (flowLength) { for (i = 0; i < flowLength; i++) { shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); texts.push('.' + CLASS.text + '-' + (flowIndex + i)); } $$.svg .selectAll('.' + CLASS.shapes) .selectAll(shapes) .remove(); $$.svg .selectAll('.' + CLASS.texts) .selectAll(texts) .remove(); $$.svg.select('.' + CLASS.xgrid).remove(); } // draw again for removing flowed elements and reverting attr xgrid .attr('transform', null) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); xgridLines.attr('transform', null); xgridLines .select('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv); xgridLines .select('text') .attr('x', config.axis_rotated ? $$.width : 0) .attr('y', xv); mainBar.attr('transform', null).attr('d', drawBar); mainLine.attr('transform', null).attr('d', drawLine); mainArea.attr('transform', null).attr('d', drawArea); mainCircle .attr('transform', null) .attr('cx', cx) .attr('cy', cy); mainText .attr('transform', null) .attr('x', xForText) .attr('y', yForText) .style('fill-opacity', $$.opacityForText.bind($$)); mainRegion.attr('transform', null); mainRegion .filter($$.isRegionOnX) .attr('x', $$.regionX.bind($$)) .attr('width', $$.regionWidth.bind($$)); // callback for end of flow done(); $$.flowing = false; }); } }; Chart.prototype.focus = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), this.revert(); this.defocus(); candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.expandArc(targetIds); } $$.toggleFocusLegend(targetIds, true); $$.focusedTargetIds = targetIds; $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); }; Chart.prototype.defocus = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } $$.toggleFocusLegend(targetIds, false); $$.focusedTargetIds = $$.focusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); $$.defocusedTargetIds = targetIds; }; Chart.prototype.revert = function(targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } if ($$.config.legend_show) { $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); $$.legend .selectAll($$.selectorLegends(targetIds)) .filter(function() { return $$.d3.select(this).classed(CLASS.legendItemFocused) }) .classed(CLASS.legendItemFocused, false); } $$.focusedTargetIds = []; $$.defocusedTargetIds = []; }; Chart.prototype.xgrids = function(grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_x_lines } config.grid_x_lines = grids; $$.redrawWithoutRescale(); return config.grid_x_lines }; Chart.prototype.xgrids.add = function(grids) { var $$ = this.internal; return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])) }; Chart.prototype.xgrids.remove = function(params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, true); }; Chart.prototype.ygrids = function(grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_y_lines } config.grid_y_lines = grids; $$.redrawWithoutRescale(); return config.grid_y_lines }; Chart.prototype.ygrids.add = function(grids) { var $$ = this.internal; return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])) }; Chart.prototype.ygrids.remove = function(params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, false); }; Chart.prototype.groups = function(groups) { var $$ = this.internal, config = $$.config; if (isUndefined(groups)) { return config.data_groups } config.data_groups = groups; $$.redraw(); return config.data_groups }; Chart.prototype.legend = function() {}; Chart.prototype.legend.show = function(targetIds) { var $$ = this.internal; $$.showLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: true }); }; Chart.prototype.legend.hide = function(targetIds) { var $$ = this.internal; $$.hideLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: false }); }; Chart.prototype.load = function(args) { var $$ = this.internal, config = $$.config; // update xs if specified if (args.xs) { $$.addXs(args.xs); } // update names if exists if ('names' in args) { Chart.prototype.data.names.bind(this)(args.names); } // update classes if exists if ('classes' in args) { Object.keys(args.classes).forEach(function(id) { config.data_classes[id] = args.classes[id]; }); } // update categories if exists if ('categories' in args && $$.isCategorized()) { config.axis_x_categories = args.categories; } // update axes if exists if ('axes' in args) { Object.keys(args.axes).forEach(function(id) { config.data_axes[id] = args.axes[id]; }); } // update colors if exists if ('colors' in args) { Object.keys(args.colors).forEach(function(id) { config.data_colors[id] = args.colors[id]; }); } // use cache if exists if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { $$.load($$.getCaches(args.cacheIds), args.done); return } // unload if needed if (args.unload) { // TODO: do not unload if target will load (included in url/rows/columns) $$.unload( $$.mapToTargetIds(args.unload === true ? null : args.unload), function() { $$.loadFromArgs(args); } ); } else { $$.loadFromArgs(args); } }; Chart.prototype.unload = function(args) { var $$ = this.internal; args = args || {}; if (args instanceof Array) { args = { ids: args }; } else if (typeof args === 'string') { args = { ids: [args] }; } $$.unload($$.mapToTargetIds(args.ids), function() { $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }); }; Chart.prototype.pie = function() {}; Chart.prototype.pie.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.pie_padAngle } this.internal.config.pie_padAngle = padAngle; this.flush(); }; Chart.prototype.regions = function(regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions } config.regions = regions; $$.redrawWithoutRescale(); return config.regions }; Chart.prototype.regions.add = function(regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions } config.regions = config.regions.concat(regions); $$.redrawWithoutRescale(); return config.regions }; Chart.prototype.regions.remove = function(options) { var $$ = this.internal, config = $$.config, duration, classes, regions; options = options || {}; duration = getOption(options, 'duration', config.transition_duration); classes = getOption(options, 'classes', [CLASS.region]); regions = $$.main.select('.' + CLASS.regions).selectAll( classes.map(function(c) { return '.' + c }) ) ;(duration ? regions.transition().duration(duration) : regions) .style('opacity', 0) .remove(); config.regions = config.regions.filter(function(region) { var found = false; if (!region['class']) { return true } region['class'].split(' ').forEach(function(c) { if (classes.indexOf(c) >= 0) { found = true; } }); return !found }); return config.regions }; Chart.prototype.selected = function(targetId) { var $$ = this.internal, d3 = $$.d3; return $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)) .selectAll('.' + CLASS.shape) .filter(function() { return d3.select(this).classed(CLASS.SELECTED) }) .nodes() .map(function(d) { var data = d.__data__; return data.data ? data.data : data }) }; Chart.prototype.select = function(ids, indices, resetOther) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d) && !isSelected) { toggle(true, shape.classed(CLASS.SELECTED, true), d, i); } } else if (isDefined(resetOther) && resetOther) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } }); }; Chart.prototype.unselect = function(ids, indices) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d)) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } } }); }; Chart.prototype.show = function(targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.removeHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('display', isIE() ? 'block' : 'initial', 'important') .style('opacity', 1, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 1); }); if (options.withLegend) { $$.showLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.hide = function(targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.addHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('opacity', 0, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 0); targets.style('display', 'none'); }); if (options.withLegend) { $$.hideLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.toggle = function(targetIds, options) { var that = this, $$ = this.internal; $$.mapToTargetIds(targetIds).forEach(function(targetId) { $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); }); }; Chart.prototype.subchart = function() {}; Chart.prototype.subchart.isShown = function() { const $$ = this.internal; return $$.config.subchart_show }; Chart.prototype.subchart.show = function() { const $$ = this.internal; if ($$.config.subchart_show) { return } $$.config.subchart_show = true; // insert DOM $$.initSubchart(); // update dimensions with sub chart now visible $$.updateDimension(); // insert brush (depends on sizes previously updated) $$.initSubchartBrush(); // attach data $$.updateTargetsForSubchart($$.getTargets()); // reset fade-in state $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = false; }); // redraw chart ! $$.updateAndRedraw(); // update visible targets ! $$.showTargets(); }; Chart.prototype.subchart.hide = function() { const $$ = this.internal; if (!$$.config.subchart_show) { return } $$.config.subchart_show = false; // remove DOM $$.removeSubchart(); // re-render chart $$.redraw(); }; Chart.prototype.tooltip = function() {}; Chart.prototype.tooltip.show = function(args) { var $$ = this.internal, targets, data, mouse = {}; // determine mouse position on the chart if (args.mouse) { mouse = args.mouse; } else { // determine focus data if (args.data) { data = args.data; } else if (typeof args.x !== 'undefined') { if (args.id) { targets = $$.data.targets.filter(function(t) { return t.id === args.id }); } else { targets = $$.data.targets; } data = $$.filterByX(targets, args.x).slice(0, 1)[0]; } mouse = data ? $$.getMousePosition(data) : null; } // emulate mouse events to show $$.dispatchEvent('mousemove', mouse); $$.config.tooltip_onshow.call($$, data); }; Chart.prototype.tooltip.hide = function() { // TODO: get target data by checking the state of focus this.internal.dispatchEvent('mouseout', 0); this.internal.config.tooltip_onhide.call(this); }; Chart.prototype.transform = function(type, targetIds) { var $$ = this.internal, options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; $$.transformTo(targetIds, type, options); }; ChartInternal.prototype.transformTo = function( targetIds, type, optionsForRedraw ) { var $$ = this, withTransitionForAxis = !$$.hasArcType(), options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis }; options.withTransitionForTransform = false; $$.transiting = false; $$.setTargetType(targetIds, type); $$.updateTargets($$.data.targets); // this is needed when transforming to arc $$.updateAndRedraw(options); }; Chart.prototype.x = function(x) { var $$ = this.internal; if (arguments.length) { $$.updateTargetX($$.data.targets, x); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs }; Chart.prototype.xs = function(xs) { var $$ = this.internal; if (arguments.length) { $$.updateTargetXs($$.data.targets, xs); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs }; Chart.prototype.zoom = function(domain) { var $$ = this.internal; if (domain) { if ($$.isTimeSeries()) { domain = domain.map(function(x) { return $$.parseDate(x) }); } if ($$.config.subchart_show) { $$.brush.selectionAsValue(domain, true); } else { $$.updateXDomain(null, true, false, false, domain); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); return domain } else { return $$.x.domain() } }; Chart.prototype.zoom.enable = function(enabled) { var $$ = this.internal; $$.config.zoom_enabled = enabled; $$.updateAndRedraw(); }; Chart.prototype.unzoom = function() { var $$ = this.internal; if ($$.config.subchart_show) { $$.brush.clear(); } else { $$.updateXDomain(null, true, false, false, $$.subX.domain()); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } }; Chart.prototype.zoom.max = function(max) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (max === 0 || max) { config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); } else { return config.zoom_x_max } }; Chart.prototype.zoom.min = function(min) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (min === 0 || min) { config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); } else { return config.zoom_x_min } }; Chart.prototype.zoom.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.domain.max(range.max); } if (isDefined(range.min)) { this.domain.min(range.min); } } else { return { max: this.domain.max(), min: this.domain.min() } } }; ChartInternal.prototype.initPie = function() { var $$ = this, d3 = $$.d3; $$.pie = d3 .pie() .padAngle(this.getPadAngle.bind(this)) .value(function(d) { return d.values.reduce(function(a, b) { return a + b.value }, 0) }); let orderFct = $$.getOrderFunction(); // we need to reverse the returned order if asc or desc to have the slice in expected order. if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) { let defaultSort = orderFct; orderFct = (t1, t2) => defaultSort(t1, t2) * -1; } $$.pie.sort(orderFct || null); }; ChartInternal.prototype.updateRadius = function() { var $$ = this, config = $$.config, w = config.gauge_width || config.donut_width, gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth; $$.radiusExpanded = (Math.min($$.arcWidth, $$.arcHeight) / 2) * ($$.hasType('gauge') ? 0.85 : 1); $$.radius = $$.radiusExpanded * 0.95; $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius; }; ChartInternal.prototype.getPadAngle = function() { if (this.hasType('pie')) { return this.config.pie_padAngle || 0 } else if (this.hasType('donut')) { return this.config.donut_padAngle || 0 } else { return 0 } }; ChartInternal.prototype.updateArc = function() { var $$ = this; $$.svgArc = $$.getSvgArc(); $$.svgArcExpanded = $$.getSvgArcExpanded(); $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); }; ChartInternal.prototype.updateAngle = function(d) { var $$ = this, config = $$.config, found = false, index = 0, gMin, gMax, gTic, gValue; if (!config) { return null } $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function(t) { if (!found && t.data.id === d.data.id) { found = true; d = t; d.index = index; } index++; }); if (isNaN(d.startAngle)) { d.startAngle = 0; } if (isNaN(d.endAngle)) { d.endAngle = d.startAngle; } if ($$.isGaugeType(d.data)) { gMin = config.gauge_min; gMax = config.gauge_max; gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin); gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin; d.startAngle = config.gauge_startingAngle; d.endAngle = d.startAngle + gTic * gValue; } return found ? d : null }; ChartInternal.prototype.getSvgArc = function() { var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }), newArc = function(d, withoutUpdate) { var updated; if (withoutUpdate) { return arc(d) } // for interpolate updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0' }; // TODO: extends all function newArc.centroid = arc.centroid; return newArc }; ChartInternal.prototype.getSvgArcExpanded = function(rate) { rate = rate || 1; var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, expandWidth = Math.min( $$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100 ), arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }); return function(d) { var updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0' } }; ChartInternal.prototype.getArc = function(d, withoutUpdate, force) { return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : 'M 0 0' }; ChartInternal.prototype.transformForArcLabel = function(d) { var $$ = this, config = $$.config, updated = $$.updateAngle(d), c, x, y, h, ratio, translate = '', hasGauge = $$.hasType('gauge'); if (updated && !hasGauge) { c = this.svgArc.centroid(updated); x = isNaN(c[0]) ? 0 : c[0]; y = isNaN(c[1]) ? 0 : c[1]; h = Math.sqrt(x * x + y * y); if ($$.hasType('donut') && config.donut_label_ratio) { ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; } else if ($$.hasType('pie') && config.pie_label_ratio) { ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; } else { ratio = $$.radius && h ? ((36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius) / h : 0; } translate = 'translate(' + x * ratio + ',' + y * ratio + ')'; } else if ( updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1 ) { var y1 = Math.sin(updated.endAngle - Math.PI / 2); x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25); y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3; translate = 'translate(' + x + ',' + y + ')'; } return translate }; /** * @deprecated Use `getRatio('arc', d)` instead. */ ChartInternal.prototype.getArcRatio = function(d) { return this.getRatio('arc', d) }; ChartInternal.prototype.convertToArcData = function(d) { return this.addName({ id: d.data.id, value: d.value, ratio: this.getRatio('arc', d), index: d.index }) }; ChartInternal.prototype.textForArcLabel = function(d) { var $$ = this, updated, value, ratio, id, format; if (!$$.shouldShowArcLabel()) { return '' } updated = $$.updateAngle(d); value = updated ? updated.value : null; ratio = $$.getRatio('arc', updated); id = d.data.id; if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { return '' } format = $$.getArcLabelFormat(); return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio) }; ChartInternal.prototype.textForGaugeMinMax = function(value, isMax) { var $$ = this, format = $$.getGaugeLabelExtents(); return format ? format(value, isMax) : value }; ChartInternal.prototype.expandArc = function(targetIds) { var $$ = this, interval; // MEMO: avoid to cancel transition if ($$.transiting) { interval = window.setInterval(function() { if (!$$.transiting) { window.clearInterval(interval); if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { $$.expandArc(targetIds); } } }, 10); return } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .each(function(d) { if (!$$.shouldExpand(d.data.id)) { return } $$.d3 .select(this) .selectAll('path') .transition() .duration($$.expandDuration(d.data.id)) .attr('d', $$.svgArcExpanded) .transition() .duration($$.expandDuration(d.data.id) * 2) .attr('d', $$.svgArcExpandedSub) .each(function(d) { if ($$.isDonutType(d.data)) ; }); }); }; ChartInternal.prototype.unexpandArc = function(targetIds) { var $$ = this; if ($$.transiting) { return } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .selectAll('path') .transition() .duration(function(d) { return $$.expandDuration(d.data.id) }) .attr('d', $$.svgArc); $$.svg.selectAll('.' + CLASS.arc); }; ChartInternal.prototype.expandDuration = function(id) { var $$ = this, config = $$.config; if ($$.isDonutType(id)) { return config.donut_expand_duration } else if ($$.isGaugeType(id)) { return config.gauge_expand_duration } else if ($$.isPieType(id)) { return config.pie_expand_duration } else { return 50 } }; ChartInternal.prototype.shouldExpand = function(id) { var $$ = this, config = $$.config; return ( ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand) ) }; ChartInternal.prototype.shouldShowArcLabel = function() { var $$ = this, config = $$.config, shouldShow = true; if ($$.hasType('donut')) { shouldShow = config.donut_label_show; } else if ($$.hasType('pie')) { shouldShow = config.pie_label_show; } // when gauge, always true return shouldShow }; ChartInternal.prototype.meetsArcLabelThreshold = function(ratio) { var $$ = this, config = $$.config, threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; return ratio >= threshold }; ChartInternal.prototype.getArcLabelFormat = function() { var $$ = this, config = $$.config, format = config.pie_label_format; if ($$.hasType('gauge')) { format = config.gauge_label_format; } else if ($$.hasType('donut')) { format = config.donut_label_format; } return format }; ChartInternal.prototype.getGaugeLabelExtents = function() { var $$ = this, config = $$.config; return config.gauge_label_extents }; ChartInternal.prototype.getArcTitle = function() { var $$ = this; return $$.hasType('donut') ? $$.config.donut_title : '' }; ChartInternal.prototype.updateTargetsForArc = function(targets) { var $$ = this, main = $$.main, mainPies, mainPieEnter, classChartArc = $$.classChartArc.bind($$), classArcs = $$.classArcs.bind($$), classFocus = $$.classFocus.bind($$); mainPies = main .select('.' + CLASS.chartArcs) .selectAll('.' + CLASS.chartArc) .data($$.pie(targets)) .attr('class', function(d) { return classChartArc(d) + classFocus(d.data) }); mainPieEnter = mainPies .enter() .append('g') .attr('class', classChartArc); mainPieEnter.append('g').attr('class', classArcs); mainPieEnter .append('text') .attr('dy', $$.hasType('gauge') ? '-.1em' : '.35em') .style('opacity', 0) .style('text-anchor', 'middle') .style('pointer-events', 'none'); // MEMO: can not keep same color..., but not bad to update color in redraw //mainPieUpdate.exit().remove(); }; ChartInternal.prototype.initArc = function() { var $$ = this; $$.arcs = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); $$.arcs .append('text') .attr('class', CLASS.chartArcsTitle) .style('text-anchor', 'middle') .text($$.getArcTitle()); }; ChartInternal.prototype.redrawArc = function( duration, durationForExit, withTransform ) { var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, arcs, mainArc, arcLabelLines, mainArcLabelLine, hasGaugeType = $$.hasType('gauge'); arcs = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arc) .data($$.arcData.bind($$)); mainArc = arcs .enter() .append('path') .attr('class', $$.classArc.bind($$)) .style('fill', function(d) { return $$.color(d.data) }) .style('cursor', function(d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? 'pointer' : null }) .each(function(d) { if ($$.isGaugeType(d.data)) { d.startAngle = d.endAngle = config.gauge_startingAngle; } this._current = d; }) .merge(arcs); if (hasGaugeType) { arcLabelLines = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arcLabelLine) .data($$.arcData.bind($$)); mainArcLabelLine = arcLabelLines .enter() .append('rect') .attr('class', function(d) { return ( CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id ) }) .merge(arcLabelLines); if ($$.filterTargetsToShow($$.data.targets).length === 1) { mainArcLabelLine.style('display', 'none'); } else { mainArcLabelLine .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data) }) .style('display', config.gauge_labelLine_show ? '' : 'none') .each(function(d) { var lineLength = 0, lineThickness = 2, x = 0, y = 0, transform = ''; if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) { var updated = $$.updateAngle(d), innerLineLength = ($$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length) * (updated.index + 1), lineAngle = updated.endAngle - Math.PI / 2, arcInnerRadius = $$.radius - innerLineLength, linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius); lineLength = $$.radiusExpanded - $$.radius + innerLineLength; x = Math.cos(linePositioningAngle) * arcInnerRadius; y = Math.sin(linePositioningAngle) * arcInnerRadius; transform = 'rotate(' + (lineAngle * 180) / Math.PI + ', ' + x + ', ' + y + ')'; } d3.select(this) .attr('x', x) .attr('y', y) .attr('width', lineLength) .attr('height', lineThickness) .attr('transform', transform) .style( 'stroke-dasharray', '0, ' + (lineLength + lineThickness) + ', 0' ); }); } } mainArc .attr('transform', function(d) { return !$$.isGaugeType(d.data) && withTransform ? 'scale(0)' : '' }) .on( 'mouseover', config.interaction_enabled ? function(d) { var updated, arcData; if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.expandArc(updated.data.id); $$.api.focus(updated.data.id); $$.toggleFocusLegend(updated.data.id, true); $$.config.data_onmouseover(arcData, this); } } : null ) .on( 'mousemove', config.interaction_enabled ? function(d) { var updated = $$.updateAngle(d), arcData, selectedData; if (updated) { (arcData = $$.convertToArcData(updated)), (selectedData = [arcData]); $$.showTooltip(selectedData, this); } } : null ) .on( 'mouseout', config.interaction_enabled ? function(d) { var updated, arcData; if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.unexpandArc(updated.data.id); $$.api.revert(); $$.revertLegend(); $$.hideTooltip(); $$.config.data_onmouseout(arcData, this); } } : null ) .on( 'click', config.interaction_enabled ? function(d, i) { var updated = $$.updateAngle(d), arcData; if (updated) { arcData = $$.convertToArcData(updated); if ($$.toggleShape) { $$.toggleShape(this, arcData, i); } $$.config.data_onclick.call($$.api, arcData, this); } } : null ) .each(function() { $$.transiting = true; }) .transition() .duration(duration) .attrTween('d', function(d) { var updated = $$.updateAngle(d), interpolate; if (!updated) { return function() { return 'M 0 0' } } // if (this._current === d) { // this._current = { // startAngle: Math.PI*2, // endAngle: Math.PI*2, // }; // } if (isNaN(this._current.startAngle)) { this._current.startAngle = 0; } if (isNaN(this._current.endAngle)) { this._current.endAngle = this._current.startAngle; } interpolate = d3.interpolate(this._current, updated); this._current = interpolate(0); return function(t) { // prevents crashing the charts once in transition and chart.destroy() has been called if ($$.config === null) { return 'M 0 0' } var interpolated = interpolate(t); interpolated.data = d.data; // data.id will be updated by interporator return $$.getArc(interpolated, true) } }) .attr('transform', withTransform ? 'scale(1)' : '') .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data.id) }) // Where gauge reading color would receive customization. .call($$.endall, function() { $$.transiting = false; }); arcs .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); main .selectAll('.' + CLASS.chartArc) .select('text') .style('opacity', 0) .attr('class', function(d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : '' }) .text($$.textForArcLabel.bind($$)) .attr('transform', $$.transformForArcLabel.bind($$)) .style('font-size', function(d) { return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : '' }) .transition() .duration(duration) .style('opacity', function(d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0 }); main .select('.' + CLASS.chartArcsTitle) .style('opacity', $$.hasType('donut') || hasGaugeType ? 1 : 0); if (hasGaugeType) { let index = 0; const backgroundArc = $$.arcs .select('g.' + CLASS.chartArcsBackground) .selectAll('path.' + CLASS.chartArcsBackground) .data($$.data.targets); backgroundArc .enter() .append('path') .attr( 'class', (d, i) => CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i ) .merge(backgroundArc) .attr('d', d1 => { if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) { return 'M 0 0' } var d = { data: [{ value: config.gauge_max }], startAngle: config.gauge_startingAngle, endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1), index: index++ }; return $$.getArc(d, true, true) }); backgroundArc.exit().remove(); $$.arcs .select('.' + CLASS.chartArcsGaugeUnit) .attr('dy', '.75em') .text(config.gauge_label_show ? config.gauge_units : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMin) .attr( 'dx', -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : '' ); $$.arcs .select('.' + CLASS.chartArcsGaugeMax) .attr( 'dx', $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : '' ); } }; ChartInternal.prototype.initGauge = function() { var arcs = this.arcs; if (this.hasType('gauge')) { arcs.append('g').attr('class', CLASS.chartArcsBackground); arcs .append('text') .attr('class', CLASS.chartArcsGaugeUnit) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMin) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMax) .style('text-anchor', 'middle') .style('pointer-events', 'none'); } }; ChartInternal.prototype.getGaugeLabelHeight = function() { return this.config.gauge_label_show ? 20 : 0 }; /** * Store value into cache * * @param key * @param value */ ChartInternal.prototype.addToCache = function(key, value) { this.cache[`$${key}`] = value; }; /** * Returns a cached value or undefined * * @param key * @return {*} */ ChartInternal.prototype.getFromCache = function(key) { return this.cache[`$${key}`] }; /** * Reset cached data */ ChartInternal.prototype.resetCache = function() { Object.keys(this.cache) .filter(key => /^\$/.test(key)) .forEach(key => { delete this.cache[key]; }); }; // Old API that stores Targets ChartInternal.prototype.hasCaches = function(ids) { for (var i = 0; i < ids.length; i++) { if (!(ids[i] in this.cache)) { return false } } return true }; ChartInternal.prototype.addCache = function(id, target) { this.cache[id] = this.cloneTarget(target); }; ChartInternal.prototype.getCaches = function(ids) { var targets = [], i; for (i = 0; i < ids.length; i++) { if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); } } return targets }; ChartInternal.prototype.categoryName = function(i) { var config = this.config; return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i }; ChartInternal.prototype.generateTargetClass = function(targetId) { return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '' }; ChartInternal.prototype.generateClass = function(prefix, targetId) { return ' ' + prefix + ' ' + prefix + this.generateTargetClass(targetId) }; ChartInternal.prototype.classText = function(d) { return this.generateClass(CLASS.text, d.index) }; ChartInternal.prototype.classTexts = function(d) { return this.generateClass(CLASS.texts, d.id) }; ChartInternal.prototype.classShape = function(d) { return this.generateClass(CLASS.shape, d.index) }; ChartInternal.prototype.classShapes = function(d) { return this.generateClass(CLASS.shapes, d.id) }; ChartInternal.prototype.classLine = function(d) { return this.classShape(d) + this.generateClass(CLASS.line, d.id) }; ChartInternal.prototype.classLines = function(d) { return this.classShapes(d) + this.generateClass(CLASS.lines, d.id) }; ChartInternal.prototype.classCircle = function(d) { return this.classShape(d) + this.generateClass(CLASS.circle, d.index) }; ChartInternal.prototype.classCircles = function(d) { return this.classShapes(d) + this.generateClass(CLASS.circles, d.id) }; ChartInternal.prototype.classBar = function(d) { return this.classShape(d) + this.generateClass(CLASS.bar, d.index) }; ChartInternal.prototype.classBars = function(d) { return this.classShapes(d) + this.generateClass(CLASS.bars, d.id) }; ChartInternal.prototype.classArc = function(d) { return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id) }; ChartInternal.prototype.classArcs = function(d) { return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id) }; ChartInternal.prototype.classArea = function(d) { return this.classShape(d) + this.generateClass(CLASS.area, d.id) }; ChartInternal.prototype.classAreas = function(d) { return this.classShapes(d) + this.generateClass(CLASS.areas, d.id) }; ChartInternal.prototype.classRegion = function(d, i) { return ( this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '') ) }; ChartInternal.prototype.classEvent = function(d) { return this.generateClass(CLASS.eventRect, d.index) }; ChartInternal.prototype.classTarget = function(id) { var $$ = this; var additionalClassSuffix = $$.config.data_classes[id], additionalClass = ''; if (additionalClassSuffix) { additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; } return $$.generateClass(CLASS.target, id) + additionalClass }; ChartInternal.prototype.classFocus = function(d) { return this.classFocused(d) + this.classDefocused(d) }; ChartInternal.prototype.classFocused = function(d) { return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '') }; ChartInternal.prototype.classDefocused = function(d) { return ( ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '') ) }; ChartInternal.prototype.classChartText = function(d) { return CLASS.chartText + this.classTarget(d.id) }; ChartInternal.prototype.classChartLine = function(d) { return CLASS.chartLine + this.classTarget(d.id) }; ChartInternal.prototype.classChartBar = function(d) { return CLASS.chartBar + this.classTarget(d.id) }; ChartInternal.prototype.classChartArc = function(d) { return CLASS.chartArc + this.classTarget(d.data.id) }; ChartInternal.prototype.getTargetSelectorSuffix = function(targetId) { const targetClass = this.generateTargetClass(targetId); if (window.CSS && window.CSS.escape) { return window.CSS.escape(targetClass) } // fallback on imperfect method for old browsers (does not handles unicode) return targetClass.replace(/([?!@#$%^&*()=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1') }; ChartInternal.prototype.selectorTarget = function(id, prefix) { return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id) }; ChartInternal.prototype.selectorTargets = function(ids, prefix) { var $$ = this; ids = ids || []; return ids.length ? ids.map(function(id) { return $$.selectorTarget(id, prefix) }) : null }; ChartInternal.prototype.selectorLegend = function(id) { return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id) }; ChartInternal.prototype.selectorLegends = function(ids) { var $$ = this; return ids && ids.length ? ids.map(function(id) { return $$.selectorLegend(id) }) : null }; ChartInternal.prototype.getClipPath = function(id) { return 'url(' + (isIE(9) ? '' : document.URL.split('#')[0]) + '#' + id + ')' }; ChartInternal.prototype.appendClip = function(parent, id) { return parent .append('clipPath') .attr('id', id) .append('rect') }; ChartInternal.prototype.getAxisClipX = function(forHorizontal) { // axis line width + padding for left var left = Math.max(30, this.margin.left); return forHorizontal ? -(1 + left) : -(left - 1) }; ChartInternal.prototype.getAxisClipY = function(forHorizontal) { return forHorizontal ? -20 : -this.margin.top }; ChartInternal.prototype.getXAxisClipX = function() { var $$ = this; return $$.getAxisClipX(!$$.config.axis_rotated) }; ChartInternal.prototype.getXAxisClipY = function() { var $$ = this; return $$.getAxisClipY(!$$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipX = function() { var $$ = this; return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipY = function() { var $$ = this; return $$.getAxisClipY($$.config.axis_rotated) }; ChartInternal.prototype.getAxisClipWidth = function(forHorizontal) { var $$ = this, left = Math.max(30, $$.margin.left), right = Math.max(30, $$.margin.right); // width + axis line width + padding for left/right return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20 }; ChartInternal.prototype.getAxisClipHeight = function(forHorizontal) { // less than 20 is not enough to show the axis label 'outer' without legend return ( (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20 ) }; ChartInternal.prototype.getXAxisClipWidth = function() { var $$ = this; return $$.getAxisClipWidth(!$$.config.axis_rotated) }; ChartInternal.prototype.getXAxisClipHeight = function() { var $$ = this; return $$.getAxisClipHeight(!$$.config.axis_rotated) }; ChartInternal.prototype.getYAxisClipWidth = function() { var $$ = this; return ( $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0) ) }; ChartInternal.prototype.getYAxisClipHeight = function() { var $$ = this; return $$.getAxisClipHeight($$.config.axis_rotated) }; ChartInternal.prototype.generateColor = function() { var $$ = this, config = $$.config, d3 = $$.d3, colors = config.data_colors, pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10, callback = config.data_color, ids = []; return function(d) { var id = d.id || (d.data && d.data.id) || d, color; // if callback function is provided if (colors[id] instanceof Function) { color = colors[id](d); } // if specified, choose that color else if (colors[id]) { color = colors[id]; } // if not specified, choose from pattern else { if (ids.indexOf(id) < 0) { ids.push(id); } color = pattern[ids.indexOf(id) % pattern.length]; colors[id] = color; } return callback instanceof Function ? callback(color, d) : color } }; ChartInternal.prototype.generateLevelColor = function() { var $$ = this, config = $$.config, colors = config.color_pattern, threshold = config.color_threshold, asValue = threshold.unit === 'value', values = threshold.values && threshold.values.length ? threshold.values : [], max = threshold.max || 100; return notEmpty(threshold) && notEmpty(colors) ? function(value) { var i, v, color = colors[colors.length - 1]; for (i = 0; i < values.length; i++) { v = asValue ? value : (value * 100) / max; if (v < values[i]) { color = colors[i]; break } } return color } : null }; ChartInternal.prototype.getDefaultConfig = function() { var config = { bindto: '#chart', svg_classname: undefined, size_width: undefined, size_height: undefined, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, resize_auto: true, zoom_enabled: false, zoom_initialRange: undefined, zoom_type: 'scroll', zoom_disableDefaultBehavior: false, zoom_privileged: false, zoom_rescale: false, zoom_onzoom: function() {}, zoom_onzoomstart: function() {}, zoom_onzoomend: function() {}, zoom_x_min: undefined, zoom_x_max: undefined, interaction_brighten: true, interaction_enabled: true, onmouseover: function() {}, onmouseout: function() {}, onresize: function() {}, onresized: function() {}, oninit: function() {}, onrendered: function() {}, transition_duration: 350, data_epochs: 'epochs', data_x: undefined, data_xs: {}, data_xFormat: '%Y-%m-%d', data_xLocaltime: true, data_xSort: true, data_idConverter: function(id) { return id }, data_names: {}, data_classes: {}, data_groups: [], data_axes: {}, data_type: undefined, data_types: {}, data_labels: {}, data_order: 'desc', data_regions: {}, data_color: undefined, data_colors: {}, data_hide: false, data_filter: undefined, data_selection_enabled: false, data_selection_grouped: false, data_selection_isselectable: function() { return true }, data_selection_multiple: true, data_selection_draggable: false, data_stack_normalize: false, data_onclick: function() {}, data_onmouseover: function() {}, data_onmouseout: function() {}, data_onselected: function() {}, data_onunselected: function() {}, data_url: undefined, data_headers: undefined, data_json: undefined, data_rows: undefined, data_columns: undefined, data_mimeType: undefined, data_keys: undefined, // configuration for no plot-able data supplied. data_empty_label_text: '', // subchart subchart_show: false, subchart_size_height: 60, subchart_axis_x_show: true, subchart_onbrush: function() {}, // color color_pattern: [], color_threshold: {}, // legend legend_show: true, legend_hide: false, legend_position: 'bottom', legend_inset_anchor: 'top-left', legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, legend_item_onclick: undefined, legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, axis_x_type: 'indexed', axis_x_localtime: true, axis_x_categories: [], axis_x_tick_centered: false, axis_x_tick_format: undefined, axis_x_tick_culling: {}, axis_x_tick_culling_max: 10, axis_x_tick_count: undefined, axis_x_tick_fit: true, axis_x_tick_values: null, axis_x_tick_rotate: 0, axis_x_tick_outer: true, axis_x_tick_multiline: true, axis_x_tick_multilineMax: 0, axis_x_tick_width: null, axis_x_max: undefined, axis_x_min: undefined, axis_x_padding: {}, axis_x_height: undefined, axis_x_selection: undefined, axis_x_label: {}, axis_x_inner: undefined, axis_y_show: true, axis_y_type: 'linear', axis_y_max: undefined, axis_y_min: undefined, axis_y_inverted: false, axis_y_center: undefined, axis_y_inner: undefined, axis_y_label: {}, axis_y_tick_format: undefined, axis_y_tick_outer: true, axis_y_tick_values: null, axis_y_tick_rotate: 0, axis_y_tick_count: undefined, axis_y_tick_time_type: undefined, axis_y_tick_time_interval: undefined, axis_y_padding: {}, axis_y_default: undefined, axis_y2_show: false, axis_y2_type: 'linear', axis_y2_max: undefined, axis_y2_min: undefined, axis_y2_inverted: false, axis_y2_center: undefined, axis_y2_inner: undefined, axis_y2_label: {}, axis_y2_tick_format: undefined, axis_y2_tick_outer: true, axis_y2_tick_values: null, axis_y2_tick_count: undefined, axis_y2_padding: {}, axis_y2_default: undefined, // grid grid_x_show: false, grid_x_type: 'tick', grid_x_lines: [], grid_y_show: false, // not used // grid_y_type: 'tick', grid_y_lines: [], grid_y_ticks: 10, grid_focus_show: true, grid_lines_front: true, // point - point of each data point_show: true, point_r: 2.5, point_sensitivity: 10, point_focus_expand_enabled: true, point_focus_expand_r: undefined, point_select_r: undefined, // line line_connectNull: false, line_step_type: 'step', // bar bar_width: undefined, bar_width_ratio: 0.6, bar_width_max: undefined, bar_zerobased: true, bar_space: 0, // area area_zerobased: true, area_above: false, // pie pie_label_show: true, pie_label_format: undefined, pie_label_threshold: 0.05, pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, pie_padAngle: 0, // gauge gauge_fullCircle: false, gauge_label_show: true, gauge_labelLine_show: true, gauge_label_format: undefined, gauge_min: 0, gauge_max: 100, gauge_startingAngle: (-1 * Math.PI) / 2, gauge_label_extents: undefined, gauge_units: undefined, gauge_width: undefined, gauge_arcs_minWidth: 5, gauge_expand: {}, gauge_expand_duration: 50, // donut donut_label_show: true, donut_label_format: undefined, donut_label_threshold: 0.05, donut_label_ratio: undefined, donut_width: undefined, donut_title: '', donut_expand: {}, donut_expand_duration: 50, donut_padAngle: 0, // spline spline_interpolation_type: 'cardinal', // stanford stanford_lines: [], stanford_regions: [], stanford_texts: [], stanford_scaleMin: undefined, stanford_scaleMax: undefined, stanford_scaleWidth: undefined, stanford_scaleFormat: undefined, stanford_scaleValues: undefined, stanford_colors: undefined, stanford_padding: { top: 0, right: 0, bottom: 0, left: 0 }, // region - region to change style regions: [], // tooltip - show when mouseover on each data tooltip_show: true, tooltip_grouped: true, tooltip_order: undefined, tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, tooltip_horizontal: undefined, tooltip_position: undefined, tooltip_contents: function( d, defaultTitleFormat, defaultValueFormat, color ) { return this.getTooltipContent ? this.getTooltipContent( d, defaultTitleFormat, defaultValueFormat, color ) : '' }, tooltip_init_show: false, tooltip_init_x: 0, tooltip_init_position: { top: '0px', left: '50px' }, tooltip_onshow: function() {}, tooltip_onhide: function() {}, // title title_text: undefined, title_padding: { top: 0, right: 0, bottom: 0, left: 0 }, title_position: 'top-center' }; Object.keys(this.additionalConfig).forEach(function(key) { config[key] = this.additionalConfig[key]; }, this); return config }; ChartInternal.prototype.additionalConfig = {}; ChartInternal.prototype.loadConfig = function(config) { var this_config = this.config, target, keys, read; function find() { var key = keys.shift(); // console.log("key =>", key, ", target =>", target); if (key && target && typeof target === 'object' && key in target) { target = target[key]; return find() } else if (!key) { return target } else { return undefined } } Object.keys(this_config).forEach(function(key) { target = config; keys = key.split('_'); read = find(); // console.log("CONFIG : ", key, read); if (isDefined(read)) { this_config[key] = read; } }); }; ChartInternal.prototype.convertUrlToData = function( url, mimeType, headers, keys, done ) { var $$ = this, type = mimeType ? mimeType : 'csv', f, converter; if (type === 'json') { f = $$.d3.json; converter = $$.convertJsonToData; } else if (type === 'tsv') { f = $$.d3.tsv; converter = $$.convertXsvToData; } else { f = $$.d3.csv; converter = $$.convertXsvToData; } f(url, headers) .then(function(data) { done.call($$, converter.call($$, data, keys)); }) .catch(function(error) { throw error }); }; ChartInternal.prototype.convertXsvToData = function(xsv) { var keys = xsv.columns, rows = xsv; if (rows.length === 0) { return { keys, rows: [keys.reduce((row, key) => Object.assign(row, { [key]: null }), {})] } } else { // [].concat() is to convert result into a plain array otherwise // test is not happy because rows have properties. return { keys, rows: [].concat(xsv) } } }; ChartInternal.prototype.convertJsonToData = function(json, keys) { var $$ = this, new_rows = [], targetKeys, data; if (keys) { // when keys specified, json would be an array that includes objects if (keys.x) { targetKeys = keys.value.concat(keys.x); $$.config.data_x = keys.x; } else { targetKeys = keys.value; } new_rows.push(targetKeys); json.forEach(function(o) { var new_row = []; targetKeys.forEach(function(key) { // convert undefined to null because undefined data will be removed in convertDataToTargets() var v = $$.findValueInJson(o, key); if (isUndefined(v)) { v = null; } new_row.push(v); }); new_rows.push(new_row); }); data = $$.convertRowsToData(new_rows); } else { Object.keys(json).forEach(function(key) { new_rows.push([key].concat(json[key])); }); data = $$.convertColumnsToData(new_rows); } return data }; /** * Finds value from the given nested object by the given path. * If it's not found, then this returns undefined. * @param {Object} object the object * @param {string} path the path */ ChartInternal.prototype.findValueInJson = function(object, path) { if (path in object) { // If object has a key that contains . or [], return the key's value // instead of searching for an inner object. // See https://github.com/c3js/c3/issues/1691 for details. return object[path] } path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) path = path.replace(/^\./, ''); // strip a leading dot var pathArray = path.split('.'); for (var i = 0; i < pathArray.length; ++i) { var k = pathArray[i]; if (k in object) { object = object[k]; } else { return } } return object }; /** * Converts the rows to normalized data. * @param {any[][]} rows The row data * @return {Object} */ ChartInternal.prototype.convertRowsToData = rows => { const newRows = []; const keys = rows[0]; for (let i = 1; i < rows.length; i++) { const newRow = {}; for (let j = 0; j < rows[i].length; j++) { if (isUndefined(rows[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRow[keys[j]] = rows[i][j]; } newRows.push(newRow); } return { keys, rows: newRows } }; /** * Converts the columns to normalized data. * @param {any[][]} columns The column data * @return {Object} */ ChartInternal.prototype.convertColumnsToData = columns => { const newRows = []; const keys = []; for (let i = 0; i < columns.length; i++) { const key = columns[i][0]; for (let j = 1; j < columns[i].length; j++) { if (isUndefined(newRows[j - 1])) { newRows[j - 1] = {}; } if (isUndefined(columns[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRows[j - 1][key] = columns[i][j]; } keys.push(key); } return { keys, rows: newRows } }; /** * Converts the data format into the target format. * @param {!Object} data * @param {!Array} data.keys Ordered list of target IDs. * @param {!Array} data.rows Rows of data to convert. * @param {boolean} appendXs True to append to $$.data.xs, False to replace. * @return {!Array} */ ChartInternal.prototype.convertDataToTargets = function(data, appendXs) { var $$ = this, config = $$.config, targets, ids, xs, keys, epochs; // handles format where keys are not orderly provided if (isArray(data)) { keys = Object.keys(data[0]); } else { keys = data.keys; data = data.rows; } xs = keys.filter($$.isX, $$); if (!$$.isStanfordGraphType()) { ids = keys.filter($$.isNotX, $$); } else { epochs = keys.filter($$.isEpochs, $$); ids = keys.filter($$.isNotXAndNotEpochs, $$); if (xs.length !== 1 || epochs.length !== 1 || ids.length !== 1) { throw new Error( "You must define the 'x' key name and the 'epochs' for Stanford Diagrams" ) } } // save x for update data by load when custom x and c3.x API ids.forEach(function(id) { var xKey = $$.getXKey(id); if ($$.isCustomX() || $$.isTimeSeries()) { // if included in input data if (xs.indexOf(xKey) >= 0) { $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : [] ).concat( data .map(function(d) { return d[xKey] }) .filter(isValue) .map(function(rawX, i) { return $$.generateTargetX(rawX, id, i) }) ); } // if not included in input data, find from preloaded data of other id's x else if (config.data_x) { $$.data.xs[id] = $$.getOtherTargetXs(); } // if not included in input data, find from preloaded data else if (notEmpty(config.data_xs)) { $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); } // MEMO: if no x included, use same x of current will be used } else { $$.data.xs[id] = data.map(function(d, i) { return i }); } }); // check x is defined ids.forEach(function(id) { if (!$$.data.xs[id]) { throw new Error('x is not defined for id = "' + id + '".') } }); // convert to target targets = ids.map(function(id, index) { var convertedId = config.data_idConverter(id); return { id: convertedId, id_org: id, values: data .map(function(d, i) { var xKey = $$.getXKey(id), rawX = d[xKey], value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x, returnData; // use x as categories if custom x and categorized if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { if (index === 0 && i === 0) { config.axis_x_categories = []; } x = config.axis_x_categories.indexOf(rawX); if (x === -1) { x = config.axis_x_categories.length; config.axis_x_categories.push(rawX); } } else { x = $$.generateTargetX(rawX, id, i); } // mark as x = undefined if value is undefined and filter to remove after mapped if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { x = undefined; } returnData = { x: x, value: value, id: convertedId }; if ($$.isStanfordGraphType()) { returnData.epochs = d[epochs]; } return returnData }) .filter(function(v) { return isDefined(v.x) }) } }); // finish targets targets.forEach(function(t) { var i; // sort values by its x if (config.data_xSort) { t.values = t.values.sort(function(v1, v2) { var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, x2 = v2.x || v2.x === 0 ? v2.x : Infinity; return x1 - x2 }); } // indexing each value i = 0; t.values.forEach(function(v) { v.index = i++; }); // this needs to be sorted because its index and value.index is identical $$.data.xs[t.id].sort(function(v1, v2) { return v1 - v2 }); }); // cache information about values $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); // set target types if (config.data_type) { $$.setTargetType( $$.mapToIds(targets).filter(function(id) { return !(id in config.data_types) }), config.data_type ); } // cache as original id keyed targets.forEach(function(d) { $$.addCache(d.id_org, d); }); return targets }; ChartInternal.prototype.isEpochs = function(key) { var $$ = this, config = $$.config; return config.data_epochs && key === config.data_epochs }; ChartInternal.prototype.isX = function(key) { var $$ = this, config = $$.config; return ( (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key)) ) }; ChartInternal.prototype.isNotX = function(key) { return !this.isX(key) }; ChartInternal.prototype.isNotXAndNotEpochs = function(key) { return !this.isX(key) && !this.isEpochs(key) }; /** * Returns whether the normalized stack option is enabled or not. * * To be enabled it must also have data.groups defined. * * @return {boolean} */ ChartInternal.prototype.isStackNormalized = function() { return this.config.data_stack_normalize && this.config.data_groups.length > 0 }; /** * Returns whether the axis is normalized or not. * * An axis is normalized as long as one of its associated target * is normalized. * * @param axisId Axis ID (y or y2) * @return {Boolean} */ ChartInternal.prototype.isAxisNormalized = function(axisId) { const $$ = this; if (!$$.isStackNormalized()) { // shortcut return false } return $$.data.targets .filter(target => $$.axis.getId(target.id) === axisId) .some(target => $$.isTargetNormalized(target.id)) }; /** * Returns whether the values for this target ID is normalized or not. * * To be normalized the option needs to be enabled and target needs * to be defined in `data.groups`. * * @param targetId ID of the target * @return {Boolean} True if the target is normalized, false otherwise. */ ChartInternal.prototype.isTargetNormalized = function(targetId) { const $$ = this; return ( $$.isStackNormalized() && $$.config.data_groups.some(group => group.includes(targetId)) ) }; ChartInternal.prototype.getXKey = function(id) { var $$ = this, config = $$.config; return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null }; /** * Get sum of visible data per index for given axis. * * Expect axisId to be either 'y' or 'y2'. * * @private * @param axisId Compute sum for data associated to given axis. * @return {Array} */ ChartInternal.prototype.getTotalPerIndex = function(axisId) { const $$ = this; if (!$$.isStackNormalized()) { return null } const cached = $$.getFromCache('getTotalPerIndex'); if (cached !== undefined) { return cached[axisId] } const sum = { y: [], y2: [] }; $$.data.targets // keep only target that are normalized .filter(target => $$.isTargetNormalized(target.id)) // keep only target that are visible .filter(target => $$.isTargetToShow(target.id)) // compute sum per axis .forEach(target => { const sumByAxis = sum[$$.axis.getId(target.id)]; target.values.forEach((v, i) => { if (!sumByAxis[i]) { sumByAxis[i] = 0; } sumByAxis[i] += isNumber(v.value) ? v.value : 0; }); }); $$.addToCache('getTotalPerIndex', sum); return sum[axisId] }; /** * Get sum of visible data. * * Should be used for normalised data only since all values * are expected to be positive. * * @private * @return {Number} */ ChartInternal.prototype.getTotalDataSum = function() { const $$ = this; const cached = $$.getFromCache('getTotalDataSum'); if (cached !== undefined) { return cached } const totalDataSum = flattenArray( $$.data.targets .filter(target => $$.isTargetToShow(target.id)) .map(target => target.values) ) .map(d => d.value) .reduce((p, c) => p + c, 0); $$.addToCache('getTotalDataSum', totalDataSum); return totalDataSum }; ChartInternal.prototype.getXValuesOfXKey = function(key, targets) { var $$ = this, xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; ids.forEach(function(id) { if ($$.getXKey(id) === key) { xValues = $$.data.xs[id]; } }); return xValues }; ChartInternal.prototype.getXValue = function(id, i) { var $$ = this; return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i }; ChartInternal.prototype.getOtherTargetXs = function() { var $$ = this, idsForX = Object.keys($$.data.xs); return idsForX.length ? $$.data.xs[idsForX[0]] : null }; ChartInternal.prototype.getOtherTargetX = function(index) { var xs = this.getOtherTargetXs(); return xs && index < xs.length ? xs[index] : null }; ChartInternal.prototype.addXs = function(xs) { var $$ = this; Object.keys(xs).forEach(function(id) { $$.config.data_xs[id] = xs[id]; }); }; ChartInternal.prototype.addName = function(data) { var $$ = this, name; if (data) { name = $$.config.data_names[data.id]; data.name = name !== undefined ? name : data.id; } return data }; ChartInternal.prototype.getValueOnIndex = function(values, index) { var valueOnIndex = values.filter(function(v) { return v.index === index }); return valueOnIndex.length ? valueOnIndex[0] : null }; ChartInternal.prototype.updateTargetX = function(targets, x) { var $$ = this; targets.forEach(function(t) { t.values.forEach(function(v, i) { v.x = $$.generateTargetX(x[i], t.id, i); }); $$.data.xs[t.id] = x; }); }; ChartInternal.prototype.updateTargetXs = function(targets, xs) { var $$ = this; targets.forEach(function(t) { if (xs[t.id]) { $$.updateTargetX([t], xs[t.id]); } }); }; ChartInternal.prototype.generateTargetX = function(rawX, id, index) { var $$ = this, x; if ($$.isTimeSeries()) { x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); } else if ($$.isCustomX() && !$$.isCategorized()) { x = isValue(rawX) ? +rawX : $$.getXValue(id, index); } else { x = index; } return x }; ChartInternal.prototype.cloneTarget = function(target) { return { id: target.id, id_org: target.id_org, values: target.values.map(function(d) { return { x: d.x, value: d.value, id: d.id } }) } }; ChartInternal.prototype.getMaxDataCount = function() { var $$ = this; return $$.d3.max($$.data.targets, function(t) { return t.values.length }) }; ChartInternal.prototype.mapToIds = function(targets) { return targets.map(function(d) { return d.id }) }; ChartInternal.prototype.mapToTargetIds = function(ids) { var $$ = this; return ids ? [].concat(ids) : $$.mapToIds($$.data.targets) }; ChartInternal.prototype.hasTarget = function(targets, id) { var ids = this.mapToIds(targets), i; for (i = 0; i < ids.length; i++) { if (ids[i] === id) { return true } } return false }; ChartInternal.prototype.isTargetToShow = function(targetId) { return this.hiddenTargetIds.indexOf(targetId) < 0 }; ChartInternal.prototype.isLegendToShow = function(targetId) { return this.hiddenLegendIds.indexOf(targetId) < 0 }; /** * Returns only visible targets. * * This is the same as calling {@link filterTargetsToShow} on $$.data.targets. * * @return {Array} */ ChartInternal.prototype.getTargetsToShow = function() { const $$ = this; return $$.filterTargetsToShow($$.data.targets) }; ChartInternal.prototype.filterTargetsToShow = function(targets) { var $$ = this; return targets.filter(function(t) { return $$.isTargetToShow(t.id) }) }; /** * @return {Array} Returns all the targets attached to the chart, visible or not */ ChartInternal.prototype.getTargets = function() { const $$ = this; return $$.data.targets }; ChartInternal.prototype.mapTargetsToUniqueXs = function(targets) { var $$ = this; var xs = $$.d3 .set( $$.d3.merge( targets.map(function(t) { return t.values.map(function(v) { return +v.x }) }) ) ) .values(); xs = $$.isTimeSeries() ? xs.map(function(x) { return new Date(+x) }) : xs.map(function(x) { return +x }); return xs.sort(function(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN }) }; ChartInternal.prototype.addHiddenTargetIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]); } } this.resetCache(); }; ChartInternal.prototype.removeHiddenTargetIds = function(targetIds) { this.hiddenTargetIds = this.hiddenTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); this.resetCache(); }; ChartInternal.prototype.addHiddenLegendIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]); } } }; ChartInternal.prototype.removeHiddenLegendIds = function(targetIds) { this.hiddenLegendIds = this.hiddenLegendIds.filter(function(id) { return targetIds.indexOf(id) < 0 }); }; ChartInternal.prototype.getValuesAsIdKeyed = function(targets) { var ys = {}; targets.forEach(function(t) { ys[t.id] = []; t.values.forEach(function(v) { ys[t.id].push(v.value); }); }); return ys }; ChartInternal.prototype.checkValueInTargets = function(targets, checker) { var ids = Object.keys(targets), i, j, values; for (i = 0; i < ids.length; i++) { values = targets[ids[i]].values; for (j = 0; j < values.length; j++) { if (checker(values[j].value)) { return true } } } return false }; ChartInternal.prototype.hasNegativeValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v < 0 }) }; ChartInternal.prototype.hasPositiveValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v > 0 }) }; ChartInternal.prototype.isOrderDesc = function() { var config = this.config; return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc' ) }; ChartInternal.prototype.isOrderAsc = function() { var config = this.config; return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc' ) }; ChartInternal.prototype.getOrderFunction = function() { var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc(); if (orderAsc || orderDesc) { var reducer = function(p, c) { return p + Math.abs(c.value) }; return function(t1, t2) { var t1Sum = t1.values.reduce(reducer, 0), t2Sum = t2.values.reduce(reducer, 0); return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum } } else if (isFunction(config.data_order)) { return config.data_order } else if (isArray(config.data_order)) { var order = config.data_order; return function(t1, t2) { return order.indexOf(t1.id) - order.indexOf(t2.id) } } }; ChartInternal.prototype.orderTargets = function(targets) { var fct = this.getOrderFunction(); if (fct) { targets.sort(fct); } return targets }; /** * Returns all the values from the given targets at the given index. * * @param {Array} targets * @param {Number} index * @return {Array} */ ChartInternal.prototype.filterByIndex = function(targets, index) { return this.d3.merge( targets.map(t => t.values.filter(v => v.index === index)) ) }; ChartInternal.prototype.filterByX = function(targets, x) { return this.d3 .merge( targets.map(function(t) { return t.values }) ) .filter(function(v) { return v.x - x === 0 }) }; ChartInternal.prototype.filterRemoveNull = function(data) { return data.filter(function(d) { return isValue(d.value) }) }; ChartInternal.prototype.filterByXDomain = function(targets, xDomain) { return targets.map(function(t) { return { id: t.id, id_org: t.id_org, values: t.values.filter(function(v) { return xDomain[0] <= v.x && v.x <= xDomain[1] }) } }) }; ChartInternal.prototype.hasDataLabel = function() { var config = this.config; if (typeof config.data_labels === 'boolean' && config.data_labels) { return true } else if ( typeof config.data_labels === 'object' && notEmpty(config.data_labels) ) { return true } return false }; ChartInternal.prototype.getDataLabelLength = function(min, max, key) { var $$ = this, lengths = [0, 0], paddingCoef = 1.3; $$.selectChart .select('svg') .selectAll('.dummy') .data([min, max]) .enter() .append('text') .text(function(d) { return $$.dataLabelFormat(d.id)(d) }) .each(function(d, i) { lengths[i] = getBBox(this)[key] * paddingCoef; }) .remove(); return lengths }; /** * Returns true if the given data point is not arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isNoneArc = function(d) { return this.hasTarget(this.data.targets, d.id) }; /** * Returns true if the given data point is arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isArc = function(d) { return 'data' in d && this.hasTarget(this.data.targets, d.data.id) }; /** * Find the closest point from the given pos among the given targets or * undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} pos An [x,y] coordinate * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargets = function(targets, pos) { const $$ = this; // for each target, find the closest point const candidates = targets .map(t => $$.findClosest( t.values, pos, $$.config.tooltip_horizontal ? $$.horizontalDistance.bind($$) : $$.dist.bind($$), $$.config.point_sensitivity ) ) .filter(v => v); // returns the closest of candidates if (candidates.length === 0) { return undefined } else if (candidates.length === 1) { return candidates[0] } else { return $$.findClosest(candidates, pos, $$.dist.bind($$)) } }; /** * Find the closest point from the x value or undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} x A value on X axis * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargetsByX = function(targets, x) { let closest; let diff; targets.forEach(t => { t.values.forEach(d => { let newDiff = Math.abs(x - d.x); if (diff === undefined || newDiff < diff) { closest = d; diff = newDiff; } }); }); return closest }; /** * Using given compute distance method, returns the closest data point from the * given position. * * Giving optionally a minimum distance to satisfy. * * @param {Array} dataPoints List of DataPoints * @param {Array} pos An [x,y] coordinate * @param {Function} computeDist Function to compute distance between 2 points * @param {Number} minDist Minimal distance to satisfy * @return {Object|undefined} Closest data point */ ChartInternal.prototype.findClosest = function( dataPoints, pos, computeDist, minDist = Infinity ) { const $$ = this; let closest; // find closest bar dataPoints .filter(v => v && $$.isBarType(v.id)) .forEach(function(v) { if (!closest) { const shape = $$.main .select( '.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index ) .node(); if ($$.isWithinBar(pos, shape)) { closest = v; } } }); // find closest point from non-bar dataPoints .filter(v => v && !$$.isBarType(v.id)) .forEach(v => { let d = computeDist(v, pos); if (d < minDist) { minDist = d; closest = v; } }); return closest }; ChartInternal.prototype.dist = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, yIndex = config.axis_rotated ? 0 : 1, y = $$.circleY(data, data.index), x = $$.x(data.x); return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)) }; ChartInternal.prototype.horizontalDistance = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, x = $$.x(data.x); return Math.abs(x - pos[xIndex]) }; ChartInternal.prototype.convertValuesToStep = function(values) { var converted = [].concat(values), i; if (!this.isCategorized()) { return values } for (i = values.length + 1; 0 < i; i--) { converted[i] = converted[i - 1]; } converted[0] = { x: converted[0].x - 1, value: converted[0].value, id: converted[0].id }; converted[values.length + 1] = { x: converted[values.length].x + 1, value: converted[values.length].value, id: converted[values.length].id }; return converted }; /** * Get ratio value * * @param {String} type Ratio for given type * @param {Object} d Data value object * @param {Boolean} asPercent Convert the return as percent or not * @return {Number} Ratio value * @private */ ChartInternal.prototype.getRatio = function(type, d, asPercent = false) { const $$ = this; const api = $$.api; let ratio = 0; if (d && api.data.shown.call(api).length) { ratio = d.ratio || d.value; if (type === 'arc') { if ($$.hasType('gauge')) { ratio = (d.endAngle - d.startAngle) / (Math.PI * ($$.config.gauge_fullCircle ? 2 : 1)); } else { const total = $$.getTotalDataSum(); ratio = d.value / total; } } else if (type === 'index') { const total = $$.getTotalPerIndex($$.axis.getId(d.id)); d.ratio = isNumber(d.value) && total && total[d.index] > 0 ? d.value / total[d.index] : 0; ratio = d.ratio; } } return asPercent && ratio ? ratio * 100 : ratio }; ChartInternal.prototype.updateDataAttributes = function(name, attrs) { var $$ = this, config = $$.config, current = config['data_' + name]; if (typeof attrs === 'undefined') { return current } Object.keys(attrs).forEach(function(id) { current[id] = attrs[id]; }); $$.redraw({ withLegend: true }); return current }; ChartInternal.prototype.load = function(targets, args) { var $$ = this; if (targets) { // filter loading targets if needed if (args.filter) { targets = targets.filter(args.filter); } // set type if args.types || args.type specified if (args.type || args.types) { targets.forEach(function(t) { var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; $$.setTargetType(t.id, type); }); } // Update/Add data $$.data.targets.forEach(function(d) { for (var i = 0; i < targets.length; i++) { if (d.id === targets[i].id) { d.values = targets[i].values; targets.splice(i, 1); break } } }); $$.data.targets = $$.data.targets.concat(targets); // add remained } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }; ChartInternal.prototype.loadFromArgs = function(args) { var $$ = this; $$.resetCache(); if (args.data) { $$.load($$.convertDataToTargets(args.data), args); } else if (args.url) { $$.convertUrlToData( args.url, args.mimeType, args.headers, args.keys, function(data) { $$.load($$.convertDataToTargets(data), args); } ); } else if (args.json) { $$.load( $$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args ); } else if (args.rows) { $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); } else if (args.columns) { $$.load( $$.convertDataToTargets($$.convertColumnsToData(args.columns)), args ); } else { $$.load(null, args); } }; ChartInternal.prototype.unload = function(targetIds, done) { var $$ = this; $$.resetCache(); if (!done) { done = function() {}; } // filter existing target targetIds = targetIds.filter(function(id) { return $$.hasTarget($$.data.targets, id) }); // If no target, call done and return if (!targetIds || targetIds.length === 0) { done(); return } $$.svg .selectAll( targetIds.map(function(id) { return $$.selectorTarget(id) }) ) .transition() .style('opacity', 0) .remove() .call($$.endall, done); targetIds.forEach(function(id) { // Reset fadein for future load $$.withoutFadeIn[id] = false; // Remove target's elements if ($$.legend) { $$.legend .selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)) .remove(); } // Remove target $$.data.targets = $$.data.targets.filter(function(t) { return t.id !== id }); }); }; ChartInternal.prototype.getYDomainMin = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasNegativeValue; if (config.data_groups.length > 0) { hasNegativeValue = $$.hasNegativeValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }); if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0]; // Consider negative values if (hasNegativeValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v < 0 ? v : 0; }); } // Compute min for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0) ) { ys[baseId][i] += +v; } }); } } } return $$.d3.min( Object.keys(ys).map(function(key) { return $$.d3.min(ys[key]) }) ) }; ChartInternal.prototype.getYDomainMax = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasPositiveValue; if (config.data_groups.length > 0) { hasPositiveValue = $$.hasPositiveValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }); if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0]; // Consider positive values if (hasPositiveValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v > 0 ? v : 0; }); } // Compute max for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0) ) { ys[baseId][i] += +v; } }); } } } return $$.d3.max( Object.keys(ys).map(function(key) { return $$.d3.max(ys[key]) }) ) }; ChartInternal.prototype.getYDomain = function(targets, axisId, xDomain) { var $$ = this, config = $$.config; if ($$.isAxisNormalized(axisId)) { return [0, 100] } var targetsByAxisId = targets.filter(function(t) { return $$.axis.getId(t.id) === axisId }), yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yDomainMin = $$.getYDomainMin(yTargets), yDomainMax = $$.getYDomainMax(yTargets), domain, domainLength, padding_top, padding_bottom, center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; // MEMO: avoid inverting domain unexpectedly yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin; yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax; if (yTargets.length === 0) { // use current domain if target of axisId is none return axisId === 'y2' ? $$.y2.domain() : $$.y.domain() } if (isNaN(yDomainMin)) { // set minimum to zero when not number yDomainMin = 0; } if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin yDomainMax = yDomainMin; } if (yDomainMin === yDomainMax) { yDomainMin < 0 ? (yDomainMax = 0) : (yDomainMin = 0); } isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; // Cancel zerobased if axis_*_min / axis_*_max specified if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { isZeroBased = false; } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { yDomainMin = 0; } if (isAllNegative) { yDomainMax = 0; } } domainLength = Math.abs(yDomainMax - yDomainMin); padding_top = padding_bottom = domainLength * 0.1; if (typeof center !== 'undefined') { yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); yDomainMax = center + yDomainAbs; yDomainMin = center - yDomainAbs; } // add padding for data label if (showHorizontalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); diff = diffDomain($$.y.range()); ratio = [lengths[0] / diff, lengths[1] / diff]; padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); } else if (showVerticalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); const pixelsToAxisPadding = $$.getY( config[`axis_${axisId}_type`], // input domain as pixels [0, config.axis_rotated ? $$.width : $$.height], // output range as axis padding [0, domainLength] ); padding_top += pixelsToAxisPadding(lengths[1]); padding_bottom += pixelsToAxisPadding(lengths[0]); } if (axisId === 'y' && notEmpty(config.axis_y_padding)) { padding_top = $$.axis.getPadding( config.axis_y_padding, 'top', padding_top, domainLength ); padding_bottom = $$.axis.getPadding( config.axis_y_padding, 'bottom', padding_bottom, domainLength ); } if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { padding_top = $$.axis.getPadding( config.axis_y2_padding, 'top', padding_top, domainLength ); padding_bottom = $$.axis.getPadding( config.axis_y2_padding, 'bottom', padding_bottom, domainLength ); } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { padding_bottom = yDomainMin; } if (isAllNegative) { padding_top = -yDomainMax; } } domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; return isInverted ? domain.reverse() : domain }; ChartInternal.prototype.getXDomainMin = function(targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function(t) { return $$.d3.min(t.values, function(v) { return v.x }) }) }; ChartInternal.prototype.getXDomainMax = function(targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function(t) { return $$.d3.max(t.values, function(v) { return v.x }) }) }; ChartInternal.prototype.getXDomainPadding = function(domain) { var $$ = this, config = $$.config, diff = domain[1] - domain[0], maxDataCount, padding, paddingLeft, paddingRight; if ($$.isCategorized()) { padding = 0; } else if ($$.hasType('bar')) { maxDataCount = $$.getMaxDataCount(); padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5; } else { padding = diff * 0.01; } if ( typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding) ) { paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; } else if (typeof config.axis_x_padding === 'number') { paddingLeft = paddingRight = config.axis_x_padding; } else { paddingLeft = paddingRight = padding; } return { left: paddingLeft, right: paddingRight } }; ChartInternal.prototype.getXDomain = function(targets) { var $$ = this, xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], firstX = xDomain[0], lastX = xDomain[1], padding = $$.getXDomainPadding(xDomain), min = 0, max = 0; // show center of x domain if min and max are the same if (firstX - lastX === 0 && !$$.isCategorized()) { if ($$.isTimeSeries()) { firstX = new Date(firstX.getTime() * 0.5); lastX = new Date(lastX.getTime() * 1.5); } else { firstX = firstX === 0 ? 1 : firstX * 0.5; lastX = lastX === 0 ? -1 : lastX * 1.5; } } if (firstX || firstX === 0) { min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; } if (lastX || lastX === 0) { max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; } return [min, max] }; ChartInternal.prototype.updateXDomain = function( targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain ) { var $$ = this, config = $$.config; if (withUpdateOrgXDomain) { $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); $$.orgXDomain = $$.x.domain(); if (config.zoom_enabled) { $$.zoom.update(); } $$.subX.domain($$.x.domain()); if ($$.brush) { $$.brush.updateScale($$.subX); } } if (withUpdateXDomain) { $$.x.domain( domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue() ); } // Trim domain when too big by zoom mousemove event if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); } return $$.x.domain() }; ChartInternal.prototype.trimXDomain = function(domain) { var zoomDomain = this.getZoomDomain(), min = zoomDomain[0], max = zoomDomain[1]; if (domain[0] <= min) { domain[1] = +domain[1] + (min - domain[0]); domain[0] = min; } if (max <= domain[1]) { domain[0] = +domain[0] - (domain[1] - max); domain[1] = max; } return domain }; ChartInternal.prototype.drag = function(mouse) { var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3; var sx, sy, mx, my, minX, maxX, minY, maxY; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable if (!config.data_selection_multiple) { return } // skip when single selection because drag is used for multiple selection sx = $$.dragStart[0]; sy = $$.dragStart[1]; mx = mouse[0]; my = mouse[1]; minX = Math.min(sx, mx); maxX = Math.max(sx, mx); minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my); maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my); main .select('.' + CLASS.dragarea) .attr('x', minX) .attr('y', minY) .attr('width', maxX - minX) .attr('height', maxY - minY); // TODO: binary search when multiple xs main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { if (!config.data_selection_isselectable(d)) { return } var shape = d3.select(this), isSelected = shape.classed(CLASS.SELECTED), isIncluded = shape.classed(CLASS.INCLUDED), _x, _y, _w, _h, toggle, isWithin = false, box; if (shape.classed(CLASS.circle)) { _x = shape.attr('cx') * 1; _y = shape.attr('cy') * 1; toggle = $$.togglePoint; isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; } else if (shape.classed(CLASS.bar)) { box = getPathBox(this); _x = box.x; _y = box.y; _w = box.width; _h = box.height; toggle = $$.togglePath; isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); } else { // line/area selection not supported yet return } if (isWithin ^ isIncluded) { shape.classed(CLASS.INCLUDED, !isIncluded); // TODO: included/unincluded callback here shape.classed(CLASS.SELECTED, !isSelected); toggle.call($$, !isSelected, shape, d, i); } }); }; ChartInternal.prototype.dragstart = function(mouse) { var $$ = this, config = $$.config; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.dragStart = mouse; $$.main .select('.' + CLASS.chart) .append('rect') .attr('class', CLASS.dragarea) .style('opacity', 0.1); $$.dragging = true; }; ChartInternal.prototype.dragend = function() { var $$ = this, config = $$.config; if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.main .select('.' + CLASS.dragarea) .transition() .duration(100) .style('opacity', 0) .remove(); $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false); $$.dragging = false; }; ChartInternal.prototype.getYFormat = function(forArc) { var $$ = this, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; return function(v, ratio, id) { var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; return format.call($$, v, ratio) } }; ChartInternal.prototype.yFormat = function(v) { var $$ = this, config = $$.config, format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; return format(v) }; ChartInternal.prototype.y2Format = function(v) { var $$ = this, config = $$.config, format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; return format(v) }; ChartInternal.prototype.defaultValueFormat = function(v) { return isValue(v) ? +v : '' }; ChartInternal.prototype.defaultArcValueFormat = function(v, ratio) { return (ratio * 100).toFixed(1) + '%' }; ChartInternal.prototype.dataLabelFormat = function(targetId) { var $$ = this, data_labels = $$.config.data_labels, format, defaultFormat = function(v) { return isValue(v) ? +v : '' }; // find format according to axis id if (typeof data_labels.format === 'function') { format = data_labels.format; } else if (typeof data_labels.format === 'object') { if (data_labels.format[targetId]) { format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; } else { format = function() { return '' }; } } else { format = defaultFormat; } return format }; ChartInternal.prototype.initGrid = function() { var $$ = this, config = $$.config, d3 = $$.d3; $$.grid = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid); if (config.grid_x_show) { $$.grid.append('g').attr('class', CLASS.xgrids); } if (config.grid_y_show) { $$.grid.append('g').attr('class', CLASS.ygrids); } if (config.grid_focus_show) { $$.grid .append('g') .attr('class', CLASS.xgridFocus) .append('line') .attr('class', CLASS.xgridFocus); } $$.xgrid = d3.selectAll([]); if (!config.grid_lines_front) { $$.initGridLines(); } }; ChartInternal.prototype.initGridLines = function() { var $$ = this, d3 = $$.d3; $$.gridLines = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid + ' ' + CLASS.gridLines); $$.gridLines.append('g').attr('class', CLASS.xgridLines); $$.gridLines.append('g').attr('class', CLASS.ygridLines); $$.xgridLines = d3.selectAll([]); }; ChartInternal.prototype.updateXGrid = function(withoutUpdate) { var $$ = this, config = $$.config, d3 = $$.d3, xgridData = $$.generateGridData(config.grid_x_type, $$.x), tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; $$.xgridAttr = config.axis_rotated ? { x1: 0, x2: $$.width, y1: function(d) { return $$.x(d) - tickOffset }, y2: function(d) { return $$.x(d) - tickOffset } } : { x1: function(d) { return $$.x(d) + tickOffset }, x2: function(d) { return $$.x(d) + tickOffset }, y1: 0, y2: $$.height }; $$.xgridAttr.opacity = function() { var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1'); return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1 }; var xgrid = $$.main .select('.' + CLASS.xgrids) .selectAll('.' + CLASS.xgrid) .data(xgridData); var xgridEnter = xgrid .enter() .append('line') .attr('class', CLASS.xgrid) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', 0); $$.xgrid = xgridEnter.merge(xgrid); if (!withoutUpdate) { $$.xgrid .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); } xgrid.exit().remove(); }; ChartInternal.prototype.updateYGrid = function() { var $$ = this, config = $$.config, gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); var ygrid = $$.main .select('.' + CLASS.ygrids) .selectAll('.' + CLASS.ygrid) .data(gridValues); var ygridEnter = ygrid .enter() .append('line') // TODO: x1, x2, y1, y2, opacity need to be set here maybe .attr('class', CLASS.ygrid); $$.ygrid = ygridEnter.merge(ygrid); $$.ygrid .attr('x1', config.axis_rotated ? $$.y : 0) .attr('x2', config.axis_rotated ? $$.y : $$.width) .attr('y1', config.axis_rotated ? 0 : $$.y) .attr('y2', config.axis_rotated ? $$.height : $$.y); ygrid.exit().remove(); $$.smoothLines($$.ygrid, 'grid'); }; ChartInternal.prototype.gridTextAnchor = function(d) { return d.position ? d.position : 'end' }; ChartInternal.prototype.gridTextDx = function(d) { return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4 }; ChartInternal.prototype.xGridTextX = function(d) { return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0 }; ChartInternal.prototype.yGridTextX = function(d) { return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width }; ChartInternal.prototype.updateGrid = function(duration) { var $$ = this, main = $$.main, config = $$.config, xgridLine, xgridLineEnter, ygridLine, ygridLineEnter, xv = $$.xv.bind($$), yv = $$.yv.bind($$), xGridTextX = $$.xGridTextX.bind($$), yGridTextX = $$.yGridTextX.bind($$); // hide if arc type $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); if (config.grid_x_show) { $$.updateXGrid(); } xgridLine = main .select('.' + CLASS.xgridLines) .selectAll('.' + CLASS.xgridLine) .data(config.grid_x_lines); // enter xgridLineEnter = xgridLine .enter() .append('g') .attr('class', function(d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '') }); xgridLineEnter .append('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 0); xgridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .attr('x', config.axis_rotated ? yGridTextX : xGridTextX) .attr('y', xv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // udpate $$.xgridLines = xgridLineEnter.merge(xgridLine); // done in d3.transition() of the end of this function // exit xgridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Y-Grid if (config.grid_y_show) { $$.updateYGrid(); } ygridLine = main .select('.' + CLASS.ygridLines) .selectAll('.' + CLASS.ygridLine) .data(config.grid_y_lines); // enter ygridLineEnter = ygridLine .enter() .append('g') .attr('class', function(d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '') }); ygridLineEnter .append('line') .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 0); ygridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .attr('x', config.axis_rotated ? xGridTextX : yGridTextX) .attr('y', yv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // update $$.ygridLines = ygridLineEnter.merge(ygridLine); $$.ygridLines .select('line') .transition() .duration(duration) .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 1); $$.ygridLines .select('text') .transition() .duration(duration) .attr( 'x', config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$) ) .attr('y', yv) .text(function(d) { return d.text }) .style('opacity', 1); // exit ygridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawGrid = function(withTransition, transition) { var $$ = this, config = $$.config, xv = $$.xv.bind($$), lines = $$.xgridLines.select('line'), texts = $$.xgridLines.select('text'); return [ (withTransition ? lines.transition(transition) : lines) .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 1), (withTransition ? texts.transition(transition) : texts) .attr( 'x', config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$) ) .attr('y', xv) .text(function(d) { return d.text }) .style('opacity', 1) ] }; ChartInternal.prototype.showXGridFocus = function(selectedData) { var $$ = this, config = $$.config, dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), xx = $$.xx.bind($$); if (!config.tooltip_show) { return } // Hide when stanford plot exists if ($$.hasType('stanford') || $$.hasArcType()) { return } focusEl .style('visibility', 'visible') .data([dataToShow[0]]) .attr(config.axis_rotated ? 'y1' : 'x1', xx) .attr(config.axis_rotated ? 'y2' : 'x2', xx); $$.smoothLines(focusEl, 'grid'); }; ChartInternal.prototype.hideXGridFocus = function() { this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); }; ChartInternal.prototype.updateXgridFocus = function() { var $$ = this, config = $$.config; $$.main .select('line.' + CLASS.xgridFocus) .attr('x1', config.axis_rotated ? 0 : -10) .attr('x2', config.axis_rotated ? $$.width : -10) .attr('y1', config.axis_rotated ? -10 : 0) .attr('y2', config.axis_rotated ? -10 : $$.height); }; ChartInternal.prototype.generateGridData = function(type, scale) { var $$ = this, gridData = [], xDomain, firstYear, lastYear, i, tickNum = $$.main .select('.' + CLASS.axisX) .selectAll('.tick') .size(); if (type === 'year') { xDomain = $$.getXDomain(); firstYear = xDomain[0].getFullYear(); lastYear = xDomain[1].getFullYear(); for (i = firstYear; i <= lastYear; i++) { gridData.push(new Date(i + '-01-01 00:00:00')); } } else { gridData = scale.ticks(10); if (gridData.length > tickNum) { // use only int gridData = gridData.filter(function(d) { return ('' + d).indexOf('.') < 0 }); } } return gridData }; ChartInternal.prototype.getGridFilterToRemove = function(params) { return params ? function(line) { var found = false ;[].concat(params).forEach(function(param) { if ( ('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class']) ) { found = true; } }); return found } : function() { return true } }; ChartInternal.prototype.removeGridLines = function(params, forX) { var $$ = this, config = $$.config, toRemove = $$.getGridFilterToRemove(params), toShow = function(line) { return !toRemove(line) }, classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; $$.main .select('.' + classLines) .selectAll('.' + classLine) .filter(toRemove) .transition() .duration(config.transition_duration) .style('opacity', 0) .remove(); if (forX) { config.grid_x_lines = config.grid_x_lines.filter(toShow); } else { config.grid_y_lines = config.grid_y_lines.filter(toShow); } }; ChartInternal.prototype.initEventRect = function() { var $$ = this, config = $$.config; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0); $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect); // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null); if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)); } } }; ChartInternal.prototype.redrawEventRect = function() { const $$ = this, d3 = $$.d3, config = $$.config; function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null); $$.hideXGridFocus(); $$.hideTooltip(); $$.unexpandCircles(); $$.unexpandBars(); } const isHoveringDataPoint = (mouse, closest) => closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity); const withName = d => (d ? $$.addName(Object.assign({}, d)) : null); // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style( 'cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null ); $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on( 'mouseout', config.interaction_enabled ? function() { if (!config) { return } // chart is destroyed if ($$.hasArcType()) { return } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } mouseout(); } : null ) .on( 'mousemove', config.interaction_enabled ? function() { // do nothing when dragging if ($$.dragging) { return } const targetsToShow = $$.getTargetsToShow(); // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this); const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ); const isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest); // ensure onmouseout is always called if mousemove switch between 2 targets if ( $$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index) ) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest); $$.mouseover = closest; } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null); // if tooltip not grouped, we want to display only data from closest data point const showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow); // find data to highlight let selectedData; if (showSingleDataPoint) { if (closest) { selectedData = [closest]; } } else { let closestByX; if (closest) { // reuse closest value closestByX = closest; } else { // try to find the closest value by X values from the mouse position const mouseX = config.axis_rotated ? mouse[1] : mouse[0]; closestByX = $$.findClosestFromTargetsByX( targetsToShow, $$.x.invert(mouseX) ); } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x); } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout() } // inject names for each point selectedData = selectedData.map(withName); // show tooltip $$.showTooltip(selectedData, this); // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles(); selectedData.forEach(function(d) { $$.expandCircles(d.index, d.id, false); }); } // expand bars $$.unexpandBars(); selectedData.forEach(function(d) { $$.expandBars(d.index, d.id, false); }); // Show xgrid focus line $$.showXGridFocus(selectedData); } : null ) .on( 'click', config.interaction_enabled ? function() { const targetsToShow = $$.getTargetsToShow(); if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this); const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ); if (!isHoveringDataPoint(mouse, closest)) { return } // select if selection enabled let sameXData; if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest]; } else { sameXData = $$.filterByX(targetsToShow, closest.x); } // toggle selected state sameXData.forEach(function(d) { $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id) ) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function() { if ( config.data_selection_grouped || $$.isWithinShape(this, d) ) { $$.toggleShape(this, d, d.index); } }); }); // call data_onclick on the closest data point if (closest) { const shape = $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id) ) .select('.' + CLASS.shape + '-' + closest.index); config.data_onclick.call($$.api, closest, shape.node()); } } : null ) .call( config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function() { $$.drag(d3.mouse(this)); }) .on('start', function() { $$.dragstart(d3.mouse(this)); }) .on('end', function() { $$.dragend(); }) : function() {} ); }; ChartInternal.prototype.getMousePosition = function(data) { var $$ = this; return [$$.x(data.x), $$.getYScale(data.id)(data.value)] }; ChartInternal.prototype.dispatchEvent = function(type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents'); event.initMouseEvent( type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null ); eventRect.dispatchEvent(event); }; ChartInternal.prototype.initLegend = function() { var $$ = this; $$.legendItemTextBox = {}; $$.legendHasRendered = false; $$.legend = $$.svg.append('g').attr('transform', $$.getTranslate('legend')); if (!$$.config.legend_show) { $$.legend.style('visibility', 'hidden'); $$.hiddenLegendIds = $$.mapToIds($$.data.targets); return } // MEMO: call here to update legend box and tranlate for all // MEMO: translate will be updated by this, so transform not needed in updateLegend() $$.updateLegendWithDefaults(); }; ChartInternal.prototype.updateLegendWithDefaults = function() { var $$ = this; $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }); }; ChartInternal.prototype.updateSizeForLegend = function( legendHeight, legendWidth ) { var $$ = this, config = $$.config, insetLegendPosition = { top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 }; $$.margin3 = { top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, right: NaN, bottom: 0, left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 }; }; ChartInternal.prototype.transformLegend = function(withTransition) { var $$ = this ;(withTransition ? $$.legend.transition() : $$.legend).attr( 'transform', $$.getTranslate('legend') ); }; ChartInternal.prototype.updateLegendStep = function(step) { this.legendStep = step; }; ChartInternal.prototype.updateLegendItemWidth = function(w) { this.legendItemWidth = w; }; ChartInternal.prototype.updateLegendItemHeight = function(h) { this.legendItemHeight = h; }; ChartInternal.prototype.getLegendWidth = function() { var $$ = this; return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0 }; ChartInternal.prototype.getLegendHeight = function() { var $$ = this, h = 0; if ($$.config.legend_show) { if ($$.isLegendRight) { h = $$.currentHeight; } else { h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); } } return h }; ChartInternal.prototype.opacityForLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 1 }; ChartInternal.prototype.opacityForUnfocusedLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3 }; ChartInternal.prototype.toggleFocusLegend = function(targetIds, focus) { var $$ = this; targetIds = $$.mapToTargetIds(targetIds); $$.legend .selectAll('.' + CLASS.legendItem) .filter(function(id) { return targetIds.indexOf(id) >= 0 }) .classed(CLASS.legendItemFocused, focus) .transition() .duration(100) .style('opacity', function() { var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; return opacity.call($$, $$.d3.select(this)) }); }; ChartInternal.prototype.revertLegend = function() { var $$ = this, d3 = $$.d3; $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemFocused, false) .transition() .duration(100) .style('opacity', function() { return $$.opacityForLegend(d3.select(this)) }); }; ChartInternal.prototype.showLegend = function(targetIds) { var $$ = this, config = $$.config; if (!config.legend_show) { config.legend_show = true; $$.legend.style('visibility', 'visible'); if (!$$.legendHasRendered) { $$.updateLegendWithDefaults(); } } $$.removeHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('visibility', 'visible') .transition() .style('opacity', function() { return $$.opacityForLegend($$.d3.select(this)) }); }; ChartInternal.prototype.hideLegend = function(targetIds) { var $$ = this, config = $$.config; if (config.legend_show && isEmpty(targetIds)) { config.legend_show = false; $$.legend.style('visibility', 'hidden'); } $$.addHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('opacity', 0) .style('visibility', 'hidden'); }; ChartInternal.prototype.clearLegendItemTextBoxCache = function() { this.legendItemTextBox = {}; }; ChartInternal.prototype.updateLegend = function( targetIds, options, transitions ) { var $$ = this, config = $$.config; var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; // Skip elements when their name is set to null targetIds = targetIds.filter(function(id) { return !isDefined(config.data_names[id]) || config.data_names[id] !== null }); options = options || {}; withTransition = getOption(options, 'withTransition', true); withTransitionForTransform = getOption( options, 'withTransitionForTransform', true ); function getTextBox(textElement, id) { if (!$$.legendItemTextBox[id]) { $$.legendItemTextBox[id] = $$.getTextRect( textElement.textContent, CLASS.legendItem, textElement ); } return $$.legendItemTextBox[id] } function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), margin, maxLength; // MEMO: care about condifion of step, totalLength function updateValues(id, withoutStep) { if (!withoutStep) { margin = (areaLength - totalLength - itemLength) / 2; if (margin < posMin) { margin = (areaLength - itemLength) / 2; totalLength = 0; step++; } } steps[id] = step; margins[step] = $$.isLegendInset ? 10 : margin; offsets[id] = totalLength; totalLength += itemLength; } if (reset) { totalLength = 0; step = 0; maxWidth = 0; maxHeight = 0; } if (config.legend_show && !$$.isLegendToShow(id)) { widths[id] = heights[id] = steps[id] = offsets[id] = 0; return } widths[id] = itemWidth; heights[id] = itemHeight; if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; } if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; } maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; if (config.legend_equally) { Object.keys(widths).forEach(function(id) { widths[id] = maxWidth; }); Object.keys(heights).forEach(function(id) { heights[id] = maxHeight; }); margin = (areaLength - maxLength * targetIds.length) / 2; if (margin < posMin) { totalLength = 0; step = 0; targetIds.forEach(function(id) { updateValues(id); }); } else { updateValues(id, true); } } else { updateValues(id); } } if ($$.isLegendInset) { step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; $$.updateLegendStep(step); } if ($$.isLegendRight) { xForLegend = function(id) { return maxWidth * steps[id] }; yForLegend = function(id) { return margins[steps[id]] + offsets[id] }; } else if ($$.isLegendInset) { xForLegend = function(id) { return maxWidth * steps[id] + 10 }; yForLegend = function(id) { return margins[steps[id]] + offsets[id] }; } else { xForLegend = function(id) { return margins[steps[id]] + offsets[id] }; yForLegend = function(id) { return maxHeight * steps[id] }; } xForLegendText = function(id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width }; yForLegendText = function(id, i) { return yForLegend(id, i) + 9 }; xForLegendRect = function(id, i) { return xForLegend(id, i) }; yForLegendRect = function(id, i) { return yForLegend(id, i) - 5 }; x1ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 }; x2ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width }; yForLegendTile = function(id, i) { return yForLegend(id, i) + 4 }; // Define g for legend area l = $$.legend .selectAll('.' + CLASS.legendItem) .data(targetIds) .enter() .append('g') .attr('class', function(id) { return $$.generateClass(CLASS.legendItem, id) }) .style('visibility', function(id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden' }) .style('cursor', function() { return config.interaction_enabled ? 'pointer' : 'auto' }) .on( 'click', config.interaction_enabled ? function(id) { if (config.legend_item_onclick) { config.legend_item_onclick.call($$, id); } else { if ($$.d3.event.altKey) { $$.api.hide(); $$.api.show(id); } else { $$.api.toggle(id); $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); } } } : null ) .on( 'mouseover', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseover) { config.legend_item_onmouseover.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, true); if (!$$.transiting && $$.isTargetToShow(id)) { $$.api.focus(id); } } } : null ) .on( 'mouseout', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseout) { config.legend_item_onmouseout.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, false); $$.api.revert(); } } : null ); l.append('text') .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) .each(function(id, i) { updatePositions(this, id, i); }) .style('pointer-events', 'none') .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); l.append('rect') .attr('class', CLASS.legendItemEvent) .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); l.append('line') .attr('class', CLASS.legendItemTile) .style('stroke', $$.color) .style('pointer-events', 'none') .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { background = $$.legend .insert('g', '.' + CLASS.legendItem) .attr('class', CLASS.legendBackground) .append('rect'); } texts = $$.legend .selectAll('text') .data(targetIds) .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) // MEMO: needed for update .each(function(id, i) { updatePositions(this, id, i); }) ;(withTransition ? texts.transition() : texts) .attr('x', xForLegendText) .attr('y', yForLegendText); rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds) ;(withTransition ? rects.transition() : rects) .attr('width', function(id) { return widths[id] }) .attr('height', function(id) { return heights[id] }) .attr('x', xForLegendRect) .attr('y', yForLegendRect); tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds) ;(withTransition ? tiles.transition() : tiles) .style( 'stroke', $$.levelColor ? function(id) { return $$.levelColor( $$.cache[id].values.reduce(function(total, item) { return total + item.value }, 0) ) } : $$.color ) .attr('x1', x1ForLegendTile) .attr('y1', yForLegendTile) .attr('x2', x2ForLegendTile) .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background) .attr('height', $$.getLegendHeight() - 12) .attr('width', maxWidth * (step + 1) + 10); } // toggle legend state $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemHidden, function(id) { return !$$.isTargetToShow(id) }); // Update all to reflect change of legend $$.updateLegendItemWidth(maxWidth); $$.updateLegendItemHeight(maxHeight); $$.updateLegendStep(step); // Update size and scale $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(withTransitionForTransform, transitions); $$.legendHasRendered = true; }; ChartInternal.prototype.initRegion = function() { var $$ = this; $$.region = $$.main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.regions); }; ChartInternal.prototype.updateRegion = function(duration) { var $$ = this, config = $$.config; // hide if arc type $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); var mainRegion = $$.main .select('.' + CLASS.regions) .selectAll('.' + CLASS.region) .data(config.regions); var g = mainRegion.enter().append('g'); g.append('rect') .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }); g.append('text').text($$.labelRegion.bind($$)); $$.mainRegion = g.merge(mainRegion).attr('class', $$.classRegion.bind($$)); mainRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawRegion = function(withTransition, transition) { var $$ = this, regions = $$.mainRegion, regionLabels = $$.mainRegion.selectAll('text'); return [ (withTransition ? regions.transition(transition) : regions) .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }), (withTransition ? regionLabels.transition(transition) : regionLabels) .attr('x', $$.labelOffsetX.bind($$)) .attr('y', $$.labelOffsetY.bind($$)) .attr('transform', $$.labelTransform.bind($$)) .attr('style', 'text-anchor: left;') ] }; ChartInternal.prototype.regionX = function(d) { var $$ = this, config = $$.config, xPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0; } else { xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0; } return xPos }; ChartInternal.prototype.regionY = function(d) { var $$ = this, config = $$.config, yPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0; } else { yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0; } return yPos }; ChartInternal.prototype.regionWidth = function(d) { var $$ = this, config = $$.config, start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width; } else { end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width; } return end < start ? 0 : end - start }; ChartInternal.prototype.regionHeight = function(d) { var $$ = this, config = $$.config, start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height; } else { end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height; } return end < start ? 0 : end - start }; ChartInternal.prototype.isRegionOnX = function(d) { return !d.axis || d.axis === 'x' }; ChartInternal.prototype.labelRegion = function(d) { return 'label' in d ? d.label : '' }; ChartInternal.prototype.labelTransform = function(d) { return 'vertical' in d && d.vertical ? 'rotate(90)' : '' }; ChartInternal.prototype.labelOffsetX = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? this.regionY(d) + paddingY : this.regionX(d) + paddingX }; ChartInternal.prototype.labelOffsetY = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? -(this.regionX(d) + paddingX) : this.regionY(d) + 10 + paddingY }; function c3LogScale(d3, linearScale, logScale) { var PROJECTION = [0.01, 10]; if (!linearScale) { linearScale = d3.scaleLinear(); linearScale.range(PROJECTION); } if (!logScale) { logScale = d3.scaleLog(); logScale.domain(PROJECTION); logScale.nice(); } // copied from https://github.com/compute-io/logspace function logspace(a, b, len) { var arr, end, tmp, d; if (arguments.length < 3) { len = 10; } else { if (len === 0) { return [] } } // Calculate the increment: end = len - 1; d = (b - a) / end; // Build the output array... arr = new Array(len); tmp = a; arr[0] = Math.pow(10, tmp); for (var i = 1; i < end; i++) { tmp += d; arr[i] = Math.pow(10, tmp); } arr[end] = Math.pow(10, b); return arr } function scale(x) { return logScale(linearScale(x)) } scale.domain = function(x) { if (!arguments.length) { return linearScale.domain() } linearScale.domain(x); return scale }; scale.range = function(x) { if (!arguments.length) { return logScale.range() } logScale.range(x); return scale }; scale.ticks = function(m) { return logspace(-2, 1, m || 10).map(function(v) { return linearScale.invert(v) }) }; scale.copy = function() { return c3LogScale(d3, linearScale.copy(), logScale.copy()) }; return scale } ChartInternal.prototype.getScale = function(min, max, forTimeseries) { return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([ min, max ]) }; ChartInternal.prototype.getX = function(min, max, domain, offset) { var $$ = this, scale = $$.getScale(min, max, $$.isTimeSeries()), _scale = domain ? scale.domain(domain) : scale, key; // Define customized scale if categorized axis if ($$.isCategorized()) { offset = offset || function() { return 0 }; scale = function(d, raw) { var v = _scale(d) + offset(d); return raw ? v : Math.ceil(v) }; } else { scale = function(d, raw) { var v = _scale(d); return raw ? v : Math.ceil(v) }; } // define functions for (key in _scale) { scale[key] = _scale[key]; } scale.orgDomain = function() { return _scale.domain() }; // define custom domain() for categorized axis if ($$.isCategorized()) { scale.domain = function(domain) { if (!arguments.length) { domain = this.orgDomain(); return [domain[0], domain[1] + 1] } _scale.domain(domain); return scale }; } return scale }; /** * Creates and configures a D3 scale instance for the given type. * * By defaults it returns a Linear scale. * * @param {String} type Type of d3-scale to create. Type can be 'linear', 'time', 'timeseries' or 'log'. * @param {Array} domain The scale domain such as [from, to] * @param {Array} range The scale's range such as [from, to] * * @return A d3-scale instance */ ChartInternal.prototype.getY = function(type, domain, range) { let scale; if (type === 'timeseries' || type === 'time') { scale = this.d3.scaleTime(); } else if (type === 'log') { scale = c3LogScale(this.d3); } else if (type === 'linear' || type === undefined) { scale = this.d3.scaleLinear(); } else { throw new Error(`Invalid Y axis type: "${type}"`) } if (domain) { scale.domain(domain); } if (range) { scale.range(range); } return scale }; ChartInternal.prototype.getYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.y2 : this.y }; ChartInternal.prototype.getSubYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY }; ChartInternal.prototype.updateScales = function() { var $$ = this, config = $$.config, forInit = !$$.x; // update edges $$.xMin = config.axis_rotated ? 1 : 0; $$.xMax = config.axis_rotated ? $$.height : $$.width; $$.yMin = config.axis_rotated ? 0 : $$.height; $$.yMax = config.axis_rotated ? $$.width : 1; $$.subXMin = $$.xMin; $$.subXMax = $$.xMax; $$.subYMin = config.axis_rotated ? 0 : $$.height2; $$.subYMax = config.axis_rotated ? $$.width2 : 1; // update scales $$.x = $$.getX( $$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function() { return $$.xAxis.tickOffset() } ); $$.y = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.y.domain(), [$$.yMin, $$.yMax] ); $$.y2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.y2.domain(), [$$.yMin, $$.yMax] ); $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function(d) { return d % 1 ? 0 : $$.subXAxis.tickOffset() }); $$.subY = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.subY.domain(), [$$.subYMin, $$.subYMax] ); $$.subY2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.subY2.domain(), [$$.subYMin, $$.subYMax] ); // update axes $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); $$.xAxisTickValues = $$.axis.getXAxisTickValues(); $$.yAxisTickValues = $$.axis.getYAxisTickValues(); $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); $$.xAxis = $$.axis.getXAxis( $$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ); $$.subXAxis = $$.axis.getXAxis( $$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ); $$.yAxis = $$.axis.getYAxis( 'y', $$.y, $$.yOrient, $$.yAxisTickValues, config.axis_y_tick_outer ); $$.y2Axis = $$.axis.getYAxis( 'y2', $$.y2, $$.y2Orient, $$.y2AxisTickValues, config.axis_y2_tick_outer ); // Set initialized scales to brush and zoom if (!forInit) { if ($$.brush) { $$.brush.updateScale($$.subX); } } // update for arc if ($$.updateArc) { $$.updateArc(); } }; ChartInternal.prototype.selectPoint = function(target, d, i) { var $$ = this, config = $$.config, cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), r = $$.pointSelectR.bind($$); config.data_onselected.call($$.api, d, target.node()); // add selected-circle on low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .data([d]) .enter() .append('circle') .attr('class', function() { return $$.generateClass(CLASS.selectedCircle, i) }) .attr('cx', cx) .attr('cy', cy) .attr('stroke', function() { return $$.color(d) }) .attr('r', function(d) { return $$.pointSelectR(d) * 1.4 }) .transition() .duration(100) .attr('r', r); }; ChartInternal.prototype.unselectPoint = function(target, d, i) { var $$ = this; $$.config.data_onunselected.call($$.api, d, target.node()); // remove selected-circle from low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .transition() .duration(100) .attr('r', 0) .remove(); }; ChartInternal.prototype.togglePoint = function(selected, target, d, i) { selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); }; ChartInternal.prototype.selectPath = function(target, d) { var $$ = this; $$.config.data_onselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.d3.rgb($$.color(d)).brighter(0.75) }); } }; ChartInternal.prototype.unselectPath = function(target, d) { var $$ = this; $$.config.data_onunselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.color(d) }); } }; ChartInternal.prototype.togglePath = function(selected, target, d, i) { selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); }; ChartInternal.prototype.getToggle = function(that, d) { var $$ = this, toggle; if (that.nodeName === 'circle') { if ($$.isStepType(d)) { // circle is hidden in step chart, so treat as within the click area toggle = function() {}; // TODO: how to select step chart? } else { toggle = $$.togglePoint; } } else if (that.nodeName === 'path') { toggle = $$.togglePath; } return toggle }; ChartInternal.prototype.toggleShape = function(that, d, i) { var $$ = this, d3 = $$.d3, config = $$.config, shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), toggle = $$.getToggle(that, d).bind($$); if (config.data_selection_enabled && config.data_selection_isselectable(d)) { if (!config.data_selection_multiple) { $$.main .selectAll( '.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : '') ) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this); if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } }); } shape.classed(CLASS.SELECTED, !isSelected); toggle(!isSelected, shape, d, i); } }; ChartInternal.prototype.initBar = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); }; ChartInternal.prototype.updateTargetsForBar = function(targets) { var $$ = this, config = $$.config, mainBars, mainBarEnter, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classFocus = $$.classFocus.bind($$); mainBars = $$.main .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) .attr('class', function(d) { return classChartBar(d) + classFocus(d) }); mainBarEnter = mainBars .enter() .append('g') .attr('class', classChartBar) .style('pointer-events', 'none'); // Bars for each data mainBarEnter .append('g') .attr('class', classBars) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }); }; ChartInternal.prototype.updateBar = function(durationForExit) { var $$ = this, barData = $$.barData.bind($$), classBar = $$.classBar.bind($$), initialOpacity = $$.initialOpacity.bind($$), color = function(d) { return $$.color(d.id) }; var mainBar = $$.main .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data(barData); var mainBarEnter = mainBar .enter() .append('path') .attr('class', classBar) .style('stroke', color) .style('fill', color); $$.mainBar = mainBarEnter.merge(mainBar).style('opacity', initialOpacity); mainBar .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawBar = function( drawBar, withTransition, transition ) { const $$ = this; return [ (withTransition ? this.mainBar.transition(transition) : this.mainBar) .attr('d', drawBar) .style('stroke', this.color) .style('fill', this.color) .style('opacity', d => ($$.isTargetToShow(d.id) ? 1 : 0)) ] }; ChartInternal.prototype.getBarW = function(axis, barTargetsNum) { var $$ = this, config = $$.config, w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0; return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w }; ChartInternal.prototype.getBars = function(i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')) }; ChartInternal.prototype.expandBars = function(i, id, reset) { var $$ = this; if (reset) { $$.unexpandBars(); } $$.getBars(i, id).classed(CLASS.EXPANDED, true); }; ChartInternal.prototype.unexpandBars = function(i) { var $$ = this; $$.getBars(i).classed(CLASS.EXPANDED, false); }; ChartInternal.prototype.generateDrawBar = function(barIndices, isSub) { var $$ = this, config = $$.config, getPoints = $$.generateGetBarPoints(barIndices, isSub); return function(d, i) { // 4 points that make a bar var points = getPoints(d, i); // switch points if axis is rotated, not applicable for sub chart var indexX = config.axis_rotated ? 1 : 0; var indexY = config.axis_rotated ? 0 : 1; var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z'; return path } }; ChartInternal.prototype.generateGetBarPoints = function(barIndices, isSub) { var $$ = this, axis = isSub ? $$.subXAxis : $$.xAxis, barTargetsNum = barIndices.__max__ + 1, barW = $$.getBarW(axis, barTargetsNum), barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), barY = $$.getShapeY(!!isSub), barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), barSpaceOffset = barW * ($$.config.bar_space / 2), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = barOffset(d, i) || y0, // offset is for stacked bar chart posX = barX(d), posY = barY(d); // fix posY not to overflow opposite quadrant if ($$.config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } posY -= y0 - offset; // 4 points that make a bar return [ [posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY], [posX + barW - barSpaceOffset, posY], [posX + barW - barSpaceOffset, offset] ] } }; /** * Returns whether the data point is within the given bar shape. * * @param mouse * @param barShape * @return {boolean} */ ChartInternal.prototype.isWithinBar = function(mouse, barShape) { return isWithinBox(mouse, getBBox(barShape), 2) }; ChartInternal.prototype.getShapeIndices = function(typeFilter) { var $$ = this, config = $$.config, indices = {}, i = 0, j, k; $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach( function(d) { for (j = 0; j < config.data_groups.length; j++) { if (config.data_groups[j].indexOf(d.id) < 0) { continue } for (k = 0; k < config.data_groups[j].length; k++) { if (config.data_groups[j][k] in indices) { indices[d.id] = indices[config.data_groups[j][k]]; break } } } if (isUndefined(indices[d.id])) { indices[d.id] = i++; } } ); indices.__max__ = i - 1; return indices }; ChartInternal.prototype.getShapeX = function( offset, targetsNum, indices, isSub ) { var $$ = this, scale = isSub ? $$.subX : $$.x; return function(d) { var index = d.id in indices ? indices[d.id] : 0; return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0 } }; ChartInternal.prototype.getShapeY = function(isSub) { const $$ = this; return function(d) { const scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); return scale( $$.isTargetNormalized(d.id) ? $$.getRatio('index', d, true) : d.value ) } }; ChartInternal.prototype.getShapeOffset = function(typeFilter, indices, isSub) { var $$ = this, targets = $$.orderTargets( $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)) ), targetIds = targets.map(function(t) { return t.id }); return function(d, i) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), y0 = scale(0), offset = y0; targets.forEach(function(t) { const rowValues = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; const isTargetNormalized = $$.isTargetNormalized(d.id); const values = rowValues.map(v => isTargetNormalized ? $$.getRatio('index', v, true) : v.value ); if (t.id === d.id || indices[t.id] !== indices[d.id]) { return } if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { // check if the x values line up if (isUndefined(rowValues[i]) || +rowValues[i].x !== +d.x) { // "+" for timeseries // if not, try to find the value that does line up i = -1; rowValues.forEach(function(v, j) { const x1 = v.x.constructor === Date ? +v.x : v.x; const x2 = d.x.constructor === Date ? +d.x : d.x; if (x1 === x2) { i = j; } }); } if (i in rowValues && rowValues[i].value * d.value >= 0) { offset += scale(values[i]) - y0; } } }); return offset } }; ChartInternal.prototype.isWithinShape = function(that, d) { var $$ = this, shape = $$.d3.select(that), isWithin; if (!$$.isTargetToShow(d.id)) { isWithin = false; } else if (that.nodeName === 'circle') { isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); } else if (that.nodeName === 'path') { isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true; } return isWithin }; ChartInternal.prototype.getInterpolate = function(d) { var $$ = this, d3 = $$.d3, types = { linear: d3.curveLinear, 'linear-closed': d3.curveLinearClosed, basis: d3.curveBasis, 'basis-open': d3.curveBasisOpen, 'basis-closed': d3.curveBasisClosed, bundle: d3.curveBundle, cardinal: d3.curveCardinal, 'cardinal-open': d3.curveCardinalOpen, 'cardinal-closed': d3.curveCardinalClosed, monotone: d3.curveMonotoneX, step: d3.curveStep, 'step-before': d3.curveStepBefore, 'step-after': d3.curveStepAfter }, type; if ($$.isSplineType(d)) { type = types[$$.config.spline_interpolation_type] || types.cardinal; } else if ($$.isStepType(d)) { type = types[$$.config.line_step_type]; } else { type = types.linear; } return type }; ChartInternal.prototype.initLine = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); }; ChartInternal.prototype.updateTargetsForLine = function(targets) { var $$ = this, config = $$.config, mainLines, mainLineEnter, classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$), classCircles = $$.classCircles.bind($$), classFocus = $$.classFocus.bind($$); mainLines = $$.main .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) .attr('class', function(d) { return classChartLine(d) + classFocus(d) }); mainLineEnter = mainLines .enter() .append('g') .attr('class', classChartLine) .style('opacity', 0) .style('pointer-events', 'none'); // Lines for each data mainLineEnter.append('g').attr('class', classLines); // Areas mainLineEnter.append('g').attr('class', classAreas); // Circles for each data point on lines mainLineEnter.append('g').attr('class', function(d) { return $$.generateClass(CLASS.selectedCircles, d.id) }); mainLineEnter .append('g') .attr('class', classCircles) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }); // Update date for selected circles targets.forEach(function(t) { $$.main .selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)) .selectAll('.' + CLASS.selectedCircle) .each(function(d) { d.value = t.values[d.index].value; }); }); // MEMO: can not keep same color... //mainLineUpdate.exit().remove(); }; ChartInternal.prototype.updateLine = function(durationForExit) { var $$ = this; var mainLine = $$.main .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var mainLineEnter = mainLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); $$.mainLine = mainLineEnter .merge(mainLine) .style('opacity', $$.initialOpacity.bind($$)) .style('shape-rendering', function(d) { return $$.isStepType(d) ? 'crispEdges' : '' }) .attr('transform', null); mainLine .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawLine = function( drawLine, withTransition, transition ) { return [ (withTransition ? this.mainLine.transition(transition) : this.mainLine) .attr('d', drawLine) .style('stroke', this.color) .style('opacity', 1) ] }; ChartInternal.prototype.generateDrawLine = function(lineIndices, isSub) { var $$ = this, config = $$.config, line = $$.d3.line(), getPoints = $$.generateGetLinePoints(lineIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, yValue = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value) }; line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); if (!config.line_connectNull) { line = line.defined(function(d) { return d.value != null }); } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x = isSub ? $$.subX : $$.x, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path; if ($$.isLineType(d)) { if (config.data_regions[d.id]) { path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); } else { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = line.curve($$.getInterpolate(d))(values); } } else { if (values[0]) { x0 = x(values[0].x); y0 = y(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0' } }; ChartInternal.prototype.generateGetLinePoints = function(lineIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, lineTargetsNum = lineIndices.__max__ + 1, x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), y = $$.getShapeY(!!isSub), lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = lineOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the line position return [ [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)] // needed for compatibility ] } }; ChartInternal.prototype.lineWithRegions = function(d, x, y, _regions) { var $$ = this, config = $$.config, prev = -1, i, j, s = 'M', sWithRegion, xp, yp, dx, dy, dd, diff, diffx2, xOffset = $$.isCategorized() ? 0.5 : 0, xValue, yValue, regions = []; function isWithinRegions(x, regions) { var i; for (i = 0; i < regions.length; i++) { if (regions[i].start < x && x <= regions[i].end) { return true } } return false } // Check start/end of regions if (isDefined(_regions)) { for (i = 0; i < _regions.length; i++) { regions[i] = {}; if (isUndefined(_regions[i].start)) { regions[i].start = d[0].x; } else { regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; } if (isUndefined(_regions[i].end)) { regions[i].end = d[d.length - 1].x; } else { regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; } } } // Set scales xValue = config.axis_rotated ? function(d) { return y(d.value) } : function(d) { return x(d.x) }; yValue = config.axis_rotated ? function(d) { return x(d.x) } : function(d) { return y(d.value) }; // Define svg generator function for region function generateM(points) { return ( 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1] ) } if ($$.isTimeSeries()) { sWithRegion = function(d0, d1, j, diff) { var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, xv0 = new Date(x0 + x_diff * j), xv1 = new Date(x0 + x_diff * (j + diff)), points; if (config.axis_rotated) { points = [ [y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)] ]; } else { points = [ [x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))] ]; } return generateM(points) }; } else { sWithRegion = function(d0, d1, j, diff) { var points; if (config.axis_rotated) { points = [ [y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))] ]; } else { points = [ [x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))] ]; } return generateM(points) }; } // Generate for (i = 0; i < d.length; i++) { // Draw as normal if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { s += ' ' + xValue(d[i]) + ' ' + yValue(d[i]); } // Draw with region // TODO: Fix for horizotal charts else { xp = $$.getScale( d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries() ); yp = $$.getScale(d[i - 1].value, d[i].value); dx = x(d[i].x) - x(d[i - 1].x); dy = y(d[i].value) - y(d[i - 1].value); dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); diff = 2 / dd; diffx2 = diff * 2; for (j = diff; j <= 1; j += diffx2) { s += sWithRegion(d[i - 1], d[i], j, diff); } } prev = d[i].x; } return s }; ChartInternal.prototype.updateArea = function(durationForExit) { var $$ = this, d3 = $$.d3; var mainArea = $$.main .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var mainAreaEnter = mainArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0 }); $$.mainArea = mainAreaEnter .merge(mainArea) .style('opacity', $$.orgAreaOpacity); mainArea .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawArea = function( drawArea, withTransition, transition ) { return [ (withTransition ? this.mainArea.transition(transition) : this.mainArea) .attr('d', drawArea) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) ] }; ChartInternal.prototype.generateDrawArea = function(areaIndices, isSub) { var $$ = this, config = $$.config, area = $$.d3.area(), getPoints = $$.generateGetAreaPoints(areaIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, value0 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)) }, value1 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value) }; area = config.axis_rotated ? area .x0(value0) .x1(value1) .y(xValue) : area .x(xValue) .y0(config.area_above ? 0 : value0) .y1(value1); if (!config.line_connectNull) { area = area.defined(function(d) { return d.value !== null }); } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path; if ($$.isAreaType(d)) { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = area.curve($$.getInterpolate(d))(values); } else { if (values[0]) { x0 = $$.x(values[0].x); y0 = $$.getYScale(d.id)(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0' } }; ChartInternal.prototype.getAreaBaseValue = function() { return 0 }; ChartInternal.prototype.generateGetAreaPoints = function(areaIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, areaTargetsNum = areaIndices.__max__ + 1, x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), y = $$.getShapeY(!!isSub), areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = areaOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the area position return [ [posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, offset] // needed for compatibility ] } }; ChartInternal.prototype.updateCircle = function(cx, cy) { var $$ = this; var mainCircle = $$.main .selectAll('.' + CLASS.circles) .selectAll('.' + CLASS.circle) .data($$.lineOrScatterOrStanfordData.bind($$)); var mainCircleEnter = mainCircle .enter() .append('circle') .attr('shape-rendering', $$.isStanfordGraphType() ? 'crispEdges' : '') .attr('class', $$.classCircle.bind($$)) .attr('cx', cx) .attr('cy', cy) .attr('r', $$.pointR.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ); $$.mainCircle = mainCircleEnter .merge(mainCircle) .style( 'opacity', $$.isStanfordGraphType() ? 1 : $$.initialOpacityForCircle.bind($$) ); mainCircle.exit().style('opacity', 0); }; ChartInternal.prototype.redrawCircle = function( cx, cy, withTransition, transition ) { var $$ = this, selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle); return [ (withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle) .style('opacity', this.opacityForCircle.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ) .attr('cx', cx) .attr('cy', cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles) .attr('cx', cx) .attr('cy', cy) ] }; ChartInternal.prototype.circleX = function(d) { return d.x || d.x === 0 ? this.x(d.x) : null }; ChartInternal.prototype.updateCircleY = function() { var $$ = this, lineIndices, getPoints; if ($$.config.data_groups.length > 0) { (lineIndices = $$.getShapeIndices($$.isLineType)), (getPoints = $$.generateGetLinePoints(lineIndices)); $$.circleY = function(d, i) { return getPoints(d, i)[0][1] }; } else { $$.circleY = function(d) { return $$.getYScale(d.id)(d.value) }; } }; ChartInternal.prototype.getCircles = function(i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')) }; ChartInternal.prototype.expandCircles = function(i, id, reset) { var $$ = this, r = $$.pointExpandedR.bind($$); if (reset) { $$.unexpandCircles(); } $$.getCircles(i, id) .classed(CLASS.EXPANDED, true) .attr('r', r); }; ChartInternal.prototype.unexpandCircles = function(i) { var $$ = this, r = $$.pointR.bind($$); $$.getCircles(i) .filter(function() { return $$.d3.select(this).classed(CLASS.EXPANDED) }) .classed(CLASS.EXPANDED, false) .attr('r', r); }; ChartInternal.prototype.pointR = function(d) { var $$ = this, config = $$.config; return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r }; ChartInternal.prototype.pointExpandedR = function(d) { var $$ = this, config = $$.config; if (config.point_focus_expand_enabled) { return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75 } else { return $$.pointR(d) } }; ChartInternal.prototype.pointSelectR = function(d) { var $$ = this, config = $$.config; return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4 }; ChartInternal.prototype.isWithinCircle = function(that, r) { var d3 = this.d3, mouse = d3.mouse(that), d3_this = d3.select(that), cx = +d3_this.attr('cx'), cy = +d3_this.attr('cy'); return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r }; ChartInternal.prototype.isWithinStep = function(that, y) { return Math.abs(y - this.d3.mouse(that)[1]) < 30 }; ChartInternal.prototype.getCurrentWidth = function() { var $$ = this, config = $$.config; return config.size_width ? config.size_width : $$.getParentWidth() }; ChartInternal.prototype.getCurrentHeight = function() { var $$ = this, config = $$.config, h = config.size_height ? config.size_height : $$.getParentHeight(); return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1) }; ChartInternal.prototype.getCurrentPaddingTop = function() { var $$ = this, config = $$.config, padding = isValue(config.padding_top) ? config.padding_top : 0; if ($$.title && $$.title.node()) { padding += $$.getTitlePadding(); } return padding }; ChartInternal.prototype.getCurrentPaddingBottom = function() { var config = this.config; return isValue(config.padding_bottom) ? config.padding_bottom : 0 }; ChartInternal.prototype.getCurrentPaddingLeft = function(withoutRecompute) { var $$ = this, config = $$.config; if (isValue(config.padding_left)) { return config.padding_left } else if (config.axis_rotated) { return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40) } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1 } else { return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)) } }; ChartInternal.prototype.getCurrentPaddingRight = function() { var $$ = this, config = $$.config, padding = 0, defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; if (isValue(config.padding_right)) { padding = config.padding_right + 1; // 1 is needed not to hide tick line } else if (config.axis_rotated) { padding = defaultPadding + legendWidthOnRight; } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated padding = 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); } else { padding = ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; } if ($$.colorScale && $$.colorScale.node()) { padding += $$.getColorScalePadding(); } return padding }; ChartInternal.prototype.getParentRectValue = function(key) { var parent = this.selectChart.node(), v; while (parent && parent.tagName !== 'BODY') { try { v = parent.getBoundingClientRect()[key]; } catch (e) { if (key === 'width') { // In IE in certain cases getBoundingClientRect // will cause an "unspecified error" v = parent.offsetWidth; } } if (v) { break } parent = parent.parentNode; } return v }; ChartInternal.prototype.getParentWidth = function() { return this.getParentRectValue('width') }; ChartInternal.prototype.getParentHeight = function() { var h = this.selectChart.style('height'); return h.indexOf('px') > 0 ? +h.replace('px', '') : 0 }; ChartInternal.prototype.getSvgLeft = function(withoutRecompute) { var $$ = this, config = $$.config, hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, leftAxis = $$.main.select('.' + leftAxisClass).node(), svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, chartRect = $$.selectChart.node().getBoundingClientRect(), hasArc = $$.hasArcType(), svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); return svgLeft > 0 ? svgLeft : 0 }; ChartInternal.prototype.getAxisWidthByAxisId = function(id, withoutRecompute) { var $$ = this, position = $$.axis.getLabelPositionById(id); return ( $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40) ) }; ChartInternal.prototype.getHorizontalAxisHeight = function(axisId) { var $$ = this, config = $$.config, h = 30; if (axisId === 'x' && !config.axis_x_show) { return 8 } if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height } if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1 } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top } // Calculate x axis height when tick rotated if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_x_tick_rotate))) / 180); } // Calculate y axis height when tick rotated if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_y_tick_rotate))) / 180); } return ( h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0) ) }; ChartInternal.prototype.initBrush = function(scale) { var $$ = this, d3 = $$.d3; // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function() { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return } $$.redrawForBrush(); }) .on('end', function() { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); } }); $$.brush.updateExtent = function() { var range = this.scale.range(), extent; if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ]; } else { extent = [ [range[0], 0], [range[1], $$.height2] ]; } this.extent(extent); return this }; $$.brush.updateScale = function(scale) { this.scale = scale; return this }; $$.brush.update = function(scale) { this.updateScale(scale || $$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call(this); }; $$.brush.clear = function() { $$.context.select('.' + CLASS.brush).call($$.brush.move, null); }; $$.brush.selection = function() { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()) }; $$.brush.selectionAsValue = function(selectionAsValue, withTransition) { var selection, brush; if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ]; brush = $$.context.select('.' + CLASS.brush); if (withTransition) { brush = brush.transition(); } $$.brush.move(brush, selection); } return [] } selection = $$.brush.selection() || [0, 0]; return [this.scale.invert(selection[0]), this.scale.invert(selection[1])] }; $$.brush.empty = function() { var selection = $$.brush.selection(); return !selection || selection[0] === selection[1] }; return $$.brush.updateScale(scale) }; ChartInternal.prototype.initSubchart = function() { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))); // set style context.style('visibility', 'visible'); // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart); // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush); // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis); }; ChartInternal.prototype.initSubchartBrush = function() { var $$ = this; // Add extent rect for Brush $$.initBrush($$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call($$.brush); }; ChartInternal.prototype.updateTargetsForSubchart = function(targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$); //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets); contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0); contextBarEnter.merge(contextBar).attr('class', classChartBar); // Bars for each data contextBarEnter.append('g').attr('class', classBars); //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets); contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0); contextLineEnter.merge(contextLine).attr('class', classChartLine); // Lines for each data contextLineEnter.append('g').attr('class', classLines); // Area contextLineEnter.append('g').attr('class', classAreas); //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr( config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2 ); }; ChartInternal.prototype.updateBarForSubchart = function(durationForExit) { var $$ = this; var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)); var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color); contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawBarForSubchart = function( drawBarOnSub, withTransition, duration ) { (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar ) .attr('d', drawBarOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateLineForSubchart = function(durationForExit) { var $$ = this; var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawLineForSubchart = function( drawLineOnSub, withTransition, duration ) { (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine ) .attr('d', drawLineOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateAreaForSubchart = function(durationForExit) { var $$ = this, d3 = $$.d3; var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0 }); contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0); }; ChartInternal.prototype.redrawAreaForSubchart = function( drawAreaOnSub, withTransition, duration ) { (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea ) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity); }; ChartInternal.prototype.redrawSubchart = function( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub; // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()); } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()); } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true); drawBarOnSub = $$.generateDrawBar(barIndices, true); drawLineOnSub = $$.generateDrawLine(lineIndices, true); $$.updateBarForSubchart(duration); $$.updateLineForSubchart(duration); $$.updateAreaForSubchart(duration); $$.redrawBarForSubchart(drawBarOnSub, duration, duration); $$.redrawLineForSubchart(drawLineOnSub, duration, duration); $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); } }; ChartInternal.prototype.redrawForBrush = function() { var $$ = this, x = $$.x, d3 = $$.d3, s; $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }); // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range(); $$.main .select('.' + CLASS.eventRect) .call( $$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) ); $$.config.subchart_onbrush.call($$.api, x.orgDomain()); }; ChartInternal.prototype.transformContext = function( withTransition, transitions ) { var $$ = this, subXAxis; if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX; } else { subXAxis = $$.context.select('.' + CLASS.axisX); if (withTransition) { subXAxis = subXAxis.transition(); } } $$.context.attr('transform', $$.getTranslate('context')); subXAxis.attr('transform', $$.getTranslate('subx')); }; ChartInternal.prototype.getDefaultSelection = function() { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection; if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])]; } return selection }; ChartInternal.prototype.removeSubchart = function() { const $$ = this; $$.brush = null; $$.context.remove(); $$.context = null; }; ChartInternal.prototype.initText = function() { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartTexts); $$.mainText = $$.d3.selectAll([]); }; ChartInternal.prototype.updateTargetsForText = function(targets) { var $$ = this, classChartText = $$.classChartText.bind($$), classTexts = $$.classTexts.bind($$), classFocus = $$.classFocus.bind($$); var mainText = $$.main .select('.' + CLASS.chartTexts) .selectAll('.' + CLASS.chartText) .data(targets); var mainTextEnter = mainText .enter() .append('g') .attr('class', classChartText) .style('opacity', 0) .style('pointer-events', 'none'); mainTextEnter.append('g').attr('class', classTexts); mainTextEnter.merge(mainText).attr('class', function(d) { return classChartText(d) + classFocus(d) }); }; ChartInternal.prototype.updateText = function( xForText, yForText, durationForExit ) { var $$ = this, config = $$.config, barOrLineData = $$.barOrLineData.bind($$), classText = $$.classText.bind($$); var mainText = $$.main .selectAll('.' + CLASS.texts) .selectAll('.' + CLASS.text) .data(barOrLineData); var mainTextEnter = mainText .enter() .append('text') .attr('class', classText) .attr('text-anchor', function(d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle' }) .style('stroke', 'none') .attr('x', xForText) .attr('y', yForText) .style('fill', function(d) { return $$.color(d) }) .style('fill-opacity', 0); $$.mainText = mainTextEnter.merge(mainText).text(function(d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j) }); mainText .exit() .transition() .duration(durationForExit) .style('fill-opacity', 0) .remove(); }; ChartInternal.prototype.redrawText = function( xForText, yForText, forFlow, withTransition, transition ) { return [ (withTransition ? this.mainText.transition(transition) : this.mainText) .attr('x', xForText) .attr('y', yForText) .style('fill', this.color) .style('fill-opacity', forFlow ? 0 : this.opacityForText.bind(this)) ] }; ChartInternal.prototype.getTextRect = function(text, cls, element) { var dummy = this.d3 .select('body') .append('div') .classed('c3', true), svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0), font = this.d3.select(element).style('font'), rect; svg .selectAll('.dummy') .data([text]) .enter() .append('text') .classed(cls ? cls : '', true) .style('font', font) .text(text) .each(function() { rect = getBBox(this); }); dummy.remove(); return rect }; ChartInternal.prototype.generateXYForText = function( areaIndices, barIndices, lineIndices, forX ) { var $$ = this, getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false), getter = forX ? $$.getXForText : $$.getYForText; return function(d, i) { var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; return getter.call($$, getPoints(d, i), d, this) } }; ChartInternal.prototype.getXForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding; if ($$.config.axis_rotated) { padding = $$.isBarType(d) ? 4 : 6; xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); } else { xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; } // show labels regardless of the domain if value is null if (d.value === null) { if (xPos > $$.width) { xPos = $$.width - box.width; } else if (xPos < 0) { xPos = 4; } } return xPos }; ChartInternal.prototype.getYForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), yPos; if ($$.config.axis_rotated) { yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; } else { yPos = points[2][1]; if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { yPos += box.height; if ($$.isBarType(d) && $$.isSafari()) { yPos -= 3; } else if (!$$.isBarType(d) && $$.isChrome()) { yPos += 3; } } else { yPos += $$.isBarType(d) ? -3 : -6; } } // show labels regardless of the domain if value is null if (d.value === null && !$$.config.axis_rotated) { if (yPos < box.height) { yPos = box.height; } else if (yPos > this.height) { yPos = this.height - 4; } } return yPos }; ChartInternal.prototype.initTitle = function() { var $$ = this; $$.title = $$.svg .append('text') .text($$.config.title_text) .attr('class', $$.CLASS.title); }; ChartInternal.prototype.redrawTitle = function() { var $$ = this; $$.title.attr('x', $$.xForTitle.bind($$)).attr('y', $$.yForTitle.bind($$)); }; ChartInternal.prototype.xForTitle = function() { var $$ = this, config = $$.config, position = config.title_position || 'left', x; if (position.indexOf('right') >= 0) { x = $$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width - config.title_padding.right; } else if (position.indexOf('center') >= 0) { x = Math.max( ($$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width) / 2, 0 ); } else { // left x = config.title_padding.left; } return x }; ChartInternal.prototype.yForTitle = function() { var $$ = this; return ( $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()) .height ) }; ChartInternal.prototype.getTitlePadding = function() { var $$ = this; return $$.yForTitle() + $$.config.title_padding.bottom }; function powerOfTen(d) { return d / Math.pow(10, Math.ceil(Math.log(d) / Math.LN10 - 1e-12)) === 1 } ChartInternal.prototype.drawColorScale = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], barWidth, barHeight, axis, points, legendAxis, axisScale, inverseScale, height; barWidth = !isNaN(config.stanford_scaleWidth) ? config.stanford_scaleWidth : 20; barHeight = 5; if (barHeight < 0 || barWidth < 0) { throw Error("Colorscale's barheight and barwidth must be greater than 0.") } height = $$.height - config.stanford_padding.bottom - config.stanford_padding.top; points = d3.range(config.stanford_padding.bottom, height, barHeight); inverseScale = d3 .scaleSequential(target.colors) .domain([points[points.length - 1], points[0]]); if ($$.colorScale) { $$.colorScale.remove(); } $$.colorScale = $$.svg .append('g') .attr('width', 50) .attr('height', height) .attr('class', CLASS.colorScale); $$.colorScale .append('g') .attr('transform', `translate(0, ${config.stanford_padding.top})`) .selectAll('bars') .data(points) .enter() .append('rect') .attr('y', (d, i) => i * barHeight) .attr('x', 0) .attr('width', barWidth) .attr('height', barHeight) .attr('fill', function(d) { return inverseScale(d) }); // Legend Axis axisScale = d3 .scaleLog() .domain([target.minEpochs, target.maxEpochs]) .range([ points[0] + config.stanford_padding.top + points[points.length - 1] + barHeight - 1, points[0] + config.stanford_padding.top ]); legendAxis = d3.axisRight(axisScale); if (config.stanford_scaleFormat === 'pow10') { legendAxis.tickValues([1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]); } else if (isFunction(config.stanford_scaleFormat)) { legendAxis.tickFormat(config.stanford_scaleFormat); } else { legendAxis.tickFormat(d3.format('d')); } if (isFunction(config.stanford_scaleValues)) { legendAxis.tickValues( config.stanford_scaleValues(target.minEpochs, target.maxEpochs) ); } // Draw Axis axis = $$.colorScale .append('g') .attr('class', 'legend axis') .attr('transform', `translate(${barWidth},0)`) .call(legendAxis); if (config.stanford_scaleFormat === 'pow10') { axis .selectAll('.tick text') .text(null) .filter(powerOfTen) .text(10) .append('tspan') .attr('dy', '-.7em') // https://bl.ocks.org/mbostock/6738229 .text(function(d) { return Math.round(Math.log(d) / Math.LN10) }); } $$.colorScale.attr( 'transform', `translate(${$$.currentWidth - $$.xForColorScale()}, 0)` ); }; ChartInternal.prototype.xForColorScale = function() { var $$ = this; return $$.config.stanford_padding.right + getBBox($$.colorScale.node()).width }; ChartInternal.prototype.getColorScalePadding = function() { var $$ = this; return $$.xForColorScale() + $$.config.stanford_padding.left + 20 }; ChartInternal.prototype.isStanfordGraphType = function() { var $$ = this; return $$.config.data_type === 'stanford' }; ChartInternal.prototype.initStanfordData = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], epochs, maxEpochs, minEpochs; // Make larger values appear on top target.values.sort(compareEpochs); // Get array of epochs epochs = target.values.map(a => a.epochs); minEpochs = !isNaN(config.stanford_scaleMin) ? config.stanford_scaleMin : d3.min(epochs); maxEpochs = !isNaN(config.stanford_scaleMax) ? config.stanford_scaleMax : d3.max(epochs); if (minEpochs > maxEpochs) { throw Error('Number of minEpochs has to be smaller than maxEpochs') } target.colors = isFunction(config.stanford_colors) ? config.stanford_colors : d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)); target.colorscale = d3 .scaleSequentialLog(target.colors) .domain([minEpochs, maxEpochs]); target.minEpochs = minEpochs; target.maxEpochs = maxEpochs; }; ChartInternal.prototype.getStanfordPointColor = function(d) { var $$ = this, target = $$.data.targets[0]; return target.colorscale(d.epochs) }; // http://jsfiddle.net/Xotic750/KtzLq/ ChartInternal.prototype.getCentroid = function(points) { var area = getRegionArea(points); var x = 0, y = 0, i, j, f, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; f = point1.x * point2.y - point2.x * point1.y; x += (point1.x + point2.x) * f; y += (point1.y + point2.y) * f; } f = area * 6; return { x: x / f, y: y / f } }; ChartInternal.prototype.getStanfordTooltipTitle = function(d) { var $$ = this, labelX = $$.axis.getLabelText('x'), labelY = $$.axis.getLabelText('y'); return ` ${labelX ? sanitise(labelX) : 'x'}${ d.x } ${labelY ? sanitise(labelY) : 'y'}${ d.value } ` }; ChartInternal.prototype.countEpochsInRegion = function(region) { var $$ = this, target = $$.data.targets[0], total, count; total = target.values.reduce( (accumulator, currentValue) => accumulator + Number(currentValue.epochs), 0 ); count = target.values.reduce((accumulator, currentValue) => { if (pointInRegion(currentValue, region)) { return accumulator + Number(currentValue.epochs) } return accumulator }, 0); return { value: count, percentage: count !== 0 ? ((count / total) * 100).toFixed(1) : 0 } }; var getRegionArea = function(points) { // thanks to: https://stackoverflow.com/questions/16282330/find-centerpoint-of-polygon-in-javascript var area = 0, i, j, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; area += point1.x * point2.y; area -= point1.y * point2.x; } area /= 2; return area }; var pointInRegion = function(point, region) { // thanks to: http://bl.ocks.org/bycoffe/5575904 // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html let xi, yi, yj, xj, intersect, x = point.x, y = point.value, inside = false; for (let i = 0, j = region.length - 1; i < region.length; j = i++) { xi = region[i].x; yi = region[i].y; xj = region[j].x; yj = region[j].y; intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside }; var compareEpochs = function(a, b) { if (a.epochs < b.epochs) { return -1 } if (a.epochs > b.epochs) { return 1 } return 0 }; ChartInternal.prototype.initStanfordElements = function() { var $$ = this; // Avoid blocking eventRect $$.stanfordElements = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.stanfordElements); $$.stanfordElements.append('g').attr('class', CLASS.stanfordLines); $$.stanfordElements.append('g').attr('class', CLASS.stanfordTexts); $$.stanfordElements.append('g').attr('class', CLASS.stanfordRegions); }; ChartInternal.prototype.updateStanfordElements = function(duration) { var $$ = this, main = $$.main, config = $$.config, stanfordLine, stanfordLineEnter, stanfordRegion, stanfordRegionEnter, stanfordText, stanfordTextEnter, xvCustom = $$.xvCustom.bind($$), yvCustom = $$.yvCustom.bind($$), countPointsInRegion = $$.countEpochsInRegion.bind($$); // Stanford-Lines stanfordLine = main .select('.' + CLASS.stanfordLines) .style('shape-rendering', 'geometricprecision') .selectAll('.' + CLASS.stanfordLine) .data(config.stanford_lines); // enter stanfordLineEnter = stanfordLine .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordLine + (d['class'] ? ' ' + d['class'] : '') }); stanfordLineEnter .append('line') .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 0); // update $$.stanfordLines = stanfordLineEnter.merge(stanfordLine); $$.stanfordLines .select('line') .transition() .duration(duration) .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 1); // exit stanfordLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Text stanfordText = main .select('.' + CLASS.stanfordTexts) .selectAll('.' + CLASS.stanfordText) .data(config.stanford_texts); // enter stanfordTextEnter = stanfordText .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordText + (d['class'] ? ' ' + d['class'] : '') }); stanfordTextEnter .append('text') .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .style('opacity', 0); // update $$.stanfordTexts = stanfordTextEnter.merge(stanfordText); $$.stanfordTexts .select('text') .transition() .duration(duration) .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .text(function(d) { return d.content }) .style('opacity', 1); // exit stanfordText .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Regions stanfordRegion = main .select('.' + CLASS.stanfordRegions) .selectAll('.' + CLASS.stanfordRegion) .data(config.stanford_regions); // enter stanfordRegionEnter = stanfordRegion .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordRegion + (d['class'] ? ' ' + d['class'] : '') }); stanfordRegionEnter .append('polygon') .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', 0); stanfordRegionEnter .append('text') .attr('x', d => $$.getCentroid(d.points).x) .attr('y', d => $$.getCentroid(d.points).y) .style('opacity', 0); // update $$.stanfordRegions = stanfordRegionEnter.merge(stanfordRegion); $$.stanfordRegions .select('polygon') .transition() .duration(duration) .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', d => { return d.opacity ? d.opacity : 0.2 }); $$.stanfordRegions .select('text') .transition() .duration(duration) .attr('x', d => config.axis_rotated ? yvCustom($$.getCentroid(d.points), 'y') : xvCustom($$.getCentroid(d.points), 'x') ) .attr('y', d => config.axis_rotated ? xvCustom($$.getCentroid(d.points), 'x') : yvCustom($$.getCentroid(d.points), 'y') ) .text(function(d) { if (d.text) { var value, percentage, temp; if ($$.isStanfordGraphType()) { temp = countPointsInRegion(d.points); value = temp.value; percentage = temp.percentage; } return d.text(value, percentage) } return '' }) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('opacity', 1); // exit stanfordRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.initTooltip = function() { var $$ = this, config = $$.config, i; $$.tooltip = $$.selectChart .style('position', 'relative') .append('div') .attr('class', CLASS.tooltipContainer) .style('position', 'absolute') .style('pointer-events', 'none') .style('display', 'none'); // Show tooltip if needed if (config.tooltip_init_show) { if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); for (i = 0; i < $$.data.targets[0].values.length; i++) { if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { break } } config.tooltip_init_x = i; } $$.tooltip.html( config.tooltip_contents.call( $$, $$.data.targets.map(function(d) { return $$.addName(d.values[config.tooltip_init_x]) }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color ) ); $$.tooltip .style('top', config.tooltip_init_position.top) .style('left', config.tooltip_init_position.left) .style('display', 'block'); } }; ChartInternal.prototype.getTooltipSortFunction = function() { var $$ = this, config = $$.config; if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { // if data are not grouped or if an order is specified // for the tooltip values we sort them by their values var order = config.tooltip_order; if (order === undefined) { order = config.data_order; } var valueOf = function(obj) { return obj ? obj.value : null }; // if data are not grouped, we sort them by their value if (isString(order) && order.toLowerCase() === 'asc') { return function(a, b) { return valueOf(a) - valueOf(b) } } else if (isString(order) && order.toLowerCase() === 'desc') { return function(a, b) { return valueOf(b) - valueOf(a) } } else if (isFunction(order)) { // if the function is from data_order we need // to wrap the returned function in order to format // the sorted value to the expected format var sortFunction = order; if (config.tooltip_order === undefined) { sortFunction = function(a, b) { return order( a ? { id: a.id, values: [a] } : null, b ? { id: b.id, values: [b] } : null ) }; } return sortFunction } else if (isArray(order)) { return function(a, b) { return order.indexOf(a.id) - order.indexOf(b.id) } } } else { // if data are grouped, we follow the order of grouped targets var ids = $$.orderTargets($$.data.targets).map(function(i) { return i.id }); // if it was either asc or desc we need to invert the order // returned by orderTargets if ($$.isOrderAsc() || $$.isOrderDesc()) { ids = ids.reverse(); } return function(a, b) { return ids.indexOf(a.id) - ids.indexOf(b.id) } } }; ChartInternal.prototype.getTooltipContent = function( d, defaultTitleFormat, defaultValueFormat, color ) { var $$ = this, config = $$.config, titleFormat = config.tooltip_format_title || defaultTitleFormat, nameFormat = config.tooltip_format_name || function(name) { return name }, text, i, title, value, name, bgcolor; var valueFormat = config.tooltip_format_value; if (!valueFormat) { valueFormat = $$.isTargetNormalized(d.id) ? (v, ratio) => `${(ratio * 100).toFixed(2)}%` : defaultValueFormat; } var tooltipSortFunction = this.getTooltipSortFunction(); if (tooltipSortFunction) { d.sort(tooltipSortFunction); } for (i = 0; i < d.length; i++) { if (!(d[i] && (d[i].value || d[i].value === 0))) { continue } if ($$.isStanfordGraphType()) { // Custom tooltip for stanford plots if (!text) { title = $$.getStanfordTooltipTitle(d[i]); text = "" + title; } bgcolor = $$.getStanfordPointColor(d[i]); name = sanitise(config.data_epochs); // Epochs key name value = d[i].epochs; } else { // Regular tooltip if (!text) { title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x); text = "
" + (title || title === 0 ? "' : ''); } value = sanitise( valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d) ); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue } name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); } } if (value !== undefined) { text += ""; text += "'; text += "'; text += ''; } } return text + '
" + title + '
" + name + '" + value + '
' }; ChartInternal.prototype.tooltipPosition = function( dataToShow, tWidth, tHeight, element ) { var $$ = this, config = $$.config, d3 = $$.d3; var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; var forArc = $$.hasArcType(), mouse = d3.mouse(element); // Determin tooltip position if (forArc) { tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]; tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20; } else { svgLeft = $$.getSvgLeft(true); if (config.axis_rotated) { tooltipLeft = svgLeft + mouse[0] + 100; tooltipRight = tooltipLeft + tWidth; chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = $$.x(dataToShow[0].x) + 20; } else { tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; tooltipRight = tooltipLeft + tWidth; chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = mouse[1] + 15; } if (tooltipRight > chartRight) { // 20 is needed for Firefox to keep tooltip width tooltipLeft -= tooltipRight - chartRight + 20; } if (tooltipTop + tHeight > $$.currentHeight) { tooltipTop -= tHeight + 30; } } if (tooltipTop < 0) { tooltipTop = 0; } return { top: tooltipTop, left: tooltipLeft } }; ChartInternal.prototype.showTooltip = function(selectedData, element) { var $$ = this, config = $$.config; var tWidth, tHeight, position; var forArc = $$.hasArcType(), dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition; if (dataToShow.length === 0 || !config.tooltip_show) { $$.hideTooltip(); return } $$.tooltip .html( config.tooltip_contents.call( $$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color ) ) .style('display', 'block'); // Get tooltip dimensions tWidth = $$.tooltip.property('offsetWidth'); tHeight = $$.tooltip.property('offsetHeight'); position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); // Set tooltip $$.tooltip .style('top', position.top + 'px') .style('left', position.left + 'px'); }; ChartInternal.prototype.hideTooltip = function() { this.tooltip.style('display', 'none'); }; ChartInternal.prototype.setTargetType = function(targetIds, type) { var $$ = this, config = $$.config; $$.mapToTargetIds(targetIds).forEach(function(id) { $$.withoutFadeIn[id] = type === config.data_types[id]; config.data_types[id] = type; }); if (!targetIds) { config.data_type = type; } }; ChartInternal.prototype.hasType = function(type, targets) { var $$ = this, types = $$.config.data_types, has = false; targets = targets || $$.data.targets; if (targets && targets.length) { targets.forEach(function(target) { var t = types[target.id]; if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { has = true; } }); } else if (Object.keys(types).length) { Object.keys(types).forEach(function(id) { if (types[id] === type) { has = true; } }); } else { has = $$.config.data_type === type; } return has }; ChartInternal.prototype.hasArcType = function(targets) { return ( this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets) ) }; ChartInternal.prototype.isLineType = function(d) { var config = this.config, id = isString(d) ? d : d.id; return ( !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf( config.data_types[id] ) >= 0 ) }; ChartInternal.prototype.isStepType = function(d) { var id = isString(d) ? d : d.id; return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0 }; ChartInternal.prototype.isSplineType = function(d) { var id = isString(d) ? d : d.id; return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0 }; ChartInternal.prototype.isAreaType = function(d) { var id = isString(d) ? d : d.id; return ( ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0 ) }; ChartInternal.prototype.isBarType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'bar' }; ChartInternal.prototype.isScatterType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'scatter' }; ChartInternal.prototype.isStanfordType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'stanford' }; ChartInternal.prototype.isPieType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'pie' }; ChartInternal.prototype.isGaugeType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'gauge' }; ChartInternal.prototype.isDonutType = function(d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'donut' }; ChartInternal.prototype.isArcType = function(d) { return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d) }; ChartInternal.prototype.lineData = function(d) { return this.isLineType(d) ? [d] : [] }; ChartInternal.prototype.arcData = function(d) { return this.isArcType(d.data) ? [d] : [] }; /* not used function scatterData(d) { return isScatterType(d) ? d.values : []; } */ ChartInternal.prototype.barData = function(d) { return this.isBarType(d) ? d.values : [] }; ChartInternal.prototype.lineOrScatterOrStanfordData = function(d) { return this.isLineType(d) || this.isScatterType(d) || this.isStanfordType(d) ? d.values : [] }; ChartInternal.prototype.barOrLineData = function(d) { return this.isBarType(d) || this.isLineType(d) ? d.values : [] }; ChartInternal.prototype.isSafari = function() { var ua = window.navigator.userAgent; return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0 }; ChartInternal.prototype.isChrome = function() { var ua = window.navigator.userAgent; return ua.indexOf('Chrome') >= 0 }; ChartInternal.prototype.initZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, startEvent; $$.zoom = d3 .zoom() .on('start', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } startEvent = e; config.zoom_onzoomstart.call($$.api, e); }) .on('zoom', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } $$.redrawForZoom(); config.zoom_onzoom.call($$.api, $$.x.orgDomain()); }) .on('end', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return } // if click, do nothing. otherwise, click interaction will be canceled. if ( e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY ) { return } config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); }); $$.zoom.updateDomain = function() { if (d3.event && d3.event.transform) { $$.x.domain(d3.event.transform.rescaleX($$.subX).domain()); } return this }; $$.zoom.updateExtent = function() { this.scaleExtent([1, Infinity]) .translateExtent([ [0, 0], [$$.width, $$.height] ]) .extent([ [0, 0], [$$.width, $$.height] ]); return this }; $$.zoom.update = function() { return this.updateExtent().updateDomain() }; return $$.zoom.updateExtent() }; ChartInternal.prototype.zoomTransform = function(range) { var $$ = this, s = [$$.x(range[0]), $$.x(range[1])]; return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) }; ChartInternal.prototype.initDragZoom = function() { const $$ = this; const d3 = $$.d3; const config = $$.config; const context = ($$.context = $$.svg); const brushXPos = $$.margin.left + 20.5; const brushYPos = $$.margin.top + 0.5; if (!(config.zoom_type === 'drag' && config.zoom_enabled)) { return } const getZoomedDomain = selection => selection && selection.map(x => $$.x.invert(x)); const brush = ($$.dragZoomBrush = d3 .brushX() .on('start', () => { $$.api.unzoom(); $$.svg.select('.' + CLASS.dragZoom).classed('disabled', false); config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); }) .on('brush', () => { config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection)); }) .on('end', () => { if (d3.event.selection == null) { return } const zoomedDomain = getZoomedDomain(d3.event.selection); if (!config.zoom_disableDefaultBehavior) { $$.api.zoom(zoomedDomain); } $$.svg.select('.' + CLASS.dragZoom).classed('disabled', true); config.zoom_onzoomend.call($$.api, zoomedDomain); })); context .append('g') .classed(CLASS.dragZoom, true) .attr('clip-path', $$.clipPath) .attr('transform', 'translate(' + brushXPos + ',' + brushYPos + ')') .call(brush); }; ChartInternal.prototype.getZoomDomain = function() { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); return [min, max] }; ChartInternal.prototype.redrawForZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x; if (!config.zoom_enabled) { return } if ($$.filterTargetsToShow($$.data.targets).length === 0) { return } zoom.update(); if (config.zoom_disableDefaultBehavior) { return } if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); } $$.redraw({ withTransition: false, withY: config.zoom_rescale, withSubchart: false, withEventRect: false, withDimension: false }); if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') { $$.cancelClick = true; } }; export default c3; ================================================ FILE: docs/js/c3.js ================================================ /* @license C3.js v0.7.20 | (c) C3 Team and other contributors | http://c3js.org/ */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = global || self, global.c3 = factory()); }(this, (function () { 'use strict'; function ChartInternal(api) { var $$ = this; // Note: This part will be replaced by rollup-plugin-modify // When bundling esm output. Beware of changing this line. // TODO: Maybe we should check that the modification by rollup-plugin-modify // is valid during unit tests. $$.d3 = window.d3 ? window.d3 : typeof require !== 'undefined' ? require('d3') : undefined; $$.api = api; $$.config = $$.getDefaultConfig(); $$.data = {}; $$.cache = {}; $$.axes = {}; } /** * The Chart class * * The methods of this class is the public APIs of the chart object. */ function Chart(config) { this.internal = new ChartInternal(this); this.internal.loadConfig(config); this.internal.beforeInit(config); this.internal.init(); this.internal.afterInit(config); (function bindThis(fn, target, argThis) { Object.keys(fn).forEach(function (key) { target[key] = fn[key].bind(argThis); if (Object.keys(fn[key]).length > 0) { bindThis(fn[key], target[key], argThis); } }); })(Chart.prototype, this, this); } var asHalfPixel = function (n) { return Math.ceil(n) + 0.5; }; var ceil10 = function (v) { return Math.ceil(v / 10) * 10; }; var diffDomain = function (d) { return d[1] - d[0]; }; var getOption = function (options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue; }; var getPathBox = function (path) { var box = getBBox(path), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], minX = items[0].x, minY = Math.min(items[0].y, items[1].y); return { x: minX, y: minY, width: box.width, height: box.height }; }; var getBBox = function (element) { try { return element.getBBox(); } catch (ignore) { // Firefox will throw an exception if getBBox() is called whereas the // element is rendered with display:none // See https://github.com/c3js/c3/issues/2692 // The previous code was using `getBoundingClientRect` which was returning // everything at 0 in this case so let's reproduce this behavior here. return { x: 0, y: 0, width: 0, height: 0 }; } }; var hasValue = function (dict, value) { var found = false; Object.keys(dict).forEach(function (key) { if (dict[key] === value) { found = true; } }); return found; }; var isArray = function (o) { return Array.isArray(o); }; var isDefined = function (v) { return typeof v !== 'undefined'; }; var isEmpty = function (o) { return (typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0)); }; var isFunction = function (o) { return typeof o === 'function'; }; var isNumber = function (o) { return typeof o === 'number'; }; var isString = function (o) { return typeof o === 'string'; }; var isUndefined = function (v) { return typeof v === 'undefined'; }; var isValue = function (v) { return v || v === 0; }; var notEmpty = function (o) { return !isEmpty(o); }; var sanitise = function (str) { return typeof str === 'string' ? str.replace(//g, '>') : str; }; var flattenArray = function (arr) { return Array.isArray(arr) ? [].concat.apply([], arr) : []; }; /** * Returns whether the point is within the given box. * * @param {Array} point An [x,y] coordinate * @param {Object} box An object with {x, y, width, height} keys * @param {Number} sensitivity An offset to ease check on very small boxes */ var isWithinBox = function (point, box, sensitivity) { if (sensitivity === void 0) { sensitivity = 0; } var xStart = box.x - sensitivity; var xEnd = box.x + box.width + sensitivity; var yStart = box.y + box.height + sensitivity; var yEnd = box.y - sensitivity; return (xStart < point[0] && point[0] < xEnd && yEnd < point[1] && point[1] < yStart); }; /** * Returns Internet Explorer version number (or false if no Internet Explorer used). * * @param string agent Optional parameter to specify user agent */ var getIEVersion = function (agent) { // https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie if (typeof agent === 'undefined') { agent = window.navigator.userAgent; } var pos = agent.indexOf('MSIE '); // up to IE10 if (pos > 0) { return parseInt(agent.substring(pos + 5, agent.indexOf('.', pos)), 10); } pos = agent.indexOf('Trident/'); // IE11 if (pos > 0) { pos = agent.indexOf('rv:'); return parseInt(agent.substring(pos + 3, agent.indexOf('.', pos)), 10); } return false; }; /** * Returns whether the used browser is Internet Explorer. * * @param version Optional parameter to specify IE version */ var isIE = function (version) { var ver = getIEVersion(); if (typeof version === 'undefined') { return !!ver; } return version === ver; }; function AxisInternal(component, params) { var internal = this; internal.component = component; internal.params = params || {}; internal.d3 = component.d3; internal.scale = internal.d3.scaleLinear(); internal.range; internal.orient = 'bottom'; internal.innerTickSize = 6; internal.outerTickSize = this.params.withOuterTick ? 6 : 0; internal.tickPadding = 3; internal.tickValues = null; internal.tickFormat; internal.tickArguments; internal.tickOffset = 0; internal.tickCulling = true; internal.tickCentered; internal.tickTextCharSize; internal.tickTextRotate = internal.params.tickTextRotate; internal.tickLength; internal.axis = internal.generateAxis(); } AxisInternal.prototype.axisX = function (selection, x, tickOffset) { selection.attr('transform', function (d) { return 'translate(' + Math.ceil(x(d) + tickOffset) + ', 0)'; }); }; AxisInternal.prototype.axisY = function (selection, y) { selection.attr('transform', function (d) { return 'translate(0,' + Math.ceil(y(d)) + ')'; }); }; AxisInternal.prototype.scaleExtent = function (domain) { var start = domain[0], stop = domain[domain.length - 1]; return start < stop ? [start, stop] : [stop, start]; }; AxisInternal.prototype.generateTicks = function (scale) { var internal = this; var i, domain, ticks = []; if (scale.ticks) { return scale.ticks.apply(scale, internal.tickArguments); } domain = scale.domain(); for (i = Math.ceil(domain[0]); i < domain[1]; i++) { ticks.push(i); } if (ticks.length > 0 && ticks[0] > 0) { ticks.unshift(ticks[0] - (ticks[1] - ticks[0])); } return ticks; }; AxisInternal.prototype.copyScale = function () { var internal = this; var newScale = internal.scale.copy(), domain; if (internal.params.isCategory) { domain = internal.scale.domain(); newScale.domain([domain[0], domain[1] - 1]); } return newScale; }; AxisInternal.prototype.textFormatted = function (v) { var internal = this, formatted = internal.tickFormat ? internal.tickFormat(v) : v; return typeof formatted !== 'undefined' ? formatted : ''; }; AxisInternal.prototype.updateRange = function () { var internal = this; internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()); return internal.range; }; AxisInternal.prototype.updateTickTextCharSize = function (tick) { var internal = this; if (internal.tickTextCharSize) { return internal.tickTextCharSize; } var size = { h: 11.5, w: 5.5 }; tick .select('text') .text(function (d) { return internal.textFormatted(d); }) .each(function (d) { var box = getBBox(this), text = internal.textFormatted(d), h = box.height, w = text ? box.width / text.length : undefined; if (h && w) { size.h = h; size.w = w; } }) .text(''); internal.tickTextCharSize = size; return size; }; AxisInternal.prototype.isVertical = function () { return this.orient === 'left' || this.orient === 'right'; }; AxisInternal.prototype.tspanData = function (d, i, scale) { var internal = this; var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d)); if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) { splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax); } return splitted.map(function (s) { return { index: i, splitted: s, length: splitted.length }; }); }; AxisInternal.prototype.splitTickText = function (d, scale) { var internal = this, tickText = internal.textFormatted(d), maxWidth = internal.params.tickWidth, subtext, spaceIndex, textWidth, splitted = []; if (Object.prototype.toString.call(tickText) === '[object Array]') { return tickText; } if (!maxWidth || maxWidth <= 0) { maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110; } function split(splitted, text) { spaceIndex = undefined; for (var i = 1; i < text.length; i++) { if (text.charAt(i) === ' ') { spaceIndex = i; } subtext = text.substr(0, i + 1); textWidth = internal.tickTextCharSize.w * subtext.length; // if text width gets over tick width, split by space index or crrent index if (maxWidth < textWidth) { return split(splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i)); } } return splitted.concat(text); } return split(splitted, tickText + ''); }; AxisInternal.prototype.ellipsify = function (splitted, max) { if (splitted.length <= max) { return splitted; } var ellipsified = splitted.slice(0, max); var remaining = 3; for (var i = max - 1; i >= 0; i--) { var available = ellipsified[i].length; ellipsified[i] = ellipsified[i] .substr(0, available - remaining) .padEnd(available, '.'); remaining -= available; if (remaining <= 0) { break; } } return ellipsified; }; AxisInternal.prototype.updateTickLength = function () { var internal = this; internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding; }; AxisInternal.prototype.lineY2 = function (d) { var internal = this, tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset); return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0; }; AxisInternal.prototype.textY = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength; }; AxisInternal.prototype.textTransform = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 'rotate(' + rotate + ')' : ''; }; AxisInternal.prototype.textTextAnchor = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? (rotate > 0 ? 'start' : 'end') : 'middle'; }; AxisInternal.prototype.tspanDx = function () { var internal = this, rotate = internal.tickTextRotate; return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0; }; AxisInternal.prototype.tspanDy = function (d, i) { var internal = this, dy = internal.tickTextCharSize.h; if (i === 0) { if (internal.isVertical()) { dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3); } else { dy = '.71em'; } } return dy; }; AxisInternal.prototype.generateAxis = function () { var internal = this, d3 = internal.d3, params = internal.params; function axis(g, transition) { var self; g.each(function () { var g = (axis.g = d3.select(this)); var scale0 = this.__chart__ || internal.scale, scale1 = (this.__chart__ = internal.copyScale()); var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), ticks = g.selectAll('.tick').data(ticksValues, scale1), tickEnter = ticks .enter() .insert('g', '.domain') .attr('class', 'tick') .style('opacity', 1e-6), // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. tickExit = ticks.exit().remove(), tickUpdate = ticks.merge(tickEnter), tickTransform, tickX, tickY; if (params.isCategory) { internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2); tickX = internal.tickCentered ? 0 : internal.tickOffset; tickY = internal.tickCentered ? internal.tickOffset : 0; } else { internal.tickOffset = tickX = 0; } internal.updateRange(); internal.updateTickLength(); internal.updateTickTextCharSize(g.select('.tick')); var lineUpdate = tickUpdate .select('line') .merge(tickEnter.append('line')), textUpdate = tickUpdate.select('text').merge(tickEnter.append('text')); var tspans = tickUpdate .selectAll('text') .selectAll('tspan') .data(function (d, i) { return internal.tspanData(d, i, scale1); }), tspanEnter = tspans.enter().append('tspan'), tspanUpdate = tspanEnter.merge(tspans).text(function (d) { return d.splitted; }); tspans.exit().remove(); var path = g.selectAll('.domain').data([0]), pathUpdate = path .enter() .append('path') .merge(path) .attr('class', 'domain'); // TODO: each attr should be one function and change its behavior by internal.orient, probably switch (internal.orient) { case 'bottom': { tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function (d, i) { return internal.lineY2(d, i); }); textUpdate .attr('x', 0) .attr('y', function (d, i) { return internal.textY(d, i); }) .attr('transform', function (d, i) { return internal.textTransform(d, i); }) .style('text-anchor', function (d, i) { return internal.textTextAnchor(d, i); }); tspanUpdate .attr('x', 0) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }) .attr('dx', function (d, i) { return internal.tspanDx(d, i); }); pathUpdate.attr('d', 'M' + internal.range[0] + ',' + internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + internal.outerTickSize); break; } case 'top': { // TODO: rotated tick text tickTransform = internal.axisX; lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function (d, i) { return -1 * internal.lineY2(d, i); }); textUpdate .attr('x', 0) .attr('y', function (d, i) { return (-1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2)); }) .attr('transform', function (d, i) { return internal.textTransform(d, i); }) .style('text-anchor', function (d, i) { return internal.textTextAnchor(d, i); }); tspanUpdate .attr('x', 0) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }) .attr('dx', function (d, i) { return internal.tspanDx(d, i); }); pathUpdate.attr('d', 'M' + internal.range[0] + ',' + -internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + -internal.outerTickSize); break; } case 'left': { tickTransform = internal.axisY; lineUpdate .attr('x2', -internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', -internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'end'); tspanUpdate .attr('x', -internal.tickLength) .attr('dy', function (d, i) { return internal.tspanDy(d, i); }); pathUpdate.attr('d', 'M' + -internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + -internal.outerTickSize); break; } case 'right': { tickTransform = internal.axisY; lineUpdate .attr('x2', internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY); textUpdate .attr('x', internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'start'); tspanUpdate.attr('x', internal.tickLength).attr('dy', function (d, i) { return internal.tspanDy(d, i); }); pathUpdate.attr('d', 'M' + internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + internal.outerTickSize); break; } } if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2; scale0 = scale1 = function (d) { return x(d) + dx; }; } else if (scale0.rangeBand) { scale0 = scale1; } else { tickExit.call(tickTransform, scale1, internal.tickOffset); } tickEnter.call(tickTransform, scale0, internal.tickOffset); self = (transition ? tickUpdate.transition(transition) : tickUpdate) .style('opacity', 1) .call(tickTransform, scale1, internal.tickOffset); }); return self; } axis.scale = function (x) { if (!arguments.length) { return internal.scale; } internal.scale = x; return axis; }; axis.orient = function (x) { if (!arguments.length) { return internal.orient; } internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + '' : 'bottom'; return axis; }; axis.tickFormat = function (format) { if (!arguments.length) { return internal.tickFormat; } internal.tickFormat = format; return axis; }; axis.tickCentered = function (isCentered) { if (!arguments.length) { return internal.tickCentered; } internal.tickCentered = isCentered; return axis; }; axis.tickOffset = function () { return internal.tickOffset; }; axis.tickInterval = function () { var interval, length; if (params.isCategory) { interval = internal.tickOffset * 2; } else { length = axis.g .select('path.domain') .node() .getTotalLength() - internal.outerTickSize * 2; interval = length / axis.g.selectAll('line').size(); } return interval === Infinity ? 0 : interval; }; axis.ticks = function () { if (!arguments.length) { return internal.tickArguments; } internal.tickArguments = arguments; return axis; }; axis.tickCulling = function (culling) { if (!arguments.length) { return internal.tickCulling; } internal.tickCulling = culling; return axis; }; axis.tickValues = function (x) { if (typeof x === 'function') { internal.tickValues = function () { return x(internal.scale.domain()); }; } else { if (!arguments.length) { return internal.tickValues; } internal.tickValues = x; } return axis; }; return axis; }; var CLASS = { target: 'c3-target', chart: 'c3-chart', chartLine: 'c3-chart-line', chartLines: 'c3-chart-lines', chartBar: 'c3-chart-bar', chartBars: 'c3-chart-bars', chartText: 'c3-chart-text', chartTexts: 'c3-chart-texts', chartArc: 'c3-chart-arc', chartArcs: 'c3-chart-arcs', chartArcsTitle: 'c3-chart-arcs-title', chartArcsBackground: 'c3-chart-arcs-background', chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', selectedCircle: 'c3-selected-circle', selectedCircles: 'c3-selected-circles', eventRect: 'c3-event-rect', eventRects: 'c3-event-rects', eventRectsSingle: 'c3-event-rects-single', eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', regions: 'c3-regions', title: 'c3-title', tooltipContainer: 'c3-tooltip-container', tooltip: 'c3-tooltip', tooltipName: 'c3-tooltip-name', shape: 'c3-shape', shapes: 'c3-shapes', line: 'c3-line', lines: 'c3-lines', bar: 'c3-bar', bars: 'c3-bars', circle: 'c3-circle', circles: 'c3-circles', arc: 'c3-arc', arcLabelLine: 'c3-arc-label-line', arcs: 'c3-arcs', area: 'c3-area', areas: 'c3-areas', empty: 'c3-empty', text: 'c3-text', texts: 'c3-texts', gaugeValue: 'c3-gauge-value', grid: 'c3-grid', gridLines: 'c3-grid-lines', xgrid: 'c3-xgrid', xgrids: 'c3-xgrids', xgridLine: 'c3-xgrid-line', xgridLines: 'c3-xgrid-lines', xgridFocus: 'c3-xgrid-focus', ygrid: 'c3-ygrid', ygrids: 'c3-ygrids', ygridLine: 'c3-ygrid-line', ygridLines: 'c3-ygrid-lines', colorScale: 'c3-colorscale', stanfordElements: 'c3-stanford-elements', stanfordLine: 'c3-stanford-line', stanfordLines: 'c3-stanford-lines', stanfordRegion: 'c3-stanford-region', stanfordRegions: 'c3-stanford-regions', stanfordText: 'c3-stanford-text', stanfordTexts: 'c3-stanford-texts', axis: 'c3-axis', axisX: 'c3-axis-x', axisXLabel: 'c3-axis-x-label', axisY: 'c3-axis-y', axisYLabel: 'c3-axis-y-label', axisY2: 'c3-axis-y2', axisY2Label: 'c3-axis-y2-label', legendBackground: 'c3-legend-background', legendItem: 'c3-legend-item', legendItemEvent: 'c3-legend-item-event', legendItemTile: 'c3-legend-item-tile', legendItemHidden: 'c3-legend-item-hidden', legendItemFocused: 'c3-legend-item-focused', dragarea: 'c3-dragarea', EXPANDED: '_expanded_', SELECTED: '_selected_', INCLUDED: '_included_' }; var AxisClass = /** @class */ (function () { function AxisClass(owner) { this.owner = owner; this.d3 = owner.d3; this.internal = AxisInternal; } return AxisClass; }()); var Axis = AxisClass; Axis.prototype.init = function init() { var $$ = this.owner, config = $$.config, main = $$.main; $$.axes.x = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisX) .attr('clip-path', config.axis_x_inner ? '' : $$.clipPathForXAxis) .attr('transform', $$.getTranslate('x')) .style('visibility', config.axis_x_show ? 'visible' : 'hidden'); $$.axes.x .append('text') .attr('class', CLASS.axisXLabel) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .style('text-anchor', this.textAnchorForXAxisLabel.bind(this)); $$.axes.y = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY) .attr('clip-path', config.axis_y_inner ? '' : $$.clipPathForYAxis) .attr('transform', $$.getTranslate('y')) .style('visibility', config.axis_y_show ? 'visible' : 'hidden'); $$.axes.y .append('text') .attr('class', CLASS.axisYLabel) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForYAxisLabel.bind(this)); $$.axes.y2 = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY2) // clip-path? .attr('transform', $$.getTranslate('y2')) .style('visibility', config.axis_y2_show ? 'visible' : 'hidden'); $$.axes.y2 .append('text') .attr('class', CLASS.axisY2Label) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForY2AxisLabel.bind(this)); }; Axis.prototype.getXAxis = function getXAxis(scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { var $$ = this.owner, config = $$.config, axisParams = { isCategory: $$.isCategorized(), withOuterTick: withOuterTick, tickMultiline: config.axis_x_tick_multiline, tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0, tickWidth: config.axis_x_tick_width, tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, withoutTransition: withoutTransition }, axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient); if ($$.isTimeSeries() && tickValues && typeof tickValues !== 'function') { tickValues = tickValues.map(function (v) { return $$.parseDate(v); }); } // Set tick axis.tickFormat(tickFormat).tickValues(tickValues); if ($$.isCategorized()) { axis.tickCentered(config.axis_x_tick_centered); if (isEmpty(config.axis_x_tick_culling)) { config.axis_x_tick_culling = false; } } return axis; }; Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues(targets, axis) { var $$ = this.owner, config = $$.config, tickValues; if (config.axis_x_tick_fit || config.axis_x_tick_count) { tickValues = this.generateTickValues($$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries()); } if (axis) { axis.tickValues(tickValues); } else { $$.xAxis.tickValues(tickValues); $$.subXAxis.tickValues(tickValues); } return tickValues; }; Axis.prototype.getYAxis = function getYAxis(axisId, scale, orient, tickValues, withOuterTick, withoutTransition, withoutRotateTickText) { var $$ = this.owner; var config = $$.config; var tickFormat = config["axis_" + axisId + "_tick_format"]; if (!tickFormat && $$.isAxisNormalized(axisId)) { tickFormat = function (x) { return x + "%"; }; } var axis = new this.internal(this, { withOuterTick: withOuterTick, withoutTransition: withoutTransition, tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate }).axis .scale(scale) .orient(orient); if (tickFormat) { axis.tickFormat(tickFormat); } if ($$.isTimeSeriesY()) { axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval); } else { axis.tickValues(tickValues); } return axis; }; Axis.prototype.getId = function getId(id) { var config = this.owner.config; return id in config.data_axes ? config.data_axes[id] : 'y'; }; Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { // #2251 previously set any negative values to a whole number, // however both should be truncated according to the users format specification var $$ = this.owner, config = $$.config; var format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function (v) { return v; }; if (config.axis_x_tick_format) { if (isFunction(config.axis_x_tick_format)) { format = config.axis_x_tick_format; } else if ($$.isTimeSeries()) { format = function (date) { return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : ''; }; } } return isFunction(format) ? function (v) { return format.call($$, v); } : format; }; Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { return tickValues ? tickValues : axis ? axis.tickValues() : undefined; }; Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { return this.getTickValues(this.owner.config.axis_x_tick_values, this.owner.xAxis); }; Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { return this.getTickValues(this.owner.config.axis_y_tick_values, this.owner.yAxis); }; Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { return this.getTickValues(this.owner.config.axis_y2_tick_values, this.owner.y2Axis); }; Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId(axisId) { var $$ = this.owner, config = $$.config, option; if (axisId === 'y') { option = config.axis_y_label; } else if (axisId === 'y2') { option = config.axis_y2_label; } else if (axisId === 'x') { option = config.axis_x_label; } return option; }; Axis.prototype.getLabelText = function getLabelText(axisId) { var option = this.getLabelOptionByAxisId(axisId); return isString(option) ? option : option ? option.text : null; }; Axis.prototype.setLabelText = function setLabelText(axisId, text) { var $$ = this.owner, config = $$.config, option = this.getLabelOptionByAxisId(axisId); if (isString(option)) { if (axisId === 'y') { config.axis_y_label = text; } else if (axisId === 'y2') { config.axis_y2_label = text; } else if (axisId === 'x') { config.axis_x_label = text; } } else if (option) { option.text = text; } }; Axis.prototype.getLabelPosition = function getLabelPosition(axisId, defaultPosition) { var option = this.getLabelOptionByAxisId(axisId), position = option && typeof option === 'object' && option.position ? option.position : defaultPosition; return { isInner: position.indexOf('inner') >= 0, isOuter: position.indexOf('outer') >= 0, isLeft: position.indexOf('left') >= 0, isCenter: position.indexOf('center') >= 0, isRight: position.indexOf('right') >= 0, isTop: position.indexOf('top') >= 0, isMiddle: position.indexOf('middle') >= 0, isBottom: position.indexOf('bottom') >= 0 }; }; Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { return this.getLabelPosition('x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right'); }; Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { return this.getLabelPosition('y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); }; Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { return this.getLabelPosition('y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top'); }; Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition(); }; Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { return this.getLabelText('x'); }; Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { return this.getLabelText('y'); }; Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { return this.getLabelText('y2'); }; Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { var $$ = this.owner; if (forHorizontal) { return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width; } else { return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0; } }; Axis.prototype.dxForAxisLabel = function dxForAxisLabel(forHorizontal, position) { if (forHorizontal) { return position.isLeft ? '0.5em' : position.isRight ? '-0.5em' : '0'; } else { return position.isTop ? '-0.5em' : position.isBottom ? '0.5em' : '0'; } }; Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel(forHorizontal, position) { if (forHorizontal) { return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end'; } else { return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end'; } }; Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { return this.xForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { return this.xForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { return this.xForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { return this.dxForAxisLabel(!this.owner.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { return this.dxForAxisLabel(this.owner.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { var $$ = this.owner, config = $$.config, position = this.getXAxisLabelPosition(); if (config.axis_rotated) { return position.isInner ? '1.2em' : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x')); } else { return position.isInner ? '-0.5em' : $$.getHorizontalAxisHeight('x') - 10; } }; Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { var $$ = this.owner, position = this.getYAxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '-0.5em' : '3em'; } else { return position.isInner ? '1.2em' : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10); } }; Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { var $$ = this.owner, position = this.getY2AxisLabelPosition(); if ($$.config.axis_rotated) { return position.isInner ? '1.2em' : '-2.2em'; } else { return position.isInner ? '-0.5em' : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15); } }; Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel(!$$.config.axis_rotated, this.getXAxisLabelPosition()); }; Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getYAxisLabelPosition()); }; Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { var $$ = this.owner; return this.textAnchorForAxisLabel($$.config.axis_rotated, this.getY2AxisLabelPosition()); }; Axis.prototype.getMaxTickWidth = function getMaxTickWidth(id, withoutRecompute) { var $$ = this.owner, maxWidth = 0, targetsToShow, scale, axis, dummy, svg; if (withoutRecompute && $$.currentMaxTickWidths[id]) { return $$.currentMaxTickWidths[id]; } if ($$.svg) { targetsToShow = $$.filterTargetsToShow($$.data.targets); if (id === 'y') { scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')); axis = this.getYAxis(id, scale, $$.yOrient, $$.yAxisTickValues, false, true, true); } else if (id === 'y2') { scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')); axis = this.getYAxis(id, scale, $$.y2Orient, $$.y2AxisTickValues, false, true, true); } else { scale = $$.x.copy().domain($$.getXDomain(targetsToShow)); axis = this.getXAxis(scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true); this.updateXAxisTickValues(targetsToShow, axis); } dummy = $$.d3 .select('body') .append('div') .classed('c3', true); (svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0)), svg .append('g') .call(axis) .each(function () { $$.d3 .select(this) .selectAll('text') .each(function () { var box = getBBox(this); if (maxWidth < box.width) { maxWidth = box.width; } }); dummy.remove(); }); } $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth; return $$.currentMaxTickWidths[id]; }; Axis.prototype.updateLabels = function updateLabels(withTransition) { var $$ = this.owner; var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label); (withTransition ? axisXLabel.transition() : axisXLabel) .attr('x', this.xForXAxisLabel.bind(this)) .attr('dx', this.dxForXAxisLabel.bind(this)) .attr('dy', this.dyForXAxisLabel.bind(this)) .text(this.textForXAxisLabel.bind(this)); (withTransition ? axisYLabel.transition() : axisYLabel) .attr('x', this.xForYAxisLabel.bind(this)) .attr('dx', this.dxForYAxisLabel.bind(this)) .attr('dy', this.dyForYAxisLabel.bind(this)) .text(this.textForYAxisLabel.bind(this)); (withTransition ? axisY2Label.transition() : axisY2Label) .attr('x', this.xForY2AxisLabel.bind(this)) .attr('dx', this.dxForY2AxisLabel.bind(this)) .attr('dy', this.dyForY2AxisLabel.bind(this)) .text(this.textForY2AxisLabel.bind(this)); }; Axis.prototype.getPadding = function getPadding(padding, key, defaultValue, domainLength) { var p = typeof padding === 'number' ? padding : padding[key]; if (!isValue(p)) { return defaultValue; } if (padding.unit === 'ratio') { return padding[key] * domainLength; } // assume padding is pixels if unit is not specified return this.convertPixelsToAxisPadding(p, domainLength); }; Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding(pixels, domainLength) { var $$ = this.owner, length = $$.config.axis_rotated ? $$.width : $$.height; return domainLength * (pixels / length); }; Axis.prototype.generateTickValues = function generateTickValues(values, tickCount, forTimeSeries) { var tickValues = values, targetCount, start, end, count, interval, i, tickValue; if (tickCount) { targetCount = isFunction(tickCount) ? tickCount() : tickCount; // compute ticks according to tickCount if (targetCount === 1) { tickValues = [values[0]]; } else if (targetCount === 2) { tickValues = [values[0], values[values.length - 1]]; } else if (targetCount > 2) { count = targetCount - 2; start = values[0]; end = values[values.length - 1]; interval = (end - start) / (count + 1); // re-construct unique values tickValues = [start]; for (i = 0; i < count; i++) { tickValue = +start + interval * (i + 1); tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue); } tickValues.push(end); } } if (!forTimeSeries) { tickValues = tickValues.sort(function (a, b) { return a - b; }); } return tickValues; }; Axis.prototype.generateTransitions = function generateTransitions(duration) { var $$ = this.owner, axes = $$.axes; return { axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx }; }; Axis.prototype.redraw = function redraw(duration, isHidden) { var $$ = this.owner, transition = duration ? $$.d3.transition().duration(duration) : null; $$.axes.x.style('opacity', isHidden ? 0 : 1).call($$.xAxis, transition); $$.axes.y.style('opacity', isHidden ? 0 : 1).call($$.yAxis, transition); $$.axes.y2.style('opacity', isHidden ? 0 : 1).call($$.y2Axis, transition); $$.axes.subx.style('opacity', isHidden ? 0 : 1).call($$.subXAxis, transition); }; var c3 = { version: '0.7.20', chart: { fn: Chart.prototype, internal: { fn: ChartInternal.prototype, axis: { fn: AxisClass.prototype, internal: { fn: AxisInternal.prototype } } } }, generate: function (config) { return new Chart(config); } }; ChartInternal.prototype.beforeInit = function () { // can do something }; ChartInternal.prototype.afterInit = function () { // can do something }; ChartInternal.prototype.init = function () { var $$ = this, config = $$.config; $$.initParams(); if (config.data_url) { $$.convertUrlToData(config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData); } else if (config.data_json) { $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)); } else if (config.data_rows) { $$.initWithData($$.convertRowsToData(config.data_rows)); } else if (config.data_columns) { $$.initWithData($$.convertColumnsToData(config.data_columns)); } else { throw Error('url or json or rows or columns is required.'); } }; ChartInternal.prototype.initParams = function () { var $$ = this, d3 = $$.d3, config = $$.config; // MEMO: clipId needs to be unique because it conflicts when multiple charts exist $$.clipId = 'c3-' + new Date().valueOf() + '-clip'; $$.clipIdForXAxis = $$.clipId + '-xaxis'; $$.clipIdForYAxis = $$.clipId + '-yaxis'; $$.clipIdForGrid = $$.clipId + '-grid'; $$.clipIdForSubchart = $$.clipId + '-subchart'; $$.clipPath = $$.getClipPath($$.clipId); $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis); $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis); $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid); $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart); $$.dragStart = null; $$.dragging = false; $$.flowing = false; $$.cancelClick = false; $$.mouseover = undefined; $$.transiting = false; $$.color = $$.generateColor(); $$.levelColor = $$.generateLevelColor(); $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)($$.config.data_xFormat); $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat; $$.defaultAxisTimeFormat = function (date) { if (date.getMilliseconds()) { return d3.timeFormat('.%L')(date); } if (date.getSeconds()) { return d3.timeFormat(':%S')(date); } if (date.getMinutes()) { return d3.timeFormat('%I:%M')(date); } if (date.getHours()) { return d3.timeFormat('%I %p')(date); } if (date.getDay() && date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date); } if (date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date); } if (date.getMonth()) { return d3.timeFormat('%-m/%-d')(date); } return d3.timeFormat('%Y/%-m/%-d')(date); }; $$.hiddenTargetIds = []; $$.hiddenLegendIds = []; $$.focusedTargetIds = []; $$.defocusedTargetIds = []; $$.xOrient = config.axis_rotated ? config.axis_x_inner ? 'right' : 'left' : config.axis_x_inner ? 'top' : 'bottom'; $$.yOrient = config.axis_rotated ? config.axis_y_inner ? 'top' : 'bottom' : config.axis_y_inner ? 'right' : 'left'; $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? 'bottom' : 'top' : config.axis_y2_inner ? 'left' : 'right'; $$.subXOrient = config.axis_rotated ? 'left' : 'bottom'; $$.isLegendRight = config.legend_position === 'right'; $$.isLegendInset = config.legend_position === 'inset'; $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right'; $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left'; $$.legendStep = 0; $$.legendItemWidth = 0; $$.legendItemHeight = 0; $$.currentMaxTickWidths = { x: 0, y: 0, y2: 0 }; $$.rotated_padding_left = 30; $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30; $$.rotated_padding_top = 5; $$.withoutFadeIn = {}; $$.intervalForObserveInserted = undefined; $$.axes.subx = d3.selectAll([]); // needs when excluding subchart.js }; ChartInternal.prototype.initChartElements = function () { if (this.initBar) { this.initBar(); } if (this.initLine) { this.initLine(); } if (this.initArc) { this.initArc(); } if (this.initGauge) { this.initGauge(); } if (this.initText) { this.initText(); } }; ChartInternal.prototype.initWithData = function (data) { var $$ = this, d3 = $$.d3, config = $$.config; var defs, main, binding = true; $$.axis = new AxisClass($$); if (!config.bindto) { $$.selectChart = d3.selectAll([]); } else if (typeof config.bindto.node === 'function') { $$.selectChart = config.bindto; } else { $$.selectChart = d3.select(config.bindto); } if ($$.selectChart.empty()) { $$.selectChart = d3 .select(document.createElement('div')) .style('opacity', 0); $$.observeInserted($$.selectChart); binding = false; } $$.selectChart.html('').classed('c3', true); // Init data as targets $$.data.xs = {}; $$.data.targets = $$.convertDataToTargets(data); if (config.data_filter) { $$.data.targets = $$.data.targets.filter(config.data_filter); } // Set targets to hide if needed if (config.data_hide) { $$.addHiddenTargetIds(config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide); } if (config.legend_hide) { $$.addHiddenLegendIds(config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide); } if ($$.isStanfordGraphType()) { $$.initStanfordData(); } // Init sizes and scales $$.updateSizes(); $$.updateScales(); // Set domains for each scale $$.x.domain(d3.extent($$.getXDomain($$.data.targets))); $$.y.domain($$.getYDomain($$.data.targets, 'y')); $$.y2.domain($$.getYDomain($$.data.targets, 'y2')); $$.subX.domain($$.x.domain()); $$.subY.domain($$.y.domain()); $$.subY2.domain($$.y2.domain()); // Save original x domain for zoom update $$.orgXDomain = $$.x.domain(); /*-- Basic Elements --*/ // Define svgs $$.svg = $$.selectChart .append('svg') .style('overflow', 'hidden') .on('mouseenter', function () { return config.onmouseover.call($$); }) .on('mouseleave', function () { return config.onmouseout.call($$); }); if ($$.config.svg_classname) { $$.svg.attr('class', $$.config.svg_classname); } // Define defs defs = $$.svg.append('defs'); $$.clipChart = $$.appendClip(defs, $$.clipId); $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis); $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis); $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid); $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart); $$.updateSvgSize(); // Define regions main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main')); if ($$.initPie) { $$.initPie(); } if ($$.initDragZoom) { $$.initDragZoom(); } if (config.subchart_show && $$.initSubchart) { $$.initSubchart(); } if ($$.initTooltip) { $$.initTooltip(); } if ($$.initLegend) { $$.initLegend(); } if ($$.initTitle) { $$.initTitle(); } if ($$.initZoom) { $$.initZoom(); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // Update selection based on size and scale // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart. if (config.subchart_show && $$.initSubchartBrush) { $$.initSubchartBrush(); } /*-- Main Region --*/ // text when empty main .append('text') .attr('class', CLASS.text + ' ' + CLASS.empty) .attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers. .attr('dominant-baseline', 'middle'); // vertical centering of text at y position in all browsers, except IE. // Regions $$.initRegion(); // Grids $$.initGrid(); // Define g for chart area main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.chart); // Grid lines if (config.grid_lines_front) { $$.initGridLines(); } $$.initStanfordElements(); // Cover whole with rects for events $$.initEventRect(); // Define g for chart $$.initChartElements(); // Add Axis $$.axis.init(); // Set targets $$.updateTargets($$.data.targets); // Set default extent if defined if (config.axis_x_selection) { $$.brush.selectionAsValue($$.getDefaultSelection()); } // Draw with targets if (binding) { $$.updateDimension(); $$.config.oninit.call($$); $$.redraw({ withTransition: false, withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransitionForAxis: false }); } // Bind to resize event $$.bindResize(); // Bind to window focus event $$.bindWindowFocus(); // export element of the chart $$.api.element = $$.selectChart.node(); }; ChartInternal.prototype.smoothLines = function (el, type) { var $$ = this; if (type === 'grid') { el.each(function () { var g = $$.d3.select(this), x1 = g.attr('x1'), x2 = g.attr('x2'), y1 = g.attr('y1'), y2 = g.attr('y2'); g.attr({ x1: Math.ceil(x1), x2: Math.ceil(x2), y1: Math.ceil(y1), y2: Math.ceil(y2) }); }); } }; ChartInternal.prototype.updateSizes = function () { var $$ = this, config = $$.config; var legendHeight = $$.legend ? $$.getLegendHeight() : 0, legendWidth = $$.legend ? $$.getLegendWidth() : 0, legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, hasArc = $$.hasArcType(), xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), subchartXAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x', true), subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + subchartXAxisHeight : 0; $$.currentWidth = $$.getCurrentWidth(); $$.currentHeight = $$.getCurrentHeight(); // for main $$.margin = config.axis_rotated ? { top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) } : { top: 4 + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: hasArc ? 0 : $$.getCurrentPaddingLeft() }; // for subchart $$.margin2 = config.axis_rotated ? { top: $$.margin.top, right: NaN, bottom: 20 + legendHeightForBottom, left: $$.rotated_padding_left } : { top: $$.currentHeight - subchartHeight - legendHeightForBottom, right: NaN, bottom: subchartXAxisHeight + legendHeightForBottom, left: $$.margin.left }; // for legend $$.margin3 = { top: 0, right: NaN, bottom: 0, left: 0 }; if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth); } $$.width = $$.currentWidth - $$.margin.left - $$.margin.right; $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom; if ($$.width < 0) { $$.width = 0; } if ($$.height < 0) { $$.height = 0; } $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width; $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom; if ($$.width2 < 0) { $$.width2 = 0; } if ($$.height2 < 0) { $$.height2 = 0; } // for arc $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0); $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10); if ($$.hasType('gauge') && !config.gauge_fullCircle) { $$.arcHeight += $$.height - $$.getGaugeLabelHeight(); } if ($$.updateRadius) { $$.updateRadius(); } if ($$.isLegendRight && hasArc) { $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1; } }; ChartInternal.prototype.updateTargets = function (targets) { var $$ = this, config = $$.config; /*-- Main --*/ //-- Text --// $$.updateTargetsForText(targets); //-- Bar --// $$.updateTargetsForBar(targets); //-- Line --// $$.updateTargetsForLine(targets); //-- Arc --// if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets); } /*-- Sub --*/ if (config.subchart_show && $$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets); } // Fade-in each chart $$.showTargets(); }; ChartInternal.prototype.showTargets = function () { var $$ = this; $$.svg .selectAll('.' + CLASS.target) .filter(function (d) { return $$.isTargetToShow(d.id); }) .transition() .duration($$.config.transition_duration) .style('opacity', 1); }; ChartInternal.prototype.redraw = function (options, transitions) { var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config; var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType); var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis; var hideAxis = $$.hasArcType(); var drawArea, drawBar, drawLine, xForText, yForText; var duration, durationForExit, durationForAxis; var transitionsToWait, waitForDraw, flow, transition; var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom; var xv = $$.xv.bind($$), cx, cy; options = options || {}; withY = getOption(options, 'withY', true); withSubchart = getOption(options, 'withSubchart', true); withTransition = getOption(options, 'withTransition', true); withTransform = getOption(options, 'withTransform', false); withUpdateXDomain = getOption(options, 'withUpdateXDomain', false); withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', false); withTrimXDomain = getOption(options, 'withTrimXDomain', true); withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain); withLegend = getOption(options, 'withLegend', false); withEventRect = getOption(options, 'withEventRect', true); withDimension = getOption(options, 'withDimension', true); withTransitionForExit = getOption(options, 'withTransitionForExit', withTransition); withTransitionForAxis = getOption(options, 'withTransitionForAxis', withTransition); duration = withTransition ? config.transition_duration : 0; durationForExit = withTransitionForExit ? duration : 0; durationForAxis = withTransitionForAxis ? duration : 0; transitions = transitions || $$.axis.generateTransitions(durationForAxis); // update legend and transform each g if (withLegend && config.legend_show) { $$.updateLegend($$.mapToIds($$.data.targets), options, transitions); } else if (withDimension) { // need to update dimension (e.g. axis.y.tick.values) because y tick values should change // no need to update axis in it because they will be updated in redraw() $$.updateDimension(true); } // MEMO: needed for grids calculation if ($$.isCategorized() && targetsToShow.length === 0) { $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]); } if (targetsToShow.length) { $$.updateXDomain(targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain); if (!config.axis_x_tick_values) { tickValues = $$.axis.updateXAxisTickValues(targetsToShow); } } else { $$.xAxis.tickValues([]); $$.subXAxis.tickValues([]); } if (config.zoom_rescale && !options.flow) { xDomainForZoom = $$.x.orgDomain(); } $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)); $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)); if (!config.axis_y_tick_values && config.axis_y_tick_count) { $$.yAxis.tickValues($$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count)); } if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { $$.y2Axis.tickValues($$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count)); } // axes $$.axis.redraw(durationForAxis, hideAxis); // Update axis label $$.axis.updateLabels(withTransition); // show/hide if manual culling needed if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if (config.axis_x_tick_culling && tickValues) { for (i = 1; i < tickValues.length; i++) { if (tickValues.length / i < config.axis_x_tick_culling_max) { intervalForCulling = i; break; } } $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function (e) { var index = tickValues.indexOf(e); if (index >= 0) { d3.select(this).style('display', index % intervalForCulling ? 'none' : 'block'); } }); } else { $$.svg .selectAll('.' + CLASS.axisX + ' .tick text') .style('display', 'block'); } } // setup drawer - MEMO: these must be called after axis updated drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined; drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined; drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined; xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true); yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false); // update circleY based on updated parameters $$.updateCircleY(); // generate circle x/y functions depending on updated params cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$); cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$); // Update sub domain if (withY) { $$.subY.domain($$.getYDomain(targetsToShow, 'y')); $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')); } // xgrid focus $$.updateXgridFocus(); // Data empty label positioning and text. main .select('text.' + CLASS.text + '.' + CLASS.empty) .attr('x', $$.width / 2) .attr('y', $$.height / 2) .text(config.data_empty_label_text) .transition() .style('opacity', targetsToShow.length ? 0 : 1); // event rect if (withEventRect) { $$.redrawEventRect(); } // grid $$.updateGrid(duration); $$.updateStanfordElements(duration); // rect for regions $$.updateRegion(duration); // bars $$.updateBar(durationForExit); // lines, areas and circles $$.updateLine(durationForExit); $$.updateArea(durationForExit); $$.updateCircle(cx, cy); // text if ($$.hasDataLabel()) { $$.updateText(xForText, yForText, durationForExit); } // title if ($$.redrawTitle) { $$.redrawTitle(); } // arc if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform); } // subchart if (config.subchart_show && $$.redrawSubchart) { $$.redrawSubchart(withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices); } if ($$.isStanfordGraphType()) { $$.drawColorScale(); } // circles for select main .selectAll('.' + CLASS.selectedCircles) .filter($$.isBarType.bind($$)) .selectAll('circle') .remove(); if (options.flow) { flow = $$.generateFlow({ targets: targetsToShow, flow: options.flow, duration: options.flow.duration, drawBar: drawBar, drawLine: drawLine, drawArea: drawArea, cx: cx, cy: cy, xv: xv, xForText: xForText, yForText: yForText }); } if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. // transition should be derived from one transition transition = d3.transition().duration(duration); transitionsToWait = []; [ $$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition) ].forEach(function (transitions) { transitions.forEach(function (transition) { transitionsToWait.push(transition); }); }); // Wait for end of transitions to call flow and onrendered callback waitForDraw = $$.generateWait(); transitionsToWait.forEach(function (t) { waitForDraw.add(t); }); waitForDraw(function () { if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } }); } else { $$.redrawBar(drawBar); $$.redrawLine(drawLine); $$.redrawArea(drawArea); $$.redrawCircle(cx, cy); $$.redrawText(xForText, yForText, options.flow); $$.redrawRegion(); $$.redrawGrid(); if (flow) { flow(); } if (config.onrendered) { config.onrendered.call($$); } } // update fadein condition $$.mapToIds($$.data.targets).forEach(function (id) { $$.withoutFadeIn[id] = true; }); }; ChartInternal.prototype.updateAndRedraw = function (options) { var $$ = this, config = $$.config, transitions; options = options || {}; // same with redraw options.withTransition = getOption(options, 'withTransition', true); options.withTransform = getOption(options, 'withTransform', false); options.withLegend = getOption(options, 'withLegend', false); // NOT same with redraw options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true); options.withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', true); options.withTransitionForExit = false; options.withTransitionForTransform = getOption(options, 'withTransitionForTransform', options.withTransition); // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) $$.updateSizes(); // MEMO: called in updateLegend in redraw if withLegend if (!(options.withLegend && config.legend_show)) { transitions = $$.axis.generateTransitions(options.withTransitionForAxis ? config.transition_duration : 0); // Update scales $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(options.withTransitionForTransform, transitions); } // Draw with new sizes & scales $$.redraw(options, transitions); }; ChartInternal.prototype.redrawWithoutRescale = function () { this.redraw({ withY: false, withSubchart: false, withEventRect: false, withTransitionForAxis: false }); }; ChartInternal.prototype.isTimeSeries = function () { return this.config.axis_x_type === 'timeseries'; }; ChartInternal.prototype.isCategorized = function () { return this.config.axis_x_type.indexOf('categor') >= 0; }; ChartInternal.prototype.isCustomX = function () { var $$ = this, config = $$.config; return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)); }; ChartInternal.prototype.isTimeSeriesY = function () { return this.config.axis_y_type === 'timeseries'; }; ChartInternal.prototype.getTranslate = function (target) { var $$ = this, config = $$.config, x, y; if (target === 'main') { x = asHalfPixel($$.margin.left); y = asHalfPixel($$.margin.top); } else if (target === 'context') { x = asHalfPixel($$.margin2.left); y = asHalfPixel($$.margin2.top); } else if (target === 'legend') { x = $$.margin3.left; y = $$.margin3.top; } else if (target === 'x') { x = 0; y = config.axis_rotated ? 0 : $$.height; } else if (target === 'y') { x = 0; y = config.axis_rotated ? $$.height : 0; } else if (target === 'y2') { x = config.axis_rotated ? 0 : $$.width; y = config.axis_rotated ? 1 : 0; } else if (target === 'subx') { x = 0; y = config.axis_rotated ? 0 : $$.height2; } else if (target === 'arc') { x = $$.arcWidth / 2; y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0); // to prevent wrong display of min and max label } return 'translate(' + x + ',' + y + ')'; }; ChartInternal.prototype.initialOpacity = function (d) { return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0; }; ChartInternal.prototype.initialOpacityForCircle = function (d) { return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0; }; ChartInternal.prototype.opacityForCircle = function (d) { var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show; var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0; return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0; }; ChartInternal.prototype.opacityForText = function () { return this.hasDataLabel() ? 1 : 0; }; ChartInternal.prototype.xx = function (d) { return d ? this.x(d.x) : null; }; ChartInternal.prototype.xvCustom = function (d, xyValue) { var $$ = this, value = xyValue ? d[xyValue] : d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)); }; ChartInternal.prototype.yvCustom = function (d, xyValue) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y, value = xyValue ? d[xyValue] : d.value; return Math.ceil(yScale(value)); }; ChartInternal.prototype.xv = function (d) { var $$ = this, value = d.value; if ($$.isTimeSeries()) { value = $$.parseDate(d.value); } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value); } return Math.ceil($$.x(value)); }; ChartInternal.prototype.yv = function (d) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y; return Math.ceil(yScale(d.value)); }; ChartInternal.prototype.subxx = function (d) { return d ? this.subX(d.x) : null; }; ChartInternal.prototype.transformMain = function (withTransition, transitions) { var $$ = this, xAxis, yAxis, y2Axis; if (transitions && transitions.axisX) { xAxis = transitions.axisX; } else { xAxis = $$.main.select('.' + CLASS.axisX); if (withTransition) { xAxis = xAxis.transition(); } } if (transitions && transitions.axisY) { yAxis = transitions.axisY; } else { yAxis = $$.main.select('.' + CLASS.axisY); if (withTransition) { yAxis = yAxis.transition(); } } if (transitions && transitions.axisY2) { y2Axis = transitions.axisY2; } else { y2Axis = $$.main.select('.' + CLASS.axisY2); if (withTransition) { y2Axis = y2Axis.transition(); } } (withTransition ? $$.main.transition() : $$.main).attr('transform', $$.getTranslate('main')); xAxis.attr('transform', $$.getTranslate('x')); yAxis.attr('transform', $$.getTranslate('y')); y2Axis.attr('transform', $$.getTranslate('y2')); $$.main .select('.' + CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); }; ChartInternal.prototype.transformAll = function (withTransition, transitions) { var $$ = this; $$.transformMain(withTransition, transitions); if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions); } if ($$.legend) { $$.transformLegend(withTransition); } }; ChartInternal.prototype.updateSvgSize = function () { var $$ = this, brush = $$.svg.select("." + CLASS.brush + " .overlay"); $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight); $$.svg .selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]) .select('rect') .attr('width', $$.width) .attr('height', $$.height); $$.svg .select('#' + $$.clipIdForXAxis) .select('rect') .attr('x', $$.getXAxisClipX.bind($$)) .attr('y', $$.getXAxisClipY.bind($$)) .attr('width', $$.getXAxisClipWidth.bind($$)) .attr('height', $$.getXAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForYAxis) .select('rect') .attr('x', $$.getYAxisClipX.bind($$)) .attr('y', $$.getYAxisClipY.bind($$)) .attr('width', $$.getYAxisClipWidth.bind($$)) .attr('height', $$.getYAxisClipHeight.bind($$)); $$.svg .select('#' + $$.clipIdForSubchart) .select('rect') .attr('width', $$.width) .attr('height', (brush.size() && brush.attr('height')) || 0); // MEMO: parent div's height will be bigger than svg when $$.selectChart.style('max-height', $$.currentHeight + 'px'); }; ChartInternal.prototype.updateDimension = function (withoutAxis) { var $$ = this; if (!withoutAxis) { if ($$.config.axis_rotated) { $$.axes.x.call($$.xAxis); $$.axes.subx.call($$.subXAxis); } else { $$.axes.y.call($$.yAxis); $$.axes.y2.call($$.y2Axis); } } $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); $$.transformAll(false); }; ChartInternal.prototype.observeInserted = function (selection) { var $$ = this, observer; if (typeof MutationObserver === 'undefined') { window.console.error('MutationObserver not defined.'); return; } observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.type === 'childList' && mutation.previousSibling) { observer.disconnect(); // need to wait for completion of load because size calculation requires the actual sizes determined after that completion $$.intervalForObserveInserted = window.setInterval(function () { // parentNode will NOT be null when completed if (selection.node().parentNode) { window.clearInterval($$.intervalForObserveInserted); $$.updateDimension(); if ($$.brush) { $$.brush.update(); } $$.config.oninit.call($$); $$.redraw({ withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransition: false, withTransitionForTransform: false, withLegend: true }); selection.transition().style('opacity', 1); } }, 10); } }); }); observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }); }; /** * Binds handlers to the window resize event. */ ChartInternal.prototype.bindResize = function () { var $$ = this, config = $$.config; $$.resizeFunction = $$.generateResize(); // need to call .remove $$.resizeFunction.add(function () { config.onresize.call($$); }); if (config.resize_auto) { $$.resizeFunction.add(function () { if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } $$.resizeTimeout = window.setTimeout(function () { delete $$.resizeTimeout; $$.updateAndRedraw({ withUpdateXDomain: false, withUpdateOrgXDomain: false, withTransition: false, withTransitionForTransform: false, withLegend: true }); if ($$.brush) { $$.brush.update(); } }, 100); }); } $$.resizeFunction.add(function () { config.onresized.call($$); }); $$.resizeIfElementDisplayed = function () { // if element not displayed skip it if ($$.api == null || !$$.api.element.offsetParent) { return; } $$.resizeFunction(); }; window.addEventListener('resize', $$.resizeIfElementDisplayed, false); }; /** * Binds handlers to the window focus event. */ ChartInternal.prototype.bindWindowFocus = function () { var _this = this; if (this.windowFocusHandler) { // The handler is already set return; } this.windowFocusHandler = function () { _this.redraw(); }; window.addEventListener('focus', this.windowFocusHandler); }; /** * Unbinds from the window focus event. */ ChartInternal.prototype.unbindWindowFocus = function () { window.removeEventListener('focus', this.windowFocusHandler); delete this.windowFocusHandler; }; ChartInternal.prototype.generateResize = function () { var resizeFunctions = []; function callResizeFunctions() { resizeFunctions.forEach(function (f) { f(); }); } callResizeFunctions.add = function (f) { resizeFunctions.push(f); }; callResizeFunctions.remove = function (f) { for (var i = 0; i < resizeFunctions.length; i++) { if (resizeFunctions[i] === f) { resizeFunctions.splice(i, 1); break; } } }; return callResizeFunctions; }; ChartInternal.prototype.endall = function (transition, callback) { var n = 0; transition .each(function () { ++n; }) .on('end', function () { if (!--n) { callback.apply(this, arguments); } }); }; ChartInternal.prototype.generateWait = function () { var $$ = this; var transitionsToWait = [], f = function (callback) { var timer = setInterval(function () { if (!$$.isTabVisible()) { return; } var done = 0; transitionsToWait.forEach(function (t) { if (t.empty()) { done += 1; return; } try { t.transition(); } catch (e) { done += 1; } }); if (done === transitionsToWait.length) { clearInterval(timer); if (callback) { callback(); } } }, 50); }; f.add = function (transition) { transitionsToWait.push(transition); }; return f; }; ChartInternal.prototype.parseDate = function (date) { var $$ = this, parsedDate; if (date instanceof Date) { parsedDate = date; } else if (typeof date === 'string') { parsedDate = $$.dataTimeParse(date); } else if (typeof date === 'object') { parsedDate = new Date(+date); } else if (typeof date === 'number' && !isNaN(date)) { parsedDate = new Date(+date); } if (!parsedDate || isNaN(+parsedDate)) { window.console.error("Failed to parse x '" + date + "' to Date object"); } return parsedDate; }; ChartInternal.prototype.isTabVisible = function () { return !document.hidden; }; ChartInternal.prototype.getPathBox = getPathBox; ChartInternal.prototype.CLASS = CLASS; /* jshint ignore:start */ (function () { if (!('SVGPathSeg' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg window.SVGPathSeg = function (type, typeAsLetter, owningPathSegList) { this.pathSegType = type; this.pathSegTypeAsLetter = typeAsLetter; this._owningPathSegList = owningPathSegList; }; window.SVGPathSeg.prototype.classname = 'SVGPathSeg'; window.SVGPathSeg.PATHSEG_UNKNOWN = 0; window.SVGPathSeg.PATHSEG_CLOSEPATH = 1; window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2; window.SVGPathSeg.PATHSEG_MOVETO_REL = 3; window.SVGPathSeg.PATHSEG_LINETO_ABS = 4; window.SVGPathSeg.PATHSEG_LINETO_REL = 5; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9; window.SVGPathSeg.PATHSEG_ARC_ABS = 10; window.SVGPathSeg.PATHSEG_ARC_REL = 11; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12; window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14; window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16; window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18; window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19; // Notify owning PathSegList on any changes so they can be synchronized back to the path element. window.SVGPathSeg.prototype._segmentChanged = function () { if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this); }; window.SVGPathSegClosePath = function (owningPathSegList) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList); }; window.SVGPathSegClosePath.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegClosePath.prototype.toString = function () { return '[object SVGPathSegClosePath]'; }; window.SVGPathSegClosePath.prototype._asPathString = function () { return this.pathSegTypeAsLetter; }; window.SVGPathSegClosePath.prototype.clone = function () { return new window.SVGPathSegClosePath(undefined); }; window.SVGPathSegMovetoAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegMovetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegMovetoAbs.prototype.toString = function () { return '[object SVGPathSegMovetoAbs]'; }; window.SVGPathSegMovetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegMovetoAbs.prototype.clone = function () { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegMovetoRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegMovetoRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegMovetoRel.prototype.toString = function () { return '[object SVGPathSegMovetoRel]'; }; window.SVGPathSegMovetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegMovetoRel.prototype.clone = function () { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegLinetoAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoAbs.prototype.toString = function () { return '[object SVGPathSegLinetoAbs]'; }; window.SVGPathSegLinetoAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegLinetoAbs.prototype.clone = function () { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegLinetoRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoRel.prototype.toString = function () { return '[object SVGPathSegLinetoRel]'; }; window.SVGPathSegLinetoRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegLinetoRel.prototype.clone = function () { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicAbs = function (owningPathSegList, x, y, x1, y1, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicAbs]'; }; window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicAbs(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicRel = function (owningPathSegList, x, y, x1, y1, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicRel]'; }; window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicRel(undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticAbs = function (owningPathSegList, x, y, x1, y1) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticAbs]'; }; window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, this._x, this._y, this._x1, this._y1); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticRel = function (owningPathSegList, x, y, x1, y1) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList); this._x = x; this._y = y; this._x1 = x1; this._y1 = y1; }; window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticRel]'; }; window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticRel(undefined, this._x, this._y, this._x1, this._y1); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function () { return this._x1; }, set: function (x1) { this._x1 = x1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function () { return this._y1; }, set: function (y1) { this._y1 = y1; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcAbs = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegArcAbs.prototype.toString = function () { return '[object SVGPathSegArcAbs]'; }; window.SVGPathSegArcAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegArcAbs.prototype.clone = function () { return new window.SVGPathSegArcAbs(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegArcRel = function (owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList); this._x = x; this._y = y; this._r1 = r1; this._r2 = r2; this._angle = angle; this._largeArcFlag = largeArcFlag; this._sweepFlag = sweepFlag; }; window.SVGPathSegArcRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegArcRel.prototype.toString = function () { return '[object SVGPathSegArcRel]'; }; window.SVGPathSegArcRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegArcRel.prototype.clone = function () { return new window.SVGPathSegArcRel(undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag); }; Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function () { return this._r1; }, set: function (r1) { this._r1 = r1; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function () { return this._r2; }, set: function (r2) { this._r2 = r2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function () { return this._angle; }, set: function (angle) { this._angle = angle; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function () { return this._largeArcFlag; }, set: function (largeArcFlag) { this._largeArcFlag = largeArcFlag; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function () { return this._sweepFlag; }, set: function (sweepFlag) { this._sweepFlag = sweepFlag; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalAbs = function (owningPathSegList, x) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList); this._x = x; }; window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalAbs]'; }; window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x); }; Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoHorizontalRel = function (owningPathSegList, x) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList); this._x = x; }; window.SVGPathSegLinetoHorizontalRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoHorizontalRel.prototype.toString = function () { return '[object SVGPathSegLinetoHorizontalRel]'; }; window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x; }; window.SVGPathSegLinetoHorizontalRel.prototype.clone = function () { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x); }; Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalAbs = function (owningPathSegList, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList); this._y = y; }; window.SVGPathSegLinetoVerticalAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoVerticalAbs.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalAbs]'; }; window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; window.SVGPathSegLinetoVerticalAbs.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y); }; Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegLinetoVerticalRel = function (owningPathSegList, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList); this._y = y; }; window.SVGPathSegLinetoVerticalRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegLinetoVerticalRel.prototype.toString = function () { return '[object SVGPathSegLinetoVerticalRel]'; }; window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._y; }; window.SVGPathSegLinetoVerticalRel.prototype.clone = function () { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y); }; Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothAbs = function (owningPathSegList, x, y, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothAbs]'; }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, this._x, this._y, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoCubicSmoothRel = function (owningPathSegList, x, y, x2, y2) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList); this._x = x; this._y = y; this._x2 = x2; this._y2 = y2; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoCubicSmoothRel]'; }; window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function () { return (this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y); }; window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, this._x, this._y, this._x2, this._y2); }; Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function () { return this._x2; }, set: function (x2) { this._x2 = x2; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function () { return this._y2; }, set: function (y2) { this._y2 = y2; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticSmoothAbs = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]'; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); window.SVGPathSegCurvetoQuadraticSmoothRel = function (owningPathSegList, x, y) { window.SVGPathSeg.call(this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList); this._x = x; this._y = y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create(window.SVGPathSeg.prototype); window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function () { return '[object SVGPathSegCurvetoQuadraticSmoothRel]'; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function () { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y; }; window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function () { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, this._x, this._y); }; Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function () { return this._x; }, set: function (x) { this._x = x; this._segmentChanged(); }, enumerable: true }); Object.defineProperty(window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function () { return this._y; }, set: function (y) { this._y = y; this._segmentChanged(); }, enumerable: true }); // Add createSVGPathSeg* functions to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. window.SVGPathElement.prototype.createSVGPathSegClosePath = function () { return new window.SVGPathSegClosePath(undefined); }; window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function (x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function (x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function (x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function (x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicAbs(undefined, x, y, x1, y1, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function (x, y, x1, y1, x2, y2) { return new window.SVGPathSegCurvetoCubicRel(undefined, x, y, x1, y1, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function (x, y, x1, y1) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1); }; window.SVGPathElement.prototype.createSVGPathSegArcAbs = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcAbs(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; window.SVGPathElement.prototype.createSVGPathSegArcRel = function (x, y, r1, r2, angle, largeArcFlag, sweepFlag) { return new window.SVGPathSegArcRel(undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag); }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function (x) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x); }; window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function (x) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x); }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function (y) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y); }; window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function (y) { return new window.SVGPathSegLinetoVerticalRel(undefined, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function (x, y, x2, y2) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y); }; window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function (x, y) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y); }; if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { // Add getPathSegAtLength to SVGPathElement. // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. window.SVGPathElement.prototype.getPathSegAtLength = function (distance) { if (distance === undefined || !isFinite(distance)) throw 'Invalid arguments.'; var measurementElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); measurementElement.setAttribute('d', this.getAttribute('d')); var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1; // If the path is empty, return 0. if (lastPathSegment <= 0) return 0; do { measurementElement.pathSegList.removeItem(lastPathSegment); if (distance > measurementElement.getTotalLength()) break; lastPathSegment--; } while (lastPathSegment > 0); return lastPathSegment; }; } } if (!('SVGPathSegList' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList window.SVGPathSegList = function (pathElement) { this._pathElement = pathElement; this._list = this._parsePath(this._pathElement.getAttribute('d')); // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { attributes: true, attributeFilter: ['d'] }; this._pathElementMutationObserver = new MutationObserver(this._updateListFromPathMutations.bind(this)); this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); }; window.SVGPathSegList.prototype.classname = 'SVGPathSegList'; Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { get: function () { this._checkPathSynchronizedToList(); return this._list.length; }, enumerable: true }); // Add the pathSegList accessors to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { get: function () { if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this); return this._pathSegList; }, enumerable: true }); // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. Object.defineProperty(window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); Object.defineProperty(window.SVGPathElement.prototype, 'animatedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); Object.defineProperty(window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function () { return this.pathSegList; }, enumerable: true }); // Process any pending mutations to the path element and update the list as needed. // This should be the first call of all public functions and is needed because // MutationObservers are not synchronous so we can have pending asynchronous mutations. window.SVGPathSegList.prototype._checkPathSynchronizedToList = function () { this._updateListFromPathMutations(this._pathElementMutationObserver.takeRecords()); }; window.SVGPathSegList.prototype._updateListFromPathMutations = function (mutationRecords) { if (!this._pathElement) return; var hasPathMutations = false; mutationRecords.forEach(function (record) { if (record.attributeName == 'd') hasPathMutations = true; }); if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute('d')); }; // Serialize the list and update the path's 'd' attribute. window.SVGPathSegList.prototype._writeListToPath = function () { this._pathElementMutationObserver.disconnect(); this._pathElement.setAttribute('d', window.SVGPathSegList._pathSegArrayAsString(this._list)); this._pathElementMutationObserver.observe(this._pathElement, this._mutationObserverConfig); }; // When a path segment changes the list needs to be synchronized back to the path element. window.SVGPathSegList.prototype.segmentChanged = function (pathSeg) { this._writeListToPath(); }; window.SVGPathSegList.prototype.clear = function () { this._checkPathSynchronizedToList(); this._list.forEach(function (pathSeg) { pathSeg._owningPathSegList = null; }); this._list = []; this._writeListToPath(); }; window.SVGPathSegList.prototype.initialize = function (newItem) { this._checkPathSynchronizedToList(); this._list = [newItem]; newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype._checkValidIndex = function (index) { if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw 'INDEX_SIZE_ERR'; }; window.SVGPathSegList.prototype.getItem = function (index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); return this._list[index]; }; window.SVGPathSegList.prototype.insertItemBefore = function (newItem, index) { this._checkPathSynchronizedToList(); // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. if (index > this.numberOfItems) index = this.numberOfItems; if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.splice(index, 0, newItem); newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype.replaceItem = function (newItem, index) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._checkValidIndex(index); this._list[index] = newItem; newItem._owningPathSegList = this; this._writeListToPath(); return newItem; }; window.SVGPathSegList.prototype.removeItem = function (index) { this._checkPathSynchronizedToList(); this._checkValidIndex(index); var item = this._list[index]; this._list.splice(index, 1); this._writeListToPath(); return item; }; window.SVGPathSegList.prototype.appendItem = function (newItem) { this._checkPathSynchronizedToList(); if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone(); } this._list.push(newItem); newItem._owningPathSegList = this; // TODO: Optimize this to just append to the existing attribute. this._writeListToPath(); return newItem; }; window.SVGPathSegList._pathSegArrayAsString = function (pathSegArray) { var string = ''; var first = true; pathSegArray.forEach(function (pathSeg) { if (first) { first = false; string += pathSeg._asPathString(); } else { string += ' ' + pathSeg._asPathString(); } }); return string; }; // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. window.SVGPathSegList.prototype._parsePath = function (string) { if (!string || string.length == 0) return []; var owningPathSegList = this; var Builder = function () { this.pathSegList = []; }; Builder.prototype.appendSegment = function (pathSeg) { this.pathSegList.push(pathSeg); }; var Source = function (string) { this._string = string; this._currentIndex = 0; this._endIndex = this._string.length; this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN; this._skipOptionalSpaces(); }; Source.prototype._isCurrentSpace = function () { var character = this._string[this._currentIndex]; return (character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f')); }; Source.prototype._skipOptionalSpaces = function () { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) this._currentIndex++; return this._currentIndex < this._endIndex; }; Source.prototype._skipOptionalSpacesOrDelimiter = function () { if (this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ',') return false; if (this._skipOptionalSpaces()) { if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ',') { this._currentIndex++; this._skipOptionalSpaces(); } } return this._currentIndex < this._endIndex; }; Source.prototype.hasMoreData = function () { return this._currentIndex < this._endIndex; }; Source.prototype.peekSegmentType = function () { var lookahead = this._string[this._currentIndex]; return this._pathSegTypeFromChar(lookahead); }; Source.prototype._pathSegTypeFromChar = function (lookahead) { switch (lookahead) { case 'Z': case 'z': return window.SVGPathSeg.PATHSEG_CLOSEPATH; case 'M': return window.SVGPathSeg.PATHSEG_MOVETO_ABS; case 'm': return window.SVGPathSeg.PATHSEG_MOVETO_REL; case 'L': return window.SVGPathSeg.PATHSEG_LINETO_ABS; case 'l': return window.SVGPathSeg.PATHSEG_LINETO_REL; case 'C': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS; case 'c': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL; case 'Q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS; case 'q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL; case 'A': return window.SVGPathSeg.PATHSEG_ARC_ABS; case 'a': return window.SVGPathSeg.PATHSEG_ARC_REL; case 'H': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS; case 'h': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL; case 'V': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS; case 'v': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL; case 'S': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; case 's': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL; case 'T': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; case 't': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; default: return window.SVGPathSeg.PATHSEG_UNKNOWN; } }; Source.prototype._nextCommandHelper = function (lookahead, previousCommand) { // Check for remaining coordinates in the current command. if ((lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH) { if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS; if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL; return previousCommand; } return window.SVGPathSeg.PATHSEG_UNKNOWN; }; Source.prototype.initialCommandIsMoveTo = function () { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) return true; var command = this.peekSegmentType(); // Path must start with moveTo. return (command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL); }; // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF Source.prototype._parseNumber = function () { var exponent = 0; var integer = 0; var frac = 1; var decimal = 0; var sign = 1; var expsign = 1; var startIndex = this._currentIndex; this._skipOptionalSpaces(); // Read the sign. if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '+') this._currentIndex++; else if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; sign = -1; } if (this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) != '.')) // The first character of a number must be one of [0-9+-.]. return undefined; // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') this._currentIndex++; // Advance to first non-digit. if (this._currentIndex != startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1; var multiplier = 1; while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0'); multiplier *= 10; } } // Read the decimals. if (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '.') { this._currentIndex++; // There must be a least one digit following the . if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') return undefined; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { frac *= 10; decimal += (this._string.charAt(this._currentIndex) - '0') / frac; this._currentIndex += 1; } } // Read the exponent part. if (this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == 'e' || this._string.charAt(this._currentIndex) == 'E') && this._string.charAt(this._currentIndex + 1) != 'x' && this._string.charAt(this._currentIndex + 1) != 'm') { this._currentIndex++; // Read the sign of the exponent. if (this._string.charAt(this._currentIndex) == '+') { this._currentIndex++; } else if (this._string.charAt(this._currentIndex) == '-') { this._currentIndex++; expsign = -1; } // There must be an exponent. if (this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') return undefined; while (this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9') { exponent *= 10; exponent += this._string.charAt(this._currentIndex) - '0'; this._currentIndex++; } } var number = integer + decimal; number *= sign; if (exponent) number *= Math.pow(10, expsign * exponent); if (startIndex == this._currentIndex) return undefined; this._skipOptionalSpacesOrDelimiter(); return number; }; Source.prototype._parseArcFlag = function () { if (this._currentIndex >= this._endIndex) return undefined; var flag = false; var flagChar = this._string.charAt(this._currentIndex++); if (flagChar == '0') flag = false; else if (flagChar == '1') flag = true; else return undefined; this._skipOptionalSpacesOrDelimiter(); return flag; }; Source.prototype.parseSegment = function () { var lookahead = this._string[this._currentIndex]; var command = this._pathSegTypeFromChar(lookahead); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { // Possibly an implicit command. Not allowed if this is the first command. if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; command = this._nextCommandHelper(lookahead, this._previousCommand); if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null; } else { this._currentIndex++; } this._previousCommand = command; switch (command) { case window.SVGPathSeg.PATHSEG_MOVETO_REL: return new window.SVGPathSegMovetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_MOVETO_ABS: return new window.SVGPathSegMovetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_REL: return new window.SVGPathSegLinetoRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_ABS: return new window.SVGPathSegLinetoAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: return new window.SVGPathSegLinetoHorizontalRel(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: return new window.SVGPathSegLinetoHorizontalAbs(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: return new window.SVGPathSegLinetoVerticalRel(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: return new window.SVGPathSegLinetoVerticalAbs(owningPathSegList, this._parseNumber()); case window.SVGPathSeg.PATHSEG_CLOSEPATH: this._skipOptionalSpaces(); return new window.SVGPathSegClosePath(owningPathSegList); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothRel(owningPathSegList, points.x, points.y, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoCubicSmoothAbs(owningPathSegList, points.x, points.y, points.x2, points.y2); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticRel(owningPathSegList, points.x, points.y, points.x1, points.y1); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegCurvetoQuadraticAbs(owningPathSegList, points.x, points.y, points.x1, points.y1); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new window.SVGPathSegCurvetoQuadraticSmoothRel(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new window.SVGPathSegCurvetoQuadraticSmoothAbs(owningPathSegList, this._parseNumber(), this._parseNumber()); case window.SVGPathSeg.PATHSEG_ARC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcRel(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); case window.SVGPathSeg.PATHSEG_ARC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() }; return new window.SVGPathSegArcAbs(owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep); default: throw 'Unknown path seg type.'; } }; var builder = new Builder(); var source = new Source(string); if (!source.initialCommandIsMoveTo()) return []; while (source.hasMoreData()) { var pathSeg = source.parseSegment(); if (!pathSeg) return []; builder.appendSegment(pathSeg); } return builder.pathSegList; }; } })(); // String.padEnd polyfill for IE11 // // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength, padString) { targetLength = targetLength >> 0; //floor if number or convert non-number to 0; padString = String(typeof padString !== 'undefined' ? padString : ' '); if (this.length > targetLength) { return String(this); } else { targetLength = targetLength - this.length; if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed } return String(this) + padString.slice(0, targetLength); } }; } // Object.assign polyfill for IE11 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } /* jshint ignore:end */ Chart.prototype.axis = function () { }; Chart.prototype.axis.labels = function (labels) { var $$ = this.internal; if (arguments.length) { Object.keys(labels).forEach(function (axisId) { $$.axis.setLabelText(axisId, labels[axisId]); }); $$.axis.updateLabels(); } // TODO: return some values? }; Chart.prototype.axis.max = function (max) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof max === 'object') { if (isValue(max.x)) { config.axis_x_max = max.x; } if (isValue(max.y)) { config.axis_y_max = max.y; } if (isValue(max.y2)) { config.axis_y2_max = max.y2; } } else { config.axis_y_max = config.axis_y2_max = max; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_max, y: config.axis_y_max, y2: config.axis_y2_max }; } }; Chart.prototype.axis.min = function (min) { var $$ = this.internal, config = $$.config; if (arguments.length) { if (typeof min === 'object') { if (isValue(min.x)) { config.axis_x_min = min.x; } if (isValue(min.y)) { config.axis_y_min = min.y; } if (isValue(min.y2)) { config.axis_y2_min = min.y2; } } else { config.axis_y_min = config.axis_y2_min = min; } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } else { return { x: config.axis_x_min, y: config.axis_y_min, y2: config.axis_y2_min }; } }; Chart.prototype.axis.range = function (range) { if (arguments.length) { if (isDefined(range.max)) { this.axis.max(range.max); } if (isDefined(range.min)) { this.axis.min(range.min); } } else { return { max: this.axis.max(), min: this.axis.min() }; } }; Chart.prototype.axis.types = function (types) { var $$ = this.internal; if (types === undefined) { return { y: $$.config.axis_y_type, y2: $$.config.axis_y2_type }; } else { if (isDefined(types.y)) { $$.config.axis_y_type = types.y; } if (isDefined(types.y2)) { $$.config.axis_y2_type = types.y2; } $$.updateScales(); $$.redraw(); } }; Chart.prototype.category = function (i, category) { var $$ = this.internal, config = $$.config; if (arguments.length > 1) { config.axis_x_categories[i] = category; $$.redraw(); } return config.axis_x_categories[i]; }; Chart.prototype.categories = function (categories) { var $$ = this.internal, config = $$.config; if (!arguments.length) { return config.axis_x_categories; } config.axis_x_categories = categories; $$.redraw(); return config.axis_x_categories; }; Chart.prototype.resize = function (size) { var $$ = this.internal, config = $$.config; config.size_width = size ? size.width : null; config.size_height = size ? size.height : null; this.flush(); }; Chart.prototype.flush = function () { var $$ = this.internal; $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }); }; Chart.prototype.destroy = function () { var $$ = this.internal; window.clearInterval($$.intervalForObserveInserted); if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout); } window.removeEventListener('resize', $$.resizeIfElementDisplayed); // Removes the inner resize functions $$.resizeFunction.remove(); // Unbinds from the window focus event $$.unbindWindowFocus(); $$.selectChart.classed('c3', false).html(''); // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. Object.keys($$).forEach(function (key) { $$[key] = null; }); return null; }; // TODO: fix Chart.prototype.color = function (id) { var $$ = this.internal; return $$.color(id); // more patterns }; Chart.prototype.data = function (targetIds) { var targets = this.internal.data.targets; return typeof targetIds === 'undefined' ? targets : targets.filter(function (t) { return [].concat(targetIds).indexOf(t.id) >= 0; }); }; Chart.prototype.data.shown = function (targetIds) { return this.internal.filterTargetsToShow(this.data(targetIds)); }; /** * Get values of the data loaded in the chart. * * @param {String|Array} targetId This API returns the value of specified target. * @param flat * @return {Array} Data values */ Chart.prototype.data.values = function (targetId, flat) { if (flat === void 0) { flat = true; } var values = null; if (targetId) { var targets = this.data(targetId); if (targets && isArray(targets)) { values = targets.reduce(function (ret, v) { var dataValue = v.values.map(function (d) { return d.value; }); if (flat) { ret = ret.concat(dataValue); } else { ret.push(dataValue); } return ret; }, []); } } return values; }; Chart.prototype.data.names = function (names) { this.internal.clearLegendItemTextBoxCache(); return this.internal.updateDataAttributes('names', names); }; Chart.prototype.data.colors = function (colors) { return this.internal.updateDataAttributes('colors', colors); }; Chart.prototype.data.axes = function (axes) { return this.internal.updateDataAttributes('axes', axes); }; Chart.prototype.data.stackNormalized = function (normalized) { if (normalized === undefined) { return this.internal.isStackNormalized(); } this.internal.config.data_stack_normalize = !!normalized; this.internal.redraw(); }; Chart.prototype.donut = function () { }; Chart.prototype.donut.padAngle = function (padAngle) { if (padAngle === undefined) { return this.internal.config.donut_padAngle; } this.internal.config.donut_padAngle = padAngle; this.flush(); }; Chart.prototype.flow = function (args) { var $$ = this.internal, targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to; if (args.json) { data = $$.convertJsonToData(args.json, args.keys); } else if (args.rows) { data = $$.convertRowsToData(args.rows); } else if (args.columns) { data = $$.convertColumnsToData(args.columns); } else { return; } targets = $$.convertDataToTargets(data, true); // Update/Add data $$.data.targets.forEach(function (t) { var found = false, i, j; for (i = 0; i < targets.length; i++) { if (t.id === targets[i].id) { found = true; if (t.values[t.values.length - 1]) { tail = t.values[t.values.length - 1].index + 1; } length = targets[i].values.length; for (j = 0; j < length; j++) { targets[i].values[j].index = tail + j; if (!$$.isTimeSeries()) { targets[i].values[j].x = tail + j; } } t.values = t.values.concat(targets[i].values); targets.splice(i, 1); break; } } if (!found) { notfoundIds.push(t.id); } }); // Append null for not found targets $$.data.targets.forEach(function (t) { var i, j; for (i = 0; i < notfoundIds.length; i++) { if (t.id === notfoundIds[i]) { tail = t.values[t.values.length - 1].index + 1; for (j = 0; j < length; j++) { t.values.push({ id: t.id, index: tail + j, x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, value: null }); } } } }); // Generate null values for new target if ($$.data.targets.length) { targets.forEach(function (t) { var i, missing = []; for (i = $$.data.targets[0].values[0].index; i < tail; i++) { missing.push({ id: t.id, index: i, x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, value: null }); } t.values.forEach(function (v) { v.index += tail; if (!$$.isTimeSeries()) { v.x += tail; } }); t.values = missing.concat(t.values); }); } $$.data.targets = $$.data.targets.concat(targets); // add remained // check data count because behavior needs to change when it's only one dataCount = $$.getMaxDataCount(); baseTarget = $$.data.targets[0]; baseValue = baseTarget.values[0]; // Update length to flow if needed if (isDefined(args.to)) { length = 0; to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to; baseTarget.values.forEach(function (v) { if (v.x < to) { length++; } }); } else if (isDefined(args.length)) { length = args.length; } // If only one data, update the domain to flow from left edge of the chart if (!orgDataCount) { if ($$.isTimeSeries()) { if (baseTarget.values.length > 1) { diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x; } else { diff = baseValue.x - $$.getXDomain($$.data.targets)[0]; } } else { diff = 1; } domain = [baseValue.x - diff, baseValue.x]; $$.updateXDomain(null, true, true, false, domain); } else if (orgDataCount === 1) { if ($$.isTimeSeries()) { diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2; domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)]; $$.updateXDomain(null, true, true, false, domain); } } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ flow: { index: baseValue.index, length: length, duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, done: args.done, orgDataCount: orgDataCount }, withLegend: true, withTransition: orgDataCount > 1, withTrimXDomain: false, withUpdateXAxis: true }); }; ChartInternal.prototype.generateFlow = function (args) { var $$ = this, config = $$.config, d3 = $$.d3; return function () { var targets = args.targets, flow = args.flow, drawBar = args.drawBar, drawLine = args.drawLine, drawArea = args.drawArea, cx = args.cx, cy = args.cy, xv = args.xv, xForText = args.xForText, yForText = args.yForText, duration = args.duration; var translateX, scaleX = 1, transform, flowIndex = flow.index, flowLength = flow.length, flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), flowEnd = $$.getValueOnIndex($$.data.targets[0].values, flowIndex + flowLength), orgDomain = $$.x.domain(), domain, durationForFlow = flow.duration || duration, done = flow.done || function () { }, wait = $$.generateWait(); var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle; // set flag $$.flowing = true; // remove head data after rendered $$.data.targets.forEach(function (d) { d.values.splice(0, flowLength); }); // update x domain to generate axis elements for flow domain = $$.updateXDomain(targets, true, true); // update elements related to x scale if ($$.updateXGrid) { $$.updateXGrid(true); } xgrid = $$.xgrid || d3.selectAll([]); // xgrid needs to be obtained after updateXGrid xgridLines = $$.xgridLines || d3.selectAll([]); mainRegion = $$.mainRegion || d3.selectAll([]); mainText = $$.mainText || d3.selectAll([]); mainBar = $$.mainBar || d3.selectAll([]); mainLine = $$.mainLine || d3.selectAll([]); mainArea = $$.mainArea || d3.selectAll([]); mainCircle = $$.mainCircle || d3.selectAll([]); // generate transform to flow if (!flow.orgDataCount) { // if empty if ($$.data.targets[0].values.length !== 1) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0); flowEnd = $$.getValueOnIndex($$.data.targets[0].values, $$.data.targets[0].values.length - 1); translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } else { translateX = diffDomain(domain) / 2; } } } else if (flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x)) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { if ($$.isTimeSeries()) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]); } else { translateX = $$.x(flowStart.x) - $$.x(flowEnd.x); } } scaleX = diffDomain(orgDomain) / diffDomain(domain); transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)'; $$.hideXGridFocus(); var flowTransition = d3 .transition() .ease(d3.easeLinear) .duration(durationForFlow); wait.add($$.xAxis($$.axes.x, flowTransition)); wait.add(mainBar.transition(flowTransition).attr('transform', transform)); wait.add(mainLine.transition(flowTransition).attr('transform', transform)); wait.add(mainArea.transition(flowTransition).attr('transform', transform)); wait.add(mainCircle.transition(flowTransition).attr('transform', transform)); wait.add(mainText.transition(flowTransition).attr('transform', transform)); wait.add(mainRegion .filter($$.isRegionOnX) .transition(flowTransition) .attr('transform', transform)); wait.add(xgrid.transition(flowTransition).attr('transform', transform)); wait.add(xgridLines.transition(flowTransition).attr('transform', transform)); wait(function () { var i, shapes = [], texts = []; // remove flowed elements if (flowLength) { for (i = 0; i < flowLength; i++) { shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)); texts.push('.' + CLASS.text + '-' + (flowIndex + i)); } $$.svg .selectAll('.' + CLASS.shapes) .selectAll(shapes) .remove(); $$.svg .selectAll('.' + CLASS.texts) .selectAll(texts) .remove(); $$.svg.select('.' + CLASS.xgrid).remove(); } // draw again for removing flowed elements and reverting attr xgrid .attr('transform', null) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); xgridLines.attr('transform', null); xgridLines .select('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv); xgridLines .select('text') .attr('x', config.axis_rotated ? $$.width : 0) .attr('y', xv); mainBar.attr('transform', null).attr('d', drawBar); mainLine.attr('transform', null).attr('d', drawLine); mainArea.attr('transform', null).attr('d', drawArea); mainCircle .attr('transform', null) .attr('cx', cx) .attr('cy', cy); mainText .attr('transform', null) .attr('x', xForText) .attr('y', yForText) .style('fill-opacity', $$.opacityForText.bind($$)); mainRegion.attr('transform', null); mainRegion .filter($$.isRegionOnX) .attr('x', $$.regionX.bind($$)) .attr('width', $$.regionWidth.bind($$)); // callback for end of flow done(); $$.flowing = false; }); }; }; Chart.prototype.focus = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); (candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)))), this.revert(); this.defocus(); candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.expandArc(targetIds); } $$.toggleFocusLegend(targetIds, true); $$.focusedTargetIds = targetIds; $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); }; Chart.prototype.defocus = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); (candidates = $$.svg.selectAll($$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)))), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } $$.toggleFocusLegend(targetIds, false); $$.focusedTargetIds = $$.focusedTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); $$.defocusedTargetIds = targetIds; }; Chart.prototype.revert = function (targetIds) { var $$ = this.internal, candidates; targetIds = $$.mapToTargetIds(targetIds); candidates = $$.svg.selectAll($$.selectorTargets(targetIds)); // should be for all targets candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false); if ($$.hasArcType()) { $$.unexpandArc(targetIds); } if ($$.config.legend_show) { $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))); $$.legend .selectAll($$.selectorLegends(targetIds)) .filter(function () { return $$.d3.select(this).classed(CLASS.legendItemFocused); }) .classed(CLASS.legendItemFocused, false); } $$.focusedTargetIds = []; $$.defocusedTargetIds = []; }; Chart.prototype.xgrids = function (grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_x_lines; } config.grid_x_lines = grids; $$.redrawWithoutRescale(); return config.grid_x_lines; }; Chart.prototype.xgrids.add = function (grids) { var $$ = this.internal; return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])); }; Chart.prototype.xgrids.remove = function (params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, true); }; Chart.prototype.ygrids = function (grids) { var $$ = this.internal, config = $$.config; if (!grids) { return config.grid_y_lines; } config.grid_y_lines = grids; $$.redrawWithoutRescale(); return config.grid_y_lines; }; Chart.prototype.ygrids.add = function (grids) { var $$ = this.internal; return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])); }; Chart.prototype.ygrids.remove = function (params) { // TODO: multiple var $$ = this.internal; $$.removeGridLines(params, false); }; Chart.prototype.groups = function (groups) { var $$ = this.internal, config = $$.config; if (isUndefined(groups)) { return config.data_groups; } config.data_groups = groups; $$.redraw(); return config.data_groups; }; Chart.prototype.legend = function () { }; Chart.prototype.legend.show = function (targetIds) { var $$ = this.internal; $$.showLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: true }); }; Chart.prototype.legend.hide = function (targetIds) { var $$ = this.internal; $$.hideLegend($$.mapToTargetIds(targetIds)); $$.updateAndRedraw({ withLegend: false }); }; Chart.prototype.load = function (args) { var $$ = this.internal, config = $$.config; // update xs if specified if (args.xs) { $$.addXs(args.xs); } // update names if exists if ('names' in args) { Chart.prototype.data.names.bind(this)(args.names); } // update classes if exists if ('classes' in args) { Object.keys(args.classes).forEach(function (id) { config.data_classes[id] = args.classes[id]; }); } // update categories if exists if ('categories' in args && $$.isCategorized()) { config.axis_x_categories = args.categories; } // update axes if exists if ('axes' in args) { Object.keys(args.axes).forEach(function (id) { config.data_axes[id] = args.axes[id]; }); } // update colors if exists if ('colors' in args) { Object.keys(args.colors).forEach(function (id) { config.data_colors[id] = args.colors[id]; }); } // use cache if exists if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { $$.load($$.getCaches(args.cacheIds), args.done); return; } // unload if needed if (args.unload) { // TODO: do not unload if target will load (included in url/rows/columns) $$.unload($$.mapToTargetIds(args.unload === true ? null : args.unload), function () { $$.loadFromArgs(args); }); } else { $$.loadFromArgs(args); } }; Chart.prototype.unload = function (args) { var $$ = this.internal; args = args || {}; if (args instanceof Array) { args = { ids: args }; } else if (typeof args === 'string') { args = { ids: [args] }; } $$.unload($$.mapToTargetIds(args.ids), function () { $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }); }; Chart.prototype.pie = function () { }; Chart.prototype.pie.padAngle = function (padAngle) { if (padAngle === undefined) { return this.internal.config.pie_padAngle; } this.internal.config.pie_padAngle = padAngle; this.flush(); }; Chart.prototype.regions = function (regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions; } config.regions = regions; $$.redrawWithoutRescale(); return config.regions; }; Chart.prototype.regions.add = function (regions) { var $$ = this.internal, config = $$.config; if (!regions) { return config.regions; } config.regions = config.regions.concat(regions); $$.redrawWithoutRescale(); return config.regions; }; Chart.prototype.regions.remove = function (options) { var $$ = this.internal, config = $$.config, duration, classes, regions; options = options || {}; duration = getOption(options, 'duration', config.transition_duration); classes = getOption(options, 'classes', [CLASS.region]); regions = $$.main.select('.' + CLASS.regions).selectAll(classes.map(function (c) { return '.' + c; })); (duration ? regions.transition().duration(duration) : regions) .style('opacity', 0) .remove(); config.regions = config.regions.filter(function (region) { var found = false; if (!region['class']) { return true; } region['class'].split(' ').forEach(function (c) { if (classes.indexOf(c) >= 0) { found = true; } }); return !found; }); return config.regions; }; Chart.prototype.selected = function (targetId) { var $$ = this.internal, d3 = $$.d3; return $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)) .selectAll('.' + CLASS.shape) .filter(function () { return d3.select(this).classed(CLASS.SELECTED); }) .nodes() .map(function (d) { var data = d.__data__; return data.data ? data.data : data; }); }; Chart.prototype.select = function (ids, indices, resetOther) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return; } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return; } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d) && !isSelected) { toggle(true, shape.classed(CLASS.SELECTED, true), d, i); } } else if (isDefined(resetOther) && resetOther) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } }); }; Chart.prototype.unselect = function (ids, indices) { var $$ = this.internal, d3 = $$.d3, config = $$.config; if (!config.data_selection_enabled) { return; } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED); // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return; } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d)) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } } } }); }; Chart.prototype.show = function (targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.removeHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('display', isIE() ? 'block' : 'initial', 'important') .style('opacity', 1, 'important') .call($$.endall, function () { targets.style('opacity', null).style('opacity', 1); }); if (options.withLegend) { $$.showLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.hide = function (targetIds, options) { var $$ = this.internal, targets; targetIds = $$.mapToTargetIds(targetIds); options = options || {}; $$.addHiddenTargetIds(targetIds); targets = $$.svg.selectAll($$.selectorTargets(targetIds)); targets .transition() .style('opacity', 0, 'important') .call($$.endall, function () { targets.style('opacity', null).style('opacity', 0); targets.style('display', 'none'); }); if (options.withLegend) { $$.hideLegend(targetIds); } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); }; Chart.prototype.toggle = function (targetIds, options) { var that = this, $$ = this.internal; $$.mapToTargetIds(targetIds).forEach(function (targetId) { $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options); }); }; Chart.prototype.subchart = function () { }; Chart.prototype.subchart.isShown = function () { var $$ = this.internal; return $$.config.subchart_show; }; Chart.prototype.subchart.show = function () { var $$ = this.internal; if ($$.config.subchart_show) { return; } $$.config.subchart_show = true; // insert DOM $$.initSubchart(); // update dimensions with sub chart now visible $$.updateDimension(); // insert brush (depends on sizes previously updated) $$.initSubchartBrush(); // attach data $$.updateTargetsForSubchart($$.getTargets()); // reset fade-in state $$.mapToIds($$.data.targets).forEach(function (id) { $$.withoutFadeIn[id] = false; }); // redraw chart ! $$.updateAndRedraw(); // update visible targets ! $$.showTargets(); }; Chart.prototype.subchart.hide = function () { var $$ = this.internal; if (!$$.config.subchart_show) { return; } $$.config.subchart_show = false; // remove DOM $$.removeSubchart(); // re-render chart $$.redraw(); }; Chart.prototype.tooltip = function () { }; Chart.prototype.tooltip.show = function (args) { var $$ = this.internal, targets, data, mouse = {}; // determine mouse position on the chart if (args.mouse) { mouse = args.mouse; } else { // determine focus data if (args.data) { data = args.data; } else if (typeof args.x !== 'undefined') { if (args.id) { targets = $$.data.targets.filter(function (t) { return t.id === args.id; }); } else { targets = $$.data.targets; } data = $$.filterByX(targets, args.x).slice(0, 1)[0]; } mouse = data ? $$.getMousePosition(data) : null; } // emulate mouse events to show $$.dispatchEvent('mousemove', mouse); $$.config.tooltip_onshow.call($$, data); }; Chart.prototype.tooltip.hide = function () { // TODO: get target data by checking the state of focus this.internal.dispatchEvent('mouseout', 0); this.internal.config.tooltip_onhide.call(this); }; Chart.prototype.transform = function (type, targetIds) { var $$ = this.internal, options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null; $$.transformTo(targetIds, type, options); }; ChartInternal.prototype.transformTo = function (targetIds, type, optionsForRedraw) { var $$ = this, withTransitionForAxis = !$$.hasArcType(), options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis }; options.withTransitionForTransform = false; $$.transiting = false; $$.setTargetType(targetIds, type); $$.updateTargets($$.data.targets); // this is needed when transforming to arc $$.updateAndRedraw(options); }; Chart.prototype.x = function (x) { var $$ = this.internal; if (arguments.length) { $$.updateTargetX($$.data.targets, x); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs; }; Chart.prototype.xs = function (xs) { var $$ = this.internal; if (arguments.length) { $$.updateTargetXs($$.data.targets, xs); $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }); } return $$.data.xs; }; Chart.prototype.zoom = function (domain) { var $$ = this.internal; if (domain) { if ($$.isTimeSeries()) { domain = domain.map(function (x) { return $$.parseDate(x); }); } if ($$.config.subchart_show) { $$.brush.selectionAsValue(domain, true); } else { $$.updateXDomain(null, true, false, false, domain); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } $$.config.zoom_onzoom.call(this, $$.x.orgDomain()); return domain; } else { return $$.x.domain(); } }; Chart.prototype.zoom.enable = function (enabled) { var $$ = this.internal; $$.config.zoom_enabled = enabled; $$.updateAndRedraw(); }; Chart.prototype.unzoom = function () { var $$ = this.internal; if ($$.config.subchart_show) { $$.brush.clear(); } else { $$.updateXDomain(null, true, false, false, $$.subX.domain()); $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }); } }; Chart.prototype.zoom.max = function (max) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (max === 0 || max) { config.zoom_x_max = d3.max([$$.orgXDomain[1], max]); } else { return config.zoom_x_max; } }; Chart.prototype.zoom.min = function (min) { var $$ = this.internal, config = $$.config, d3 = $$.d3; if (min === 0 || min) { config.zoom_x_min = d3.min([$$.orgXDomain[0], min]); } else { return config.zoom_x_min; } }; Chart.prototype.zoom.range = function (range) { if (arguments.length) { if (isDefined(range.max)) { this.domain.max(range.max); } if (isDefined(range.min)) { this.domain.min(range.min); } } else { return { max: this.domain.max(), min: this.domain.min() }; } }; ChartInternal.prototype.initPie = function () { var $$ = this, d3 = $$.d3; $$.pie = d3 .pie() .padAngle(this.getPadAngle.bind(this)) .value(function (d) { return d.values.reduce(function (a, b) { return a + b.value; }, 0); }); var orderFct = $$.getOrderFunction(); // we need to reverse the returned order if asc or desc to have the slice in expected order. if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) { var defaultSort_1 = orderFct; orderFct = function (t1, t2) { return defaultSort_1(t1, t2) * -1; }; } $$.pie.sort(orderFct || null); }; ChartInternal.prototype.updateRadius = function () { var $$ = this, config = $$.config, w = config.gauge_width || config.donut_width, gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth; $$.radiusExpanded = (Math.min($$.arcWidth, $$.arcHeight) / 2) * ($$.hasType('gauge') ? 0.85 : 1); $$.radius = $$.radiusExpanded * 0.95; $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6; $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0; $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius; }; ChartInternal.prototype.getPadAngle = function () { if (this.hasType('pie')) { return this.config.pie_padAngle || 0; } else if (this.hasType('donut')) { return this.config.donut_padAngle || 0; } else { return 0; } }; ChartInternal.prototype.updateArc = function () { var $$ = this; $$.svgArc = $$.getSvgArc(); $$.svgArcExpanded = $$.getSvgArcExpanded(); $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98); }; ChartInternal.prototype.updateAngle = function (d) { var $$ = this, config = $$.config, found = false, index = 0, gMin, gMax, gTic, gValue; if (!config) { return null; } $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function (t) { if (!found && t.data.id === d.data.id) { found = true; d = t; d.index = index; } index++; }); if (isNaN(d.startAngle)) { d.startAngle = 0; } if (isNaN(d.endAngle)) { d.endAngle = d.startAngle; } if ($$.isGaugeType(d.data)) { gMin = config.gauge_min; gMax = config.gauge_max; gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin); gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin; d.startAngle = config.gauge_startingAngle; d.endAngle = d.startAngle + gTic * gValue; } return found ? d : null; }; ChartInternal.prototype.getSvgArc = function () { var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, arc = $$.d3 .arc() .outerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius; }) .innerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius; }), newArc = function (d, withoutUpdate) { var updated; if (withoutUpdate) { return arc(d); } // for interpolate updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0'; }; newArc.centroid = arc.centroid; return newArc; }; ChartInternal.prototype.getSvgArcExpanded = function (rate) { rate = rate || 1; var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, expandWidth = Math.min($$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100), arc = $$.d3 .arc() .outerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate; }) .innerRadius(function (d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius; }); return function (d) { var updated = $$.updateAngle(d); return updated ? arc(updated) : 'M 0 0'; }; }; ChartInternal.prototype.getArc = function (d, withoutUpdate, force) { return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : 'M 0 0'; }; ChartInternal.prototype.transformForArcLabel = function (d) { var $$ = this, config = $$.config, updated = $$.updateAngle(d), c, x, y, h, ratio, translate = '', hasGauge = $$.hasType('gauge'); if (updated && !hasGauge) { c = this.svgArc.centroid(updated); x = isNaN(c[0]) ? 0 : c[0]; y = isNaN(c[1]) ? 0 : c[1]; h = Math.sqrt(x * x + y * y); if ($$.hasType('donut') && config.donut_label_ratio) { ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio; } else if ($$.hasType('pie') && config.pie_label_ratio) { ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio; } else { ratio = $$.radius && h ? ((36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius) / h : 0; } translate = 'translate(' + x * ratio + ',' + y * ratio + ')'; } else if (updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1) { var y1 = Math.sin(updated.endAngle - Math.PI / 2); x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25); y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3; translate = 'translate(' + x + ',' + y + ')'; } return translate; }; /** * @deprecated Use `getRatio('arc', d)` instead. */ ChartInternal.prototype.getArcRatio = function (d) { return this.getRatio('arc', d); }; ChartInternal.prototype.convertToArcData = function (d) { return this.addName({ id: d.data.id, value: d.value, ratio: this.getRatio('arc', d), index: d.index }); }; ChartInternal.prototype.textForArcLabel = function (d) { var $$ = this, updated, value, ratio, id, format; if (!$$.shouldShowArcLabel()) { return ''; } updated = $$.updateAngle(d); value = updated ? updated.value : null; ratio = $$.getRatio('arc', updated); id = d.data.id; if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { return ''; } format = $$.getArcLabelFormat(); return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio); }; ChartInternal.prototype.textForGaugeMinMax = function (value, isMax) { var $$ = this, format = $$.getGaugeLabelExtents(); return format ? format(value, isMax) : value; }; ChartInternal.prototype.expandArc = function (targetIds) { var $$ = this, interval; // MEMO: avoid to cancel transition if ($$.transiting) { interval = window.setInterval(function () { if (!$$.transiting) { window.clearInterval(interval); if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { $$.expandArc(targetIds); } } }, 10); return; } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .each(function (d) { if (!$$.shouldExpand(d.data.id)) { return; } $$.d3 .select(this) .selectAll('path') .transition() .duration($$.expandDuration(d.data.id)) .attr('d', $$.svgArcExpanded) .transition() .duration($$.expandDuration(d.data.id) * 2) .attr('d', $$.svgArcExpandedSub) .each(function (d) { if ($$.isDonutType(d.data)) ; }); }); }; ChartInternal.prototype.unexpandArc = function (targetIds) { var $$ = this; if ($$.transiting) { return; } targetIds = $$.mapToTargetIds(targetIds); $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .selectAll('path') .transition() .duration(function (d) { return $$.expandDuration(d.data.id); }) .attr('d', $$.svgArc); $$.svg.selectAll('.' + CLASS.arc); }; ChartInternal.prototype.expandDuration = function (id) { var $$ = this, config = $$.config; if ($$.isDonutType(id)) { return config.donut_expand_duration; } else if ($$.isGaugeType(id)) { return config.gauge_expand_duration; } else if ($$.isPieType(id)) { return config.pie_expand_duration; } else { return 50; } }; ChartInternal.prototype.shouldExpand = function (id) { var $$ = this, config = $$.config; return (($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand)); }; ChartInternal.prototype.shouldShowArcLabel = function () { var $$ = this, config = $$.config, shouldShow = true; if ($$.hasType('donut')) { shouldShow = config.donut_label_show; } else if ($$.hasType('pie')) { shouldShow = config.pie_label_show; } // when gauge, always true return shouldShow; }; ChartInternal.prototype.meetsArcLabelThreshold = function (ratio) { var $$ = this, config = $$.config, threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold; return ratio >= threshold; }; ChartInternal.prototype.getArcLabelFormat = function () { var $$ = this, config = $$.config, format = config.pie_label_format; if ($$.hasType('gauge')) { format = config.gauge_label_format; } else if ($$.hasType('donut')) { format = config.donut_label_format; } return format; }; ChartInternal.prototype.getGaugeLabelExtents = function () { var $$ = this, config = $$.config; return config.gauge_label_extents; }; ChartInternal.prototype.getArcTitle = function () { var $$ = this; return $$.hasType('donut') ? $$.config.donut_title : ''; }; ChartInternal.prototype.updateTargetsForArc = function (targets) { var $$ = this, main = $$.main, mainPies, mainPieEnter, classChartArc = $$.classChartArc.bind($$), classArcs = $$.classArcs.bind($$), classFocus = $$.classFocus.bind($$); mainPies = main .select('.' + CLASS.chartArcs) .selectAll('.' + CLASS.chartArc) .data($$.pie(targets)) .attr('class', function (d) { return classChartArc(d) + classFocus(d.data); }); mainPieEnter = mainPies .enter() .append('g') .attr('class', classChartArc); mainPieEnter.append('g').attr('class', classArcs); mainPieEnter .append('text') .attr('dy', $$.hasType('gauge') ? '-.1em' : '.35em') .style('opacity', 0) .style('text-anchor', 'middle') .style('pointer-events', 'none'); // MEMO: can not keep same color..., but not bad to update color in redraw //mainPieUpdate.exit().remove(); }; ChartInternal.prototype.initArc = function () { var $$ = this; $$.arcs = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')); $$.arcs .append('text') .attr('class', CLASS.chartArcsTitle) .style('text-anchor', 'middle') .text($$.getArcTitle()); }; ChartInternal.prototype.redrawArc = function (duration, durationForExit, withTransform) { var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, arcs, mainArc, arcLabelLines, mainArcLabelLine, hasGaugeType = $$.hasType('gauge'); arcs = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arc) .data($$.arcData.bind($$)); mainArc = arcs .enter() .append('path') .attr('class', $$.classArc.bind($$)) .style('fill', function (d) { return $$.color(d.data); }) .style('cursor', function (d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? 'pointer' : null; }) .each(function (d) { if ($$.isGaugeType(d.data)) { d.startAngle = d.endAngle = config.gauge_startingAngle; } this._current = d; }) .merge(arcs); if (hasGaugeType) { arcLabelLines = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arcLabelLine) .data($$.arcData.bind($$)); mainArcLabelLine = arcLabelLines .enter() .append('rect') .attr('class', function (d) { return (CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id); }) .merge(arcLabelLines); if ($$.filterTargetsToShow($$.data.targets).length === 1) { mainArcLabelLine.style('display', 'none'); } else { mainArcLabelLine .style('fill', function (d) { return $$.levelColor ? $$.levelColor(d.data.values.reduce(function (total, item) { return total + item.value; }, 0)) : $$.color(d.data); }) .style('display', config.gauge_labelLine_show ? '' : 'none') .each(function (d) { var lineLength = 0, lineThickness = 2, x = 0, y = 0, transform = ''; if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) { var updated = $$.updateAngle(d), innerLineLength = ($$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length) * (updated.index + 1), lineAngle = updated.endAngle - Math.PI / 2, arcInnerRadius = $$.radius - innerLineLength, linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius); lineLength = $$.radiusExpanded - $$.radius + innerLineLength; x = Math.cos(linePositioningAngle) * arcInnerRadius; y = Math.sin(linePositioningAngle) * arcInnerRadius; transform = 'rotate(' + (lineAngle * 180) / Math.PI + ', ' + x + ', ' + y + ')'; } d3.select(this) .attr('x', x) .attr('y', y) .attr('width', lineLength) .attr('height', lineThickness) .attr('transform', transform) .style('stroke-dasharray', '0, ' + (lineLength + lineThickness) + ', 0'); }); } } mainArc .attr('transform', function (d) { return !$$.isGaugeType(d.data) && withTransform ? 'scale(0)' : ''; }) .on('mouseover', config.interaction_enabled ? function (d) { var updated, arcData; if ($$.transiting) { // skip while transiting return; } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.expandArc(updated.data.id); $$.api.focus(updated.data.id); $$.toggleFocusLegend(updated.data.id, true); $$.config.data_onmouseover(arcData, this); } } : null) .on('mousemove', config.interaction_enabled ? function (d) { var updated = $$.updateAngle(d), arcData, selectedData; if (updated) { (arcData = $$.convertToArcData(updated)), (selectedData = [arcData]); $$.showTooltip(selectedData, this); } } : null) .on('mouseout', config.interaction_enabled ? function (d) { var updated, arcData; if ($$.transiting) { // skip while transiting return; } updated = $$.updateAngle(d); if (updated) { arcData = $$.convertToArcData(updated); // transitions $$.unexpandArc(updated.data.id); $$.api.revert(); $$.revertLegend(); $$.hideTooltip(); $$.config.data_onmouseout(arcData, this); } } : null) .on('click', config.interaction_enabled ? function (d, i) { var updated = $$.updateAngle(d), arcData; if (updated) { arcData = $$.convertToArcData(updated); if ($$.toggleShape) { $$.toggleShape(this, arcData, i); } $$.config.data_onclick.call($$.api, arcData, this); } } : null) .each(function () { $$.transiting = true; }) .transition() .duration(duration) .attrTween('d', function (d) { var updated = $$.updateAngle(d), interpolate; if (!updated) { return function () { return 'M 0 0'; }; } // if (this._current === d) { // this._current = { // startAngle: Math.PI*2, // endAngle: Math.PI*2, // }; // } if (isNaN(this._current.startAngle)) { this._current.startAngle = 0; } if (isNaN(this._current.endAngle)) { this._current.endAngle = this._current.startAngle; } interpolate = d3.interpolate(this._current, updated); this._current = interpolate(0); return function (t) { // prevents crashing the charts once in transition and chart.destroy() has been called if ($$.config === null) { return 'M 0 0'; } var interpolated = interpolate(t); interpolated.data = d.data; // data.id will be updated by interporator return $$.getArc(interpolated, true); }; }) .attr('transform', withTransform ? 'scale(1)' : '') .style('fill', function (d) { return $$.levelColor ? $$.levelColor(d.data.values.reduce(function (total, item) { return total + item.value; }, 0)) : $$.color(d.data.id); }) // Where gauge reading color would receive customization. .call($$.endall, function () { $$.transiting = false; }); arcs .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); main .selectAll('.' + CLASS.chartArc) .select('text') .style('opacity', 0) .attr('class', function (d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : ''; }) .text($$.textForArcLabel.bind($$)) .attr('transform', $$.transformForArcLabel.bind($$)) .style('font-size', function (d) { return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : ''; }) .transition() .duration(duration) .style('opacity', function (d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0; }); main .select('.' + CLASS.chartArcsTitle) .style('opacity', $$.hasType('donut') || hasGaugeType ? 1 : 0); if (hasGaugeType) { var index_1 = 0; var backgroundArc = $$.arcs .select('g.' + CLASS.chartArcsBackground) .selectAll('path.' + CLASS.chartArcsBackground) .data($$.data.targets); backgroundArc .enter() .append('path') .attr('class', function (d, i) { return CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i; }) .merge(backgroundArc) .attr('d', function (d1) { if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) { return 'M 0 0'; } var d = { data: [{ value: config.gauge_max }], startAngle: config.gauge_startingAngle, endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1), index: index_1++ }; return $$.getArc(d, true, true); }); backgroundArc.exit().remove(); $$.arcs .select('.' + CLASS.chartArcsGaugeUnit) .attr('dy', '.75em') .text(config.gauge_label_show ? config.gauge_units : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMin) .attr('dx', -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + 'px') .attr('dy', '1.2em') .text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : ''); $$.arcs .select('.' + CLASS.chartArcsGaugeMax) .attr('dx', $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + 'px') .attr('dy', '1.2em') .text(config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : ''); } }; ChartInternal.prototype.initGauge = function () { var arcs = this.arcs; if (this.hasType('gauge')) { arcs.append('g').attr('class', CLASS.chartArcsBackground); arcs .append('text') .attr('class', CLASS.chartArcsGaugeUnit) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMin) .style('text-anchor', 'middle') .style('pointer-events', 'none'); arcs .append('text') .attr('class', CLASS.chartArcsGaugeMax) .style('text-anchor', 'middle') .style('pointer-events', 'none'); } }; ChartInternal.prototype.getGaugeLabelHeight = function () { return this.config.gauge_label_show ? 20 : 0; }; /** * Store value into cache * * @param key * @param value */ ChartInternal.prototype.addToCache = function (key, value) { this.cache["$" + key] = value; }; /** * Returns a cached value or undefined * * @param key * @return {*} */ ChartInternal.prototype.getFromCache = function (key) { return this.cache["$" + key]; }; /** * Reset cached data */ ChartInternal.prototype.resetCache = function () { var _this = this; Object.keys(this.cache) .filter(function (key) { return /^\$/.test(key); }) .forEach(function (key) { delete _this.cache[key]; }); }; // Old API that stores Targets ChartInternal.prototype.hasCaches = function (ids) { for (var i = 0; i < ids.length; i++) { if (!(ids[i] in this.cache)) { return false; } } return true; }; ChartInternal.prototype.addCache = function (id, target) { this.cache[id] = this.cloneTarget(target); }; ChartInternal.prototype.getCaches = function (ids) { var targets = [], i; for (i = 0; i < ids.length; i++) { if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])); } } return targets; }; ChartInternal.prototype.categoryName = function (i) { var config = this.config; return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i; }; ChartInternal.prototype.generateTargetClass = function (targetId) { return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : ''; }; ChartInternal.prototype.generateClass = function (prefix, targetId) { return ' ' + prefix + ' ' + prefix + this.generateTargetClass(targetId); }; ChartInternal.prototype.classText = function (d) { return this.generateClass(CLASS.text, d.index); }; ChartInternal.prototype.classTexts = function (d) { return this.generateClass(CLASS.texts, d.id); }; ChartInternal.prototype.classShape = function (d) { return this.generateClass(CLASS.shape, d.index); }; ChartInternal.prototype.classShapes = function (d) { return this.generateClass(CLASS.shapes, d.id); }; ChartInternal.prototype.classLine = function (d) { return this.classShape(d) + this.generateClass(CLASS.line, d.id); }; ChartInternal.prototype.classLines = function (d) { return this.classShapes(d) + this.generateClass(CLASS.lines, d.id); }; ChartInternal.prototype.classCircle = function (d) { return this.classShape(d) + this.generateClass(CLASS.circle, d.index); }; ChartInternal.prototype.classCircles = function (d) { return this.classShapes(d) + this.generateClass(CLASS.circles, d.id); }; ChartInternal.prototype.classBar = function (d) { return this.classShape(d) + this.generateClass(CLASS.bar, d.index); }; ChartInternal.prototype.classBars = function (d) { return this.classShapes(d) + this.generateClass(CLASS.bars, d.id); }; ChartInternal.prototype.classArc = function (d) { return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id); }; ChartInternal.prototype.classArcs = function (d) { return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id); }; ChartInternal.prototype.classArea = function (d) { return this.classShape(d) + this.generateClass(CLASS.area, d.id); }; ChartInternal.prototype.classAreas = function (d) { return this.classShapes(d) + this.generateClass(CLASS.areas, d.id); }; ChartInternal.prototype.classRegion = function (d, i) { return (this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '')); }; ChartInternal.prototype.classEvent = function (d) { return this.generateClass(CLASS.eventRect, d.index); }; ChartInternal.prototype.classTarget = function (id) { var $$ = this; var additionalClassSuffix = $$.config.data_classes[id], additionalClass = ''; if (additionalClassSuffix) { additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix; } return $$.generateClass(CLASS.target, id) + additionalClass; }; ChartInternal.prototype.classFocus = function (d) { return this.classFocused(d) + this.classDefocused(d); }; ChartInternal.prototype.classFocused = function (d) { return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : ''); }; ChartInternal.prototype.classDefocused = function (d) { return (' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '')); }; ChartInternal.prototype.classChartText = function (d) { return CLASS.chartText + this.classTarget(d.id); }; ChartInternal.prototype.classChartLine = function (d) { return CLASS.chartLine + this.classTarget(d.id); }; ChartInternal.prototype.classChartBar = function (d) { return CLASS.chartBar + this.classTarget(d.id); }; ChartInternal.prototype.classChartArc = function (d) { return CLASS.chartArc + this.classTarget(d.data.id); }; ChartInternal.prototype.getTargetSelectorSuffix = function (targetId) { var targetClass = this.generateTargetClass(targetId); if (window.CSS && window.CSS.escape) { return window.CSS.escape(targetClass); } // fallback on imperfect method for old browsers (does not handles unicode) return targetClass.replace(/([?!@#$%^&*()=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1'); }; ChartInternal.prototype.selectorTarget = function (id, prefix) { return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id); }; ChartInternal.prototype.selectorTargets = function (ids, prefix) { var $$ = this; ids = ids || []; return ids.length ? ids.map(function (id) { return $$.selectorTarget(id, prefix); }) : null; }; ChartInternal.prototype.selectorLegend = function (id) { return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id); }; ChartInternal.prototype.selectorLegends = function (ids) { var $$ = this; return ids && ids.length ? ids.map(function (id) { return $$.selectorLegend(id); }) : null; }; ChartInternal.prototype.getClipPath = function (id) { return 'url(' + (isIE(9) ? '' : document.URL.split('#')[0]) + '#' + id + ')'; }; ChartInternal.prototype.appendClip = function (parent, id) { return parent .append('clipPath') .attr('id', id) .append('rect'); }; ChartInternal.prototype.getAxisClipX = function (forHorizontal) { // axis line width + padding for left var left = Math.max(30, this.margin.left); return forHorizontal ? -(1 + left) : -(left - 1); }; ChartInternal.prototype.getAxisClipY = function (forHorizontal) { return forHorizontal ? -20 : -this.margin.top; }; ChartInternal.prototype.getXAxisClipX = function () { var $$ = this; return $$.getAxisClipX(!$$.config.axis_rotated); }; ChartInternal.prototype.getXAxisClipY = function () { var $$ = this; return $$.getAxisClipY(!$$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipX = function () { var $$ = this; return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipY = function () { var $$ = this; return $$.getAxisClipY($$.config.axis_rotated); }; ChartInternal.prototype.getAxisClipWidth = function (forHorizontal) { var $$ = this, left = Math.max(30, $$.margin.left), right = Math.max(30, $$.margin.right); // width + axis line width + padding for left/right return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20; }; ChartInternal.prototype.getAxisClipHeight = function (forHorizontal) { // less than 20 is not enough to show the axis label 'outer' without legend return ((forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20); }; ChartInternal.prototype.getXAxisClipWidth = function () { var $$ = this; return $$.getAxisClipWidth(!$$.config.axis_rotated); }; ChartInternal.prototype.getXAxisClipHeight = function () { var $$ = this; return $$.getAxisClipHeight(!$$.config.axis_rotated); }; ChartInternal.prototype.getYAxisClipWidth = function () { var $$ = this; return ($$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0)); }; ChartInternal.prototype.getYAxisClipHeight = function () { var $$ = this; return $$.getAxisClipHeight($$.config.axis_rotated); }; ChartInternal.prototype.generateColor = function () { var $$ = this, config = $$.config, d3 = $$.d3, colors = config.data_colors, pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10, callback = config.data_color, ids = []; return function (d) { var id = d.id || (d.data && d.data.id) || d, color; // if callback function is provided if (colors[id] instanceof Function) { color = colors[id](d); } // if specified, choose that color else if (colors[id]) { color = colors[id]; } // if not specified, choose from pattern else { if (ids.indexOf(id) < 0) { ids.push(id); } color = pattern[ids.indexOf(id) % pattern.length]; colors[id] = color; } return callback instanceof Function ? callback(color, d) : color; }; }; ChartInternal.prototype.generateLevelColor = function () { var $$ = this, config = $$.config, colors = config.color_pattern, threshold = config.color_threshold, asValue = threshold.unit === 'value', values = threshold.values && threshold.values.length ? threshold.values : [], max = threshold.max || 100; return notEmpty(threshold) && notEmpty(colors) ? function (value) { var i, v, color = colors[colors.length - 1]; for (i = 0; i < values.length; i++) { v = asValue ? value : (value * 100) / max; if (v < values[i]) { color = colors[i]; break; } } return color; } : null; }; ChartInternal.prototype.getDefaultConfig = function () { var config = { bindto: '#chart', svg_classname: undefined, size_width: undefined, size_height: undefined, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, resize_auto: true, zoom_enabled: false, zoom_initialRange: undefined, zoom_type: 'scroll', zoom_disableDefaultBehavior: false, zoom_privileged: false, zoom_rescale: false, zoom_onzoom: function () { }, zoom_onzoomstart: function () { }, zoom_onzoomend: function () { }, zoom_x_min: undefined, zoom_x_max: undefined, interaction_brighten: true, interaction_enabled: true, onmouseover: function () { }, onmouseout: function () { }, onresize: function () { }, onresized: function () { }, oninit: function () { }, onrendered: function () { }, transition_duration: 350, data_epochs: 'epochs', data_x: undefined, data_xs: {}, data_xFormat: '%Y-%m-%d', data_xLocaltime: true, data_xSort: true, data_idConverter: function (id) { return id; }, data_names: {}, data_classes: {}, data_groups: [], data_axes: {}, data_type: undefined, data_types: {}, data_labels: {}, data_order: 'desc', data_regions: {}, data_color: undefined, data_colors: {}, data_hide: false, data_filter: undefined, data_selection_enabled: false, data_selection_grouped: false, data_selection_isselectable: function () { return true; }, data_selection_multiple: true, data_selection_draggable: false, data_stack_normalize: false, data_onclick: function () { }, data_onmouseover: function () { }, data_onmouseout: function () { }, data_onselected: function () { }, data_onunselected: function () { }, data_url: undefined, data_headers: undefined, data_json: undefined, data_rows: undefined, data_columns: undefined, data_mimeType: undefined, data_keys: undefined, // configuration for no plot-able data supplied. data_empty_label_text: '', // subchart subchart_show: false, subchart_size_height: 60, subchart_axis_x_show: true, subchart_onbrush: function () { }, // color color_pattern: [], color_threshold: {}, // legend legend_show: true, legend_hide: false, legend_position: 'bottom', legend_inset_anchor: 'top-left', legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, legend_item_onclick: undefined, legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, axis_x_type: 'indexed', axis_x_localtime: true, axis_x_categories: [], axis_x_tick_centered: false, axis_x_tick_format: undefined, axis_x_tick_culling: {}, axis_x_tick_culling_max: 10, axis_x_tick_count: undefined, axis_x_tick_fit: true, axis_x_tick_values: null, axis_x_tick_rotate: 0, axis_x_tick_outer: true, axis_x_tick_multiline: true, axis_x_tick_multilineMax: 0, axis_x_tick_width: null, axis_x_max: undefined, axis_x_min: undefined, axis_x_padding: {}, axis_x_height: undefined, axis_x_selection: undefined, axis_x_label: {}, axis_x_inner: undefined, axis_y_show: true, axis_y_type: 'linear', axis_y_max: undefined, axis_y_min: undefined, axis_y_inverted: false, axis_y_center: undefined, axis_y_inner: undefined, axis_y_label: {}, axis_y_tick_format: undefined, axis_y_tick_outer: true, axis_y_tick_values: null, axis_y_tick_rotate: 0, axis_y_tick_count: undefined, axis_y_tick_time_type: undefined, axis_y_tick_time_interval: undefined, axis_y_padding: {}, axis_y_default: undefined, axis_y2_show: false, axis_y2_type: 'linear', axis_y2_max: undefined, axis_y2_min: undefined, axis_y2_inverted: false, axis_y2_center: undefined, axis_y2_inner: undefined, axis_y2_label: {}, axis_y2_tick_format: undefined, axis_y2_tick_outer: true, axis_y2_tick_values: null, axis_y2_tick_count: undefined, axis_y2_padding: {}, axis_y2_default: undefined, // grid grid_x_show: false, grid_x_type: 'tick', grid_x_lines: [], grid_y_show: false, // not used // grid_y_type: 'tick', grid_y_lines: [], grid_y_ticks: 10, grid_focus_show: true, grid_lines_front: true, // point - point of each data point_show: true, point_r: 2.5, point_sensitivity: 10, point_focus_expand_enabled: true, point_focus_expand_r: undefined, point_select_r: undefined, // line line_connectNull: false, line_step_type: 'step', // bar bar_width: undefined, bar_width_ratio: 0.6, bar_width_max: undefined, bar_zerobased: true, bar_space: 0, // area area_zerobased: true, area_above: false, // pie pie_label_show: true, pie_label_format: undefined, pie_label_threshold: 0.05, pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, pie_padAngle: 0, // gauge gauge_fullCircle: false, gauge_label_show: true, gauge_labelLine_show: true, gauge_label_format: undefined, gauge_min: 0, gauge_max: 100, gauge_startingAngle: (-1 * Math.PI) / 2, gauge_label_extents: undefined, gauge_units: undefined, gauge_width: undefined, gauge_arcs_minWidth: 5, gauge_expand: {}, gauge_expand_duration: 50, // donut donut_label_show: true, donut_label_format: undefined, donut_label_threshold: 0.05, donut_label_ratio: undefined, donut_width: undefined, donut_title: '', donut_expand: {}, donut_expand_duration: 50, donut_padAngle: 0, // spline spline_interpolation_type: 'cardinal', // stanford stanford_lines: [], stanford_regions: [], stanford_texts: [], stanford_scaleMin: undefined, stanford_scaleMax: undefined, stanford_scaleWidth: undefined, stanford_scaleFormat: undefined, stanford_scaleValues: undefined, stanford_colors: undefined, stanford_padding: { top: 0, right: 0, bottom: 0, left: 0 }, // region - region to change style regions: [], // tooltip - show when mouseover on each data tooltip_show: true, tooltip_grouped: true, tooltip_order: undefined, tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, tooltip_horizontal: undefined, tooltip_position: undefined, tooltip_contents: function (d, defaultTitleFormat, defaultValueFormat, color) { return this.getTooltipContent ? this.getTooltipContent(d, defaultTitleFormat, defaultValueFormat, color) : ''; }, tooltip_init_show: false, tooltip_init_x: 0, tooltip_init_position: { top: '0px', left: '50px' }, tooltip_onshow: function () { }, tooltip_onhide: function () { }, // title title_text: undefined, title_padding: { top: 0, right: 0, bottom: 0, left: 0 }, title_position: 'top-center' }; Object.keys(this.additionalConfig).forEach(function (key) { config[key] = this.additionalConfig[key]; }, this); return config; }; ChartInternal.prototype.additionalConfig = {}; ChartInternal.prototype.loadConfig = function (config) { var this_config = this.config, target, keys, read; function find() { var key = keys.shift(); // console.log("key =>", key, ", target =>", target); if (key && target && typeof target === 'object' && key in target) { target = target[key]; return find(); } else if (!key) { return target; } else { return undefined; } } Object.keys(this_config).forEach(function (key) { target = config; keys = key.split('_'); read = find(); // console.log("CONFIG : ", key, read); if (isDefined(read)) { this_config[key] = read; } }); }; ChartInternal.prototype.convertUrlToData = function (url, mimeType, headers, keys, done) { var $$ = this, type = mimeType ? mimeType : 'csv', f, converter; if (type === 'json') { f = $$.d3.json; converter = $$.convertJsonToData; } else if (type === 'tsv') { f = $$.d3.tsv; converter = $$.convertXsvToData; } else { f = $$.d3.csv; converter = $$.convertXsvToData; } f(url, headers) .then(function (data) { done.call($$, converter.call($$, data, keys)); }) .catch(function (error) { throw error; }); }; ChartInternal.prototype.convertXsvToData = function (xsv) { var keys = xsv.columns, rows = xsv; if (rows.length === 0) { return { keys: keys, rows: [keys.reduce(function (row, key) { var _a; return Object.assign(row, (_a = {}, _a[key] = null, _a)); }, {})] }; } else { // [].concat() is to convert result into a plain array otherwise // test is not happy because rows have properties. return { keys: keys, rows: [].concat(xsv) }; } }; ChartInternal.prototype.convertJsonToData = function (json, keys) { var $$ = this, new_rows = [], targetKeys, data; if (keys) { // when keys specified, json would be an array that includes objects if (keys.x) { targetKeys = keys.value.concat(keys.x); $$.config.data_x = keys.x; } else { targetKeys = keys.value; } new_rows.push(targetKeys); json.forEach(function (o) { var new_row = []; targetKeys.forEach(function (key) { // convert undefined to null because undefined data will be removed in convertDataToTargets() var v = $$.findValueInJson(o, key); if (isUndefined(v)) { v = null; } new_row.push(v); }); new_rows.push(new_row); }); data = $$.convertRowsToData(new_rows); } else { Object.keys(json).forEach(function (key) { new_rows.push([key].concat(json[key])); }); data = $$.convertColumnsToData(new_rows); } return data; }; /** * Finds value from the given nested object by the given path. * If it's not found, then this returns undefined. * @param {Object} object the object * @param {string} path the path */ ChartInternal.prototype.findValueInJson = function (object, path) { if (path in object) { // If object has a key that contains . or [], return the key's value // instead of searching for an inner object. // See https://github.com/c3js/c3/issues/1691 for details. return object[path]; } path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties (replace [] with .) path = path.replace(/^\./, ''); // strip a leading dot var pathArray = path.split('.'); for (var i = 0; i < pathArray.length; ++i) { var k = pathArray[i]; if (k in object) { object = object[k]; } else { return; } } return object; }; /** * Converts the rows to normalized data. * @param {any[][]} rows The row data * @return {Object} */ ChartInternal.prototype.convertRowsToData = function (rows) { var newRows = []; var keys = rows[0]; for (var i = 1; i < rows.length; i++) { var newRow = {}; for (var j = 0; j < rows[i].length; j++) { if (isUndefined(rows[i][j])) { throw new Error('Source data is missing a component at (' + i + ',' + j + ')!'); } newRow[keys[j]] = rows[i][j]; } newRows.push(newRow); } return { keys: keys, rows: newRows }; }; /** * Converts the columns to normalized data. * @param {any[][]} columns The column data * @return {Object} */ ChartInternal.prototype.convertColumnsToData = function (columns) { var newRows = []; var keys = []; for (var i = 0; i < columns.length; i++) { var key = columns[i][0]; for (var j = 1; j < columns[i].length; j++) { if (isUndefined(newRows[j - 1])) { newRows[j - 1] = {}; } if (isUndefined(columns[i][j])) { throw new Error('Source data is missing a component at (' + i + ',' + j + ')!'); } newRows[j - 1][key] = columns[i][j]; } keys.push(key); } return { keys: keys, rows: newRows }; }; /** * Converts the data format into the target format. * @param {!Object} data * @param {!Array} data.keys Ordered list of target IDs. * @param {!Array} data.rows Rows of data to convert. * @param {boolean} appendXs True to append to $$.data.xs, False to replace. * @return {!Array} */ ChartInternal.prototype.convertDataToTargets = function (data, appendXs) { var $$ = this, config = $$.config, targets, ids, xs, keys, epochs; // handles format where keys are not orderly provided if (isArray(data)) { keys = Object.keys(data[0]); } else { keys = data.keys; data = data.rows; } xs = keys.filter($$.isX, $$); if (!$$.isStanfordGraphType()) { ids = keys.filter($$.isNotX, $$); } else { epochs = keys.filter($$.isEpochs, $$); ids = keys.filter($$.isNotXAndNotEpochs, $$); if (xs.length !== 1 || epochs.length !== 1 || ids.length !== 1) { throw new Error("You must define the 'x' key name and the 'epochs' for Stanford Diagrams"); } } // save x for update data by load when custom x and c3.x API ids.forEach(function (id) { var xKey = $$.getXKey(id); if ($$.isCustomX() || $$.isTimeSeries()) { // if included in input data if (xs.indexOf(xKey) >= 0) { $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : []).concat(data .map(function (d) { return d[xKey]; }) .filter(isValue) .map(function (rawX, i) { return $$.generateTargetX(rawX, id, i); })); } // if not included in input data, find from preloaded data of other id's x else if (config.data_x) { $$.data.xs[id] = $$.getOtherTargetXs(); } // if not included in input data, find from preloaded data else if (notEmpty(config.data_xs)) { $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets); } // MEMO: if no x included, use same x of current will be used } else { $$.data.xs[id] = data.map(function (d, i) { return i; }); } }); // check x is defined ids.forEach(function (id) { if (!$$.data.xs[id]) { throw new Error('x is not defined for id = "' + id + '".'); } }); // convert to target targets = ids.map(function (id, index) { var convertedId = config.data_idConverter(id); return { id: convertedId, id_org: id, values: data .map(function (d, i) { var xKey = $$.getXKey(id), rawX = d[xKey], value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x, returnData; // use x as categories if custom x and categorized if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { if (index === 0 && i === 0) { config.axis_x_categories = []; } x = config.axis_x_categories.indexOf(rawX); if (x === -1) { x = config.axis_x_categories.length; config.axis_x_categories.push(rawX); } } else { x = $$.generateTargetX(rawX, id, i); } // mark as x = undefined if value is undefined and filter to remove after mapped if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { x = undefined; } returnData = { x: x, value: value, id: convertedId }; if ($$.isStanfordGraphType()) { returnData.epochs = d[epochs]; } return returnData; }) .filter(function (v) { return isDefined(v.x); }) }; }); // finish targets targets.forEach(function (t) { var i; // sort values by its x if (config.data_xSort) { t.values = t.values.sort(function (v1, v2) { var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, x2 = v2.x || v2.x === 0 ? v2.x : Infinity; return x1 - x2; }); } // indexing each value i = 0; t.values.forEach(function (v) { v.index = i++; }); // this needs to be sorted because its index and value.index is identical $$.data.xs[t.id].sort(function (v1, v2) { return v1 - v2; }); }); // cache information about values $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets); $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets); // set target types if (config.data_type) { $$.setTargetType($$.mapToIds(targets).filter(function (id) { return !(id in config.data_types); }), config.data_type); } // cache as original id keyed targets.forEach(function (d) { $$.addCache(d.id_org, d); }); return targets; }; ChartInternal.prototype.isEpochs = function (key) { var $$ = this, config = $$.config; return config.data_epochs && key === config.data_epochs; }; ChartInternal.prototype.isX = function (key) { var $$ = this, config = $$.config; return ((config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key))); }; ChartInternal.prototype.isNotX = function (key) { return !this.isX(key); }; ChartInternal.prototype.isNotXAndNotEpochs = function (key) { return !this.isX(key) && !this.isEpochs(key); }; /** * Returns whether the normalized stack option is enabled or not. * * To be enabled it must also have data.groups defined. * * @return {boolean} */ ChartInternal.prototype.isStackNormalized = function () { return this.config.data_stack_normalize && this.config.data_groups.length > 0; }; /** * Returns whether the axis is normalized or not. * * An axis is normalized as long as one of its associated target * is normalized. * * @param axisId Axis ID (y or y2) * @return {Boolean} */ ChartInternal.prototype.isAxisNormalized = function (axisId) { var $$ = this; if (!$$.isStackNormalized()) { // shortcut return false; } return $$.data.targets .filter(function (target) { return $$.axis.getId(target.id) === axisId; }) .some(function (target) { return $$.isTargetNormalized(target.id); }); }; /** * Returns whether the values for this target ID is normalized or not. * * To be normalized the option needs to be enabled and target needs * to be defined in `data.groups`. * * @param targetId ID of the target * @return {Boolean} True if the target is normalized, false otherwise. */ ChartInternal.prototype.isTargetNormalized = function (targetId) { var $$ = this; return ($$.isStackNormalized() && $$.config.data_groups.some(function (group) { return group.includes(targetId); })); }; ChartInternal.prototype.getXKey = function (id) { var $$ = this, config = $$.config; return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null; }; /** * Get sum of visible data per index for given axis. * * Expect axisId to be either 'y' or 'y2'. * * @private * @param axisId Compute sum for data associated to given axis. * @return {Array} */ ChartInternal.prototype.getTotalPerIndex = function (axisId) { var $$ = this; if (!$$.isStackNormalized()) { return null; } var cached = $$.getFromCache('getTotalPerIndex'); if (cached !== undefined) { return cached[axisId]; } var sum = { y: [], y2: [] }; $$.data.targets // keep only target that are normalized .filter(function (target) { return $$.isTargetNormalized(target.id); }) // keep only target that are visible .filter(function (target) { return $$.isTargetToShow(target.id); }) // compute sum per axis .forEach(function (target) { var sumByAxis = sum[$$.axis.getId(target.id)]; target.values.forEach(function (v, i) { if (!sumByAxis[i]) { sumByAxis[i] = 0; } sumByAxis[i] += isNumber(v.value) ? v.value : 0; }); }); $$.addToCache('getTotalPerIndex', sum); return sum[axisId]; }; /** * Get sum of visible data. * * Should be used for normalised data only since all values * are expected to be positive. * * @private * @return {Number} */ ChartInternal.prototype.getTotalDataSum = function () { var $$ = this; var cached = $$.getFromCache('getTotalDataSum'); if (cached !== undefined) { return cached; } var totalDataSum = flattenArray($$.data.targets .filter(function (target) { return $$.isTargetToShow(target.id); }) .map(function (target) { return target.values; })) .map(function (d) { return d.value; }) .reduce(function (p, c) { return p + c; }, 0); $$.addToCache('getTotalDataSum', totalDataSum); return totalDataSum; }; ChartInternal.prototype.getXValuesOfXKey = function (key, targets) { var $$ = this, xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : []; ids.forEach(function (id) { if ($$.getXKey(id) === key) { xValues = $$.data.xs[id]; } }); return xValues; }; ChartInternal.prototype.getXValue = function (id, i) { var $$ = this; return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i; }; ChartInternal.prototype.getOtherTargetXs = function () { var $$ = this, idsForX = Object.keys($$.data.xs); return idsForX.length ? $$.data.xs[idsForX[0]] : null; }; ChartInternal.prototype.getOtherTargetX = function (index) { var xs = this.getOtherTargetXs(); return xs && index < xs.length ? xs[index] : null; }; ChartInternal.prototype.addXs = function (xs) { var $$ = this; Object.keys(xs).forEach(function (id) { $$.config.data_xs[id] = xs[id]; }); }; ChartInternal.prototype.addName = function (data) { var $$ = this, name; if (data) { name = $$.config.data_names[data.id]; data.name = name !== undefined ? name : data.id; } return data; }; ChartInternal.prototype.getValueOnIndex = function (values, index) { var valueOnIndex = values.filter(function (v) { return v.index === index; }); return valueOnIndex.length ? valueOnIndex[0] : null; }; ChartInternal.prototype.updateTargetX = function (targets, x) { var $$ = this; targets.forEach(function (t) { t.values.forEach(function (v, i) { v.x = $$.generateTargetX(x[i], t.id, i); }); $$.data.xs[t.id] = x; }); }; ChartInternal.prototype.updateTargetXs = function (targets, xs) { var $$ = this; targets.forEach(function (t) { if (xs[t.id]) { $$.updateTargetX([t], xs[t.id]); } }); }; ChartInternal.prototype.generateTargetX = function (rawX, id, index) { var $$ = this, x; if ($$.isTimeSeries()) { x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)); } else if ($$.isCustomX() && !$$.isCategorized()) { x = isValue(rawX) ? +rawX : $$.getXValue(id, index); } else { x = index; } return x; }; ChartInternal.prototype.cloneTarget = function (target) { return { id: target.id, id_org: target.id_org, values: target.values.map(function (d) { return { x: d.x, value: d.value, id: d.id }; }) }; }; ChartInternal.prototype.getMaxDataCount = function () { var $$ = this; return $$.d3.max($$.data.targets, function (t) { return t.values.length; }); }; ChartInternal.prototype.mapToIds = function (targets) { return targets.map(function (d) { return d.id; }); }; ChartInternal.prototype.mapToTargetIds = function (ids) { var $$ = this; return ids ? [].concat(ids) : $$.mapToIds($$.data.targets); }; ChartInternal.prototype.hasTarget = function (targets, id) { var ids = this.mapToIds(targets), i; for (i = 0; i < ids.length; i++) { if (ids[i] === id) { return true; } } return false; }; ChartInternal.prototype.isTargetToShow = function (targetId) { return this.hiddenTargetIds.indexOf(targetId) < 0; }; ChartInternal.prototype.isLegendToShow = function (targetId) { return this.hiddenLegendIds.indexOf(targetId) < 0; }; /** * Returns only visible targets. * * This is the same as calling {@link filterTargetsToShow} on $$.data.targets. * * @return {Array} */ ChartInternal.prototype.getTargetsToShow = function () { var $$ = this; return $$.filterTargetsToShow($$.data.targets); }; ChartInternal.prototype.filterTargetsToShow = function (targets) { var $$ = this; return targets.filter(function (t) { return $$.isTargetToShow(t.id); }); }; /** * @return {Array} Returns all the targets attached to the chart, visible or not */ ChartInternal.prototype.getTargets = function () { var $$ = this; return $$.data.targets; }; ChartInternal.prototype.mapTargetsToUniqueXs = function (targets) { var $$ = this; var xs = $$.d3 .set($$.d3.merge(targets.map(function (t) { return t.values.map(function (v) { return +v.x; }); }))) .values(); xs = $$.isTimeSeries() ? xs.map(function (x) { return new Date(+x); }) : xs.map(function (x) { return +x; }); return xs.sort(function (a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; }); }; ChartInternal.prototype.addHiddenTargetIds = function (targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]); } } this.resetCache(); }; ChartInternal.prototype.removeHiddenTargetIds = function (targetIds) { this.hiddenTargetIds = this.hiddenTargetIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); this.resetCache(); }; ChartInternal.prototype.addHiddenLegendIds = function (targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds); for (var i = 0; i < targetIds.length; i++) { if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]); } } }; ChartInternal.prototype.removeHiddenLegendIds = function (targetIds) { this.hiddenLegendIds = this.hiddenLegendIds.filter(function (id) { return targetIds.indexOf(id) < 0; }); }; ChartInternal.prototype.getValuesAsIdKeyed = function (targets) { var ys = {}; targets.forEach(function (t) { ys[t.id] = []; t.values.forEach(function (v) { ys[t.id].push(v.value); }); }); return ys; }; ChartInternal.prototype.checkValueInTargets = function (targets, checker) { var ids = Object.keys(targets), i, j, values; for (i = 0; i < ids.length; i++) { values = targets[ids[i]].values; for (j = 0; j < values.length; j++) { if (checker(values[j].value)) { return true; } } } return false; }; ChartInternal.prototype.hasNegativeValueInTargets = function (targets) { return this.checkValueInTargets(targets, function (v) { return v < 0; }); }; ChartInternal.prototype.hasPositiveValueInTargets = function (targets) { return this.checkValueInTargets(targets, function (v) { return v > 0; }); }; ChartInternal.prototype.isOrderDesc = function () { var config = this.config; return (typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc'); }; ChartInternal.prototype.isOrderAsc = function () { var config = this.config; return (typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc'); }; ChartInternal.prototype.getOrderFunction = function () { var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc(); if (orderAsc || orderDesc) { var reducer = function (p, c) { return p + Math.abs(c.value); }; return function (t1, t2) { var t1Sum = t1.values.reduce(reducer, 0), t2Sum = t2.values.reduce(reducer, 0); return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum; }; } else if (isFunction(config.data_order)) { return config.data_order; } else if (isArray(config.data_order)) { var order = config.data_order; return function (t1, t2) { return order.indexOf(t1.id) - order.indexOf(t2.id); }; } }; ChartInternal.prototype.orderTargets = function (targets) { var fct = this.getOrderFunction(); if (fct) { targets.sort(fct); } return targets; }; /** * Returns all the values from the given targets at the given index. * * @param {Array} targets * @param {Number} index * @return {Array} */ ChartInternal.prototype.filterByIndex = function (targets, index) { return this.d3.merge(targets.map(function (t) { return t.values.filter(function (v) { return v.index === index; }); })); }; ChartInternal.prototype.filterByX = function (targets, x) { return this.d3 .merge(targets.map(function (t) { return t.values; })) .filter(function (v) { return v.x - x === 0; }); }; ChartInternal.prototype.filterRemoveNull = function (data) { return data.filter(function (d) { return isValue(d.value); }); }; ChartInternal.prototype.filterByXDomain = function (targets, xDomain) { return targets.map(function (t) { return { id: t.id, id_org: t.id_org, values: t.values.filter(function (v) { return xDomain[0] <= v.x && v.x <= xDomain[1]; }) }; }); }; ChartInternal.prototype.hasDataLabel = function () { var config = this.config; if (typeof config.data_labels === 'boolean' && config.data_labels) { return true; } else if (typeof config.data_labels === 'object' && notEmpty(config.data_labels)) { return true; } return false; }; ChartInternal.prototype.getDataLabelLength = function (min, max, key) { var $$ = this, lengths = [0, 0], paddingCoef = 1.3; $$.selectChart .select('svg') .selectAll('.dummy') .data([min, max]) .enter() .append('text') .text(function (d) { return $$.dataLabelFormat(d.id)(d); }) .each(function (d, i) { lengths[i] = getBBox(this)[key] * paddingCoef; }) .remove(); return lengths; }; /** * Returns true if the given data point is not arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isNoneArc = function (d) { return this.hasTarget(this.data.targets, d.id); }; /** * Returns true if the given data point is arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isArc = function (d) { return 'data' in d && this.hasTarget(this.data.targets, d.data.id); }; /** * Find the closest point from the given pos among the given targets or * undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} pos An [x,y] coordinate * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargets = function (targets, pos) { var $$ = this; // for each target, find the closest point var candidates = targets .map(function (t) { return $$.findClosest(t.values, pos, $$.config.tooltip_horizontal ? $$.horizontalDistance.bind($$) : $$.dist.bind($$), $$.config.point_sensitivity); }) .filter(function (v) { return v; }); // returns the closest of candidates if (candidates.length === 0) { return undefined; } else if (candidates.length === 1) { return candidates[0]; } else { return $$.findClosest(candidates, pos, $$.dist.bind($$)); } }; /** * Find the closest point from the x value or undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} x A value on X axis * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargetsByX = function (targets, x) { var closest; var diff; targets.forEach(function (t) { t.values.forEach(function (d) { var newDiff = Math.abs(x - d.x); if (diff === undefined || newDiff < diff) { closest = d; diff = newDiff; } }); }); return closest; }; /** * Using given compute distance method, returns the closest data point from the * given position. * * Giving optionally a minimum distance to satisfy. * * @param {Array} dataPoints List of DataPoints * @param {Array} pos An [x,y] coordinate * @param {Function} computeDist Function to compute distance between 2 points * @param {Number} minDist Minimal distance to satisfy * @return {Object|undefined} Closest data point */ ChartInternal.prototype.findClosest = function (dataPoints, pos, computeDist, minDist) { if (minDist === void 0) { minDist = Infinity; } var $$ = this; var closest; // find closest bar dataPoints .filter(function (v) { return v && $$.isBarType(v.id); }) .forEach(function (v) { if (!closest) { var shape = $$.main .select('.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index) .node(); if ($$.isWithinBar(pos, shape)) { closest = v; } } }); // find closest point from non-bar dataPoints .filter(function (v) { return v && !$$.isBarType(v.id); }) .forEach(function (v) { var d = computeDist(v, pos); if (d < minDist) { minDist = d; closest = v; } }); return closest; }; ChartInternal.prototype.dist = function (data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, yIndex = config.axis_rotated ? 0 : 1, y = $$.circleY(data, data.index), x = $$.x(data.x); return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)); }; ChartInternal.prototype.horizontalDistance = function (data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, x = $$.x(data.x); return Math.abs(x - pos[xIndex]); }; ChartInternal.prototype.convertValuesToStep = function (values) { var converted = [].concat(values), i; if (!this.isCategorized()) { return values; } for (i = values.length + 1; 0 < i; i--) { converted[i] = converted[i - 1]; } converted[0] = { x: converted[0].x - 1, value: converted[0].value, id: converted[0].id }; converted[values.length + 1] = { x: converted[values.length].x + 1, value: converted[values.length].value, id: converted[values.length].id }; return converted; }; /** * Get ratio value * * @param {String} type Ratio for given type * @param {Object} d Data value object * @param {Boolean} asPercent Convert the return as percent or not * @return {Number} Ratio value * @private */ ChartInternal.prototype.getRatio = function (type, d, asPercent) { if (asPercent === void 0) { asPercent = false; } var $$ = this; var api = $$.api; var ratio = 0; if (d && api.data.shown.call(api).length) { ratio = d.ratio || d.value; if (type === 'arc') { if ($$.hasType('gauge')) { ratio = (d.endAngle - d.startAngle) / (Math.PI * ($$.config.gauge_fullCircle ? 2 : 1)); } else { var total = $$.getTotalDataSum(); ratio = d.value / total; } } else if (type === 'index') { var total = $$.getTotalPerIndex($$.axis.getId(d.id)); d.ratio = isNumber(d.value) && total && total[d.index] > 0 ? d.value / total[d.index] : 0; ratio = d.ratio; } } return asPercent && ratio ? ratio * 100 : ratio; }; ChartInternal.prototype.updateDataAttributes = function (name, attrs) { var $$ = this, config = $$.config, current = config['data_' + name]; if (typeof attrs === 'undefined') { return current; } Object.keys(attrs).forEach(function (id) { current[id] = attrs[id]; }); $$.redraw({ withLegend: true }); return current; }; ChartInternal.prototype.load = function (targets, args) { var $$ = this; if (targets) { // filter loading targets if needed if (args.filter) { targets = targets.filter(args.filter); } // set type if args.types || args.type specified if (args.type || args.types) { targets.forEach(function (t) { var type = args.types && args.types[t.id] ? args.types[t.id] : args.type; $$.setTargetType(t.id, type); }); } // Update/Add data $$.data.targets.forEach(function (d) { for (var i = 0; i < targets.length; i++) { if (d.id === targets[i].id) { d.values = targets[i].values; targets.splice(i, 1); break; } } }); $$.data.targets = $$.data.targets.concat(targets); // add remained } // Set targets $$.updateTargets($$.data.targets); // Redraw with new targets $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }); if (args.done) { args.done(); } }; ChartInternal.prototype.loadFromArgs = function (args) { var $$ = this; $$.resetCache(); if (args.data) { $$.load($$.convertDataToTargets(args.data), args); } else if (args.url) { $$.convertUrlToData(args.url, args.mimeType, args.headers, args.keys, function (data) { $$.load($$.convertDataToTargets(data), args); }); } else if (args.json) { $$.load($$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args); } else if (args.rows) { $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args); } else if (args.columns) { $$.load($$.convertDataToTargets($$.convertColumnsToData(args.columns)), args); } else { $$.load(null, args); } }; ChartInternal.prototype.unload = function (targetIds, done) { var $$ = this; $$.resetCache(); if (!done) { done = function () { }; } // filter existing target targetIds = targetIds.filter(function (id) { return $$.hasTarget($$.data.targets, id); }); // If no target, call done and return if (!targetIds || targetIds.length === 0) { done(); return; } $$.svg .selectAll(targetIds.map(function (id) { return $$.selectorTarget(id); })) .transition() .style('opacity', 0) .remove() .call($$.endall, done); targetIds.forEach(function (id) { // Reset fadein for future load $$.withoutFadeIn[id] = false; // Remove target's elements if ($$.legend) { $$.legend .selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)) .remove(); } // Remove target $$.data.targets = $$.data.targets.filter(function (t) { return t.id !== id; }); }); }; ChartInternal.prototype.getYDomainMin = function (targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasNegativeValue; if (config.data_groups.length > 0) { hasNegativeValue = $$.hasNegativeValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); if (idsInGroup.length === 0) { continue; } baseId = idsInGroup[0]; // Consider negative values if (hasNegativeValue && ys[baseId]) { ys[baseId].forEach(function (v, i) { ys[baseId][i] = v < 0 ? v : 0; }); } // Compute min for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue; } ys[id].forEach(function (v, i) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0)) { ys[baseId][i] += +v; } }); } } } return $$.d3.min(Object.keys(ys).map(function (key) { return $$.d3.min(ys[key]); })); }; ChartInternal.prototype.getYDomainMax = function (targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasPositiveValue; if (config.data_groups.length > 0) { hasPositiveValue = $$.hasPositiveValueInTargets(targets); for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function (id) { return ids.indexOf(id) >= 0; }); if (idsInGroup.length === 0) { continue; } baseId = idsInGroup[0]; // Consider positive values if (hasPositiveValue && ys[baseId]) { ys[baseId].forEach(function (v, i) { ys[baseId][i] = v > 0 ? v : 0; }); } // Compute max for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k]; if (!ys[id]) { continue; } ys[id].forEach(function (v, i) { if ($$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0)) { ys[baseId][i] += +v; } }); } } } return $$.d3.max(Object.keys(ys).map(function (key) { return $$.d3.max(ys[key]); })); }; ChartInternal.prototype.getYDomain = function (targets, axisId, xDomain) { var $$ = this, config = $$.config; if ($$.isAxisNormalized(axisId)) { return [0, 100]; } var targetsByAxisId = targets.filter(function (t) { return $$.axis.getId(t.id) === axisId; }), yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yDomainMin = $$.getYDomainMin(yTargets), yDomainMax = $$.getYDomainMax(yTargets), domain, domainLength, padding_top, padding_bottom, center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated; // MEMO: avoid inverting domain unexpectedly yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin; yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax; if (yTargets.length === 0) { // use current domain if target of axisId is none return axisId === 'y2' ? $$.y2.domain() : $$.y.domain(); } if (isNaN(yDomainMin)) { // set minimum to zero when not number yDomainMin = 0; } if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin yDomainMax = yDomainMin; } if (yDomainMin === yDomainMax) { yDomainMin < 0 ? (yDomainMax = 0) : (yDomainMin = 0); } isAllPositive = yDomainMin >= 0 && yDomainMax >= 0; isAllNegative = yDomainMin <= 0 && yDomainMax <= 0; // Cancel zerobased if axis_*_min / axis_*_max specified if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { isZeroBased = false; } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { yDomainMin = 0; } if (isAllNegative) { yDomainMax = 0; } } domainLength = Math.abs(yDomainMax - yDomainMin); padding_top = padding_bottom = domainLength * 0.1; if (typeof center !== 'undefined') { yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)); yDomainMax = center + yDomainAbs; yDomainMin = center - yDomainAbs; } // add padding for data label if (showHorizontalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width'); diff = diffDomain($$.y.range()); ratio = [lengths[0] / diff, lengths[1] / diff]; padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])); padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])); } else if (showVerticalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height'); var pixelsToAxisPadding = $$.getY(config["axis_" + axisId + "_type"], // input domain as pixels [0, config.axis_rotated ? $$.width : $$.height], // output range as axis padding [0, domainLength]); padding_top += pixelsToAxisPadding(lengths[1]); padding_bottom += pixelsToAxisPadding(lengths[0]); } if (axisId === 'y' && notEmpty(config.axis_y_padding)) { padding_top = $$.axis.getPadding(config.axis_y_padding, 'top', padding_top, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y_padding, 'bottom', padding_bottom, domainLength); } if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { padding_top = $$.axis.getPadding(config.axis_y2_padding, 'top', padding_top, domainLength); padding_bottom = $$.axis.getPadding(config.axis_y2_padding, 'bottom', padding_bottom, domainLength); } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { padding_bottom = yDomainMin; } if (isAllNegative) { padding_top = -yDomainMax; } } domain = [yDomainMin - padding_bottom, yDomainMax + padding_top]; return isInverted ? domain.reverse() : domain; }; ChartInternal.prototype.getXDomainMin = function (targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function (t) { return $$.d3.min(t.values, function (v) { return v.x; }); }); }; ChartInternal.prototype.getXDomainMax = function (targets) { var $$ = this, config = $$.config; return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function (t) { return $$.d3.max(t.values, function (v) { return v.x; }); }); }; ChartInternal.prototype.getXDomainPadding = function (domain) { var $$ = this, config = $$.config, diff = domain[1] - domain[0], maxDataCount, padding, paddingLeft, paddingRight; if ($$.isCategorized()) { padding = 0; } else if ($$.hasType('bar')) { maxDataCount = $$.getMaxDataCount(); padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5; } else { padding = diff * 0.01; } if (typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding)) { paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding; paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding; } else if (typeof config.axis_x_padding === 'number') { paddingLeft = paddingRight = config.axis_x_padding; } else { paddingLeft = paddingRight = padding; } return { left: paddingLeft, right: paddingRight }; }; ChartInternal.prototype.getXDomain = function (targets) { var $$ = this, xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], firstX = xDomain[0], lastX = xDomain[1], padding = $$.getXDomainPadding(xDomain), min = 0, max = 0; // show center of x domain if min and max are the same if (firstX - lastX === 0 && !$$.isCategorized()) { if ($$.isTimeSeries()) { firstX = new Date(firstX.getTime() * 0.5); lastX = new Date(lastX.getTime() * 1.5); } else { firstX = firstX === 0 ? 1 : firstX * 0.5; lastX = lastX === 0 ? -1 : lastX * 1.5; } } if (firstX || firstX === 0) { min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left; } if (lastX || lastX === 0) { max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right; } return [min, max]; }; ChartInternal.prototype.updateXDomain = function (targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain) { var $$ = this, config = $$.config; if (withUpdateOrgXDomain) { $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))); $$.orgXDomain = $$.x.domain(); if (config.zoom_enabled) { $$.zoom.update(); } $$.subX.domain($$.x.domain()); if ($$.brush) { $$.brush.updateScale($$.subX); } } if (withUpdateXDomain) { $$.x.domain(domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue()); } // Trim domain when too big by zoom mousemove event if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())); } return $$.x.domain(); }; ChartInternal.prototype.trimXDomain = function (domain) { var zoomDomain = this.getZoomDomain(), min = zoomDomain[0], max = zoomDomain[1]; if (domain[0] <= min) { domain[1] = +domain[1] + (min - domain[0]); domain[0] = min; } if (max <= domain[1]) { domain[0] = +domain[0] - (domain[1] - max); domain[1] = max; } return domain; }; ChartInternal.prototype.drag = function (mouse) { var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3; var sx, sy, mx, my, minX, maxX, minY, maxY; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable if (!config.data_selection_multiple) { return; } // skip when single selection because drag is used for multiple selection sx = $$.dragStart[0]; sy = $$.dragStart[1]; mx = mouse[0]; my = mouse[1]; minX = Math.min(sx, mx); maxX = Math.max(sx, mx); minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my); maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my); main .select('.' + CLASS.dragarea) .attr('x', minX) .attr('y', minY) .attr('width', maxX - minX) .attr('height', maxY - minY); // TODO: binary search when multiple xs main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function (d, i) { if (!config.data_selection_isselectable(d)) { return; } var shape = d3.select(this), isSelected = shape.classed(CLASS.SELECTED), isIncluded = shape.classed(CLASS.INCLUDED), _x, _y, _w, _h, toggle, isWithin = false, box; if (shape.classed(CLASS.circle)) { _x = shape.attr('cx') * 1; _y = shape.attr('cy') * 1; toggle = $$.togglePoint; isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY; } else if (shape.classed(CLASS.bar)) { box = getPathBox(this); _x = box.x; _y = box.y; _w = box.width; _h = box.height; toggle = $$.togglePath; isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY); } else { // line/area selection not supported yet return; } if (isWithin ^ isIncluded) { shape.classed(CLASS.INCLUDED, !isIncluded); // TODO: included/unincluded callback here shape.classed(CLASS.SELECTED, !isSelected); toggle.call($$, !isSelected, shape, d, i); } }); }; ChartInternal.prototype.dragstart = function (mouse) { var $$ = this, config = $$.config; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable $$.dragStart = mouse; $$.main .select('.' + CLASS.chart) .append('rect') .attr('class', CLASS.dragarea) .style('opacity', 0.1); $$.dragging = true; }; ChartInternal.prototype.dragend = function () { var $$ = this, config = $$.config; if ($$.hasArcType()) { return; } if (!config.data_selection_enabled) { return; } // do nothing if not selectable $$.main .select('.' + CLASS.dragarea) .transition() .duration(100) .style('opacity', 0) .remove(); $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false); $$.dragging = false; }; ChartInternal.prototype.getYFormat = function (forArc) { var $$ = this, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format; return function (v, ratio, id) { var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY; return format.call($$, v, ratio); }; }; ChartInternal.prototype.yFormat = function (v) { var $$ = this, config = $$.config, format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat; return format(v); }; ChartInternal.prototype.y2Format = function (v) { var $$ = this, config = $$.config, format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat; return format(v); }; ChartInternal.prototype.defaultValueFormat = function (v) { return isValue(v) ? +v : ''; }; ChartInternal.prototype.defaultArcValueFormat = function (v, ratio) { return (ratio * 100).toFixed(1) + '%'; }; ChartInternal.prototype.dataLabelFormat = function (targetId) { var $$ = this, data_labels = $$.config.data_labels, format, defaultFormat = function (v) { return isValue(v) ? +v : ''; }; // find format according to axis id if (typeof data_labels.format === 'function') { format = data_labels.format; } else if (typeof data_labels.format === 'object') { if (data_labels.format[targetId]) { format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId]; } else { format = function () { return ''; }; } } else { format = defaultFormat; } return format; }; ChartInternal.prototype.initGrid = function () { var $$ = this, config = $$.config, d3 = $$.d3; $$.grid = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid); if (config.grid_x_show) { $$.grid.append('g').attr('class', CLASS.xgrids); } if (config.grid_y_show) { $$.grid.append('g').attr('class', CLASS.ygrids); } if (config.grid_focus_show) { $$.grid .append('g') .attr('class', CLASS.xgridFocus) .append('line') .attr('class', CLASS.xgridFocus); } $$.xgrid = d3.selectAll([]); if (!config.grid_lines_front) { $$.initGridLines(); } }; ChartInternal.prototype.initGridLines = function () { var $$ = this, d3 = $$.d3; $$.gridLines = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid + ' ' + CLASS.gridLines); $$.gridLines.append('g').attr('class', CLASS.xgridLines); $$.gridLines.append('g').attr('class', CLASS.ygridLines); $$.xgridLines = d3.selectAll([]); }; ChartInternal.prototype.updateXGrid = function (withoutUpdate) { var $$ = this, config = $$.config, d3 = $$.d3, xgridData = $$.generateGridData(config.grid_x_type, $$.x), tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0; $$.xgridAttr = config.axis_rotated ? { x1: 0, x2: $$.width, y1: function (d) { return $$.x(d) - tickOffset; }, y2: function (d) { return $$.x(d) - tickOffset; } } : { x1: function (d) { return $$.x(d) + tickOffset; }, x2: function (d) { return $$.x(d) + tickOffset; }, y1: 0, y2: $$.height }; $$.xgridAttr.opacity = function () { var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1'); return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1; }; var xgrid = $$.main .select('.' + CLASS.xgrids) .selectAll('.' + CLASS.xgrid) .data(xgridData); var xgridEnter = xgrid .enter() .append('line') .attr('class', CLASS.xgrid) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', 0); $$.xgrid = xgridEnter.merge(xgrid); if (!withoutUpdate) { $$.xgrid .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity); } xgrid.exit().remove(); }; ChartInternal.prototype.updateYGrid = function () { var $$ = this, config = $$.config, gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks); var ygrid = $$.main .select('.' + CLASS.ygrids) .selectAll('.' + CLASS.ygrid) .data(gridValues); var ygridEnter = ygrid .enter() .append('line') // TODO: x1, x2, y1, y2, opacity need to be set here maybe .attr('class', CLASS.ygrid); $$.ygrid = ygridEnter.merge(ygrid); $$.ygrid .attr('x1', config.axis_rotated ? $$.y : 0) .attr('x2', config.axis_rotated ? $$.y : $$.width) .attr('y1', config.axis_rotated ? 0 : $$.y) .attr('y2', config.axis_rotated ? $$.height : $$.y); ygrid.exit().remove(); $$.smoothLines($$.ygrid, 'grid'); }; ChartInternal.prototype.gridTextAnchor = function (d) { return d.position ? d.position : 'end'; }; ChartInternal.prototype.gridTextDx = function (d) { return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4; }; ChartInternal.prototype.xGridTextX = function (d) { return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0; }; ChartInternal.prototype.yGridTextX = function (d) { return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width; }; ChartInternal.prototype.updateGrid = function (duration) { var $$ = this, main = $$.main, config = $$.config, xgridLine, xgridLineEnter, ygridLine, ygridLineEnter, xv = $$.xv.bind($$), yv = $$.yv.bind($$), xGridTextX = $$.xGridTextX.bind($$), yGridTextX = $$.yGridTextX.bind($$); // hide if arc type $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); if (config.grid_x_show) { $$.updateXGrid(); } xgridLine = main .select('.' + CLASS.xgridLines) .selectAll('.' + CLASS.xgridLine) .data(config.grid_x_lines); // enter xgridLineEnter = xgridLine .enter() .append('g') .attr('class', function (d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : ''); }); xgridLineEnter .append('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 0); xgridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .attr('x', config.axis_rotated ? yGridTextX : xGridTextX) .attr('y', xv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // udpate $$.xgridLines = xgridLineEnter.merge(xgridLine); // done in d3.transition() of the end of this function // exit xgridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Y-Grid if (config.grid_y_show) { $$.updateYGrid(); } ygridLine = main .select('.' + CLASS.ygridLines) .selectAll('.' + CLASS.ygridLine) .data(config.grid_y_lines); // enter ygridLineEnter = ygridLine .enter() .append('g') .attr('class', function (d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : ''); }); ygridLineEnter .append('line') .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 0); ygridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .attr('x', config.axis_rotated ? xGridTextX : yGridTextX) .attr('y', yv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0); // update $$.ygridLines = ygridLineEnter.merge(ygridLine); $$.ygridLines .select('line') .transition() .duration(duration) .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 1); $$.ygridLines .select('text') .transition() .duration(duration) .attr('x', config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$)) .attr('y', yv) .text(function (d) { return d.text; }) .style('opacity', 1); // exit ygridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawGrid = function (withTransition, transition) { var $$ = this, config = $$.config, xv = $$.xv.bind($$), lines = $$.xgridLines.select('line'), texts = $$.xgridLines.select('text'); return [ (withTransition ? lines.transition(transition) : lines) .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 1), (withTransition ? texts.transition(transition) : texts) .attr('x', config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$)) .attr('y', xv) .text(function (d) { return d.text; }) .style('opacity', 1) ]; }; ChartInternal.prototype.showXGridFocus = function (selectedData) { var $$ = this, config = $$.config, dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), xx = $$.xx.bind($$); if (!config.tooltip_show) { return; } // Hide when stanford plot exists if ($$.hasType('stanford') || $$.hasArcType()) { return; } focusEl .style('visibility', 'visible') .data([dataToShow[0]]) .attr(config.axis_rotated ? 'y1' : 'x1', xx) .attr(config.axis_rotated ? 'y2' : 'x2', xx); $$.smoothLines(focusEl, 'grid'); }; ChartInternal.prototype.hideXGridFocus = function () { this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden'); }; ChartInternal.prototype.updateXgridFocus = function () { var $$ = this, config = $$.config; $$.main .select('line.' + CLASS.xgridFocus) .attr('x1', config.axis_rotated ? 0 : -10) .attr('x2', config.axis_rotated ? $$.width : -10) .attr('y1', config.axis_rotated ? -10 : 0) .attr('y2', config.axis_rotated ? -10 : $$.height); }; ChartInternal.prototype.generateGridData = function (type, scale) { var $$ = this, gridData = [], xDomain, firstYear, lastYear, i, tickNum = $$.main .select('.' + CLASS.axisX) .selectAll('.tick') .size(); if (type === 'year') { xDomain = $$.getXDomain(); firstYear = xDomain[0].getFullYear(); lastYear = xDomain[1].getFullYear(); for (i = firstYear; i <= lastYear; i++) { gridData.push(new Date(i + '-01-01 00:00:00')); } } else { gridData = scale.ticks(10); if (gridData.length > tickNum) { // use only int gridData = gridData.filter(function (d) { return ('' + d).indexOf('.') < 0; }); } } return gridData; }; ChartInternal.prototype.getGridFilterToRemove = function (params) { return params ? function (line) { var found = false; [].concat(params).forEach(function (param) { if (('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class'])) { found = true; } }); return found; } : function () { return true; }; }; ChartInternal.prototype.removeGridLines = function (params, forX) { var $$ = this, config = $$.config, toRemove = $$.getGridFilterToRemove(params), toShow = function (line) { return !toRemove(line); }, classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, classLine = forX ? CLASS.xgridLine : CLASS.ygridLine; $$.main .select('.' + classLines) .selectAll('.' + classLine) .filter(toRemove) .transition() .duration(config.transition_duration) .style('opacity', 0) .remove(); if (forX) { config.grid_x_lines = config.grid_x_lines.filter(toShow); } else { config.grid_y_lines = config.grid_y_lines.filter(toShow); } }; ChartInternal.prototype.initEventRect = function () { var $$ = this, config = $$.config; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0); $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect); // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null); if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)); } } }; ChartInternal.prototype.redrawEventRect = function () { var $$ = this, d3 = $$.d3, config = $$.config; function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null); $$.hideXGridFocus(); $$.hideTooltip(); $$.unexpandCircles(); $$.unexpandBars(); } var isHoveringDataPoint = function (mouse, closest) { return closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity); }; var withName = function (d) { return (d ? $$.addName(Object.assign({}, d)) : null); }; // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style('cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null); $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on('mouseout', config.interaction_enabled ? function () { if (!config) { return; } // chart is destroyed if ($$.hasArcType()) { return; } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } mouseout(); } : null) .on('mousemove', config.interaction_enabled ? function () { // do nothing when dragging if ($$.dragging) { return; } var targetsToShow = $$.getTargetsToShow(); // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return; } var mouse = d3.mouse(this); var closest = withName($$.findClosestFromTargets(targetsToShow, mouse)); var isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest); // ensure onmouseout is always called if mousemove switch between 2 targets if ($$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index)) { config.data_onmouseout.call($$.api, $$.mouseover); $$.mouseover = undefined; } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest); $$.mouseover = closest; } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null); // if tooltip not grouped, we want to display only data from closest data point var showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow); // find data to highlight var selectedData; if (showSingleDataPoint) { if (closest) { selectedData = [closest]; } } else { var closestByX = void 0; if (closest) { // reuse closest value closestByX = closest; } else { // try to find the closest value by X values from the mouse position var mouseX = config.axis_rotated ? mouse[1] : mouse[0]; closestByX = $$.findClosestFromTargetsByX(targetsToShow, $$.x.invert(mouseX)); } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x); } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout(); } // inject names for each point selectedData = selectedData.map(withName); // show tooltip $$.showTooltip(selectedData, this); // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles(); selectedData.forEach(function (d) { $$.expandCircles(d.index, d.id, false); }); } // expand bars $$.unexpandBars(); selectedData.forEach(function (d) { $$.expandBars(d.index, d.id, false); }); // Show xgrid focus line $$.showXGridFocus(selectedData); } : null) .on('click', config.interaction_enabled ? function () { var targetsToShow = $$.getTargetsToShow(); if ($$.hasArcType(targetsToShow)) { return; } var mouse = d3.mouse(this); var closest = withName($$.findClosestFromTargets(targetsToShow, mouse)); if (!isHoveringDataPoint(mouse, closest)) { return; } // select if selection enabled var sameXData; if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest]; } else { sameXData = $$.filterByX(targetsToShow, closest.x); } // toggle selected state sameXData.forEach(function (d) { $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function () { if (config.data_selection_grouped || $$.isWithinShape(this, d)) { $$.toggleShape(this, d, d.index); } }); }); // call data_onclick on the closest data point if (closest) { var shape = $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id)) .select('.' + CLASS.shape + '-' + closest.index); config.data_onclick.call($$.api, closest, shape.node()); } } : null) .call(config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function () { $$.drag(d3.mouse(this)); }) .on('start', function () { $$.dragstart(d3.mouse(this)); }) .on('end', function () { $$.dragend(); }) : function () { }); }; ChartInternal.prototype.getMousePosition = function (data) { var $$ = this; return [$$.x(data.x), $$.getYScale(data.id)(data.value)]; }; ChartInternal.prototype.dispatchEvent = function (type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents'); event.initMouseEvent(type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null); eventRect.dispatchEvent(event); }; ChartInternal.prototype.initLegend = function () { var $$ = this; $$.legendItemTextBox = {}; $$.legendHasRendered = false; $$.legend = $$.svg.append('g').attr('transform', $$.getTranslate('legend')); if (!$$.config.legend_show) { $$.legend.style('visibility', 'hidden'); $$.hiddenLegendIds = $$.mapToIds($$.data.targets); return; } // MEMO: call here to update legend box and tranlate for all // MEMO: translate will be updated by this, so transform not needed in updateLegend() $$.updateLegendWithDefaults(); }; ChartInternal.prototype.updateLegendWithDefaults = function () { var $$ = this; $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }); }; ChartInternal.prototype.updateSizeForLegend = function (legendHeight, legendWidth) { var $$ = this, config = $$.config, insetLegendPosition = { top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 }; $$.margin3 = { top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, right: NaN, bottom: 0, left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 }; }; ChartInternal.prototype.transformLegend = function (withTransition) { var $$ = this; (withTransition ? $$.legend.transition() : $$.legend).attr('transform', $$.getTranslate('legend')); }; ChartInternal.prototype.updateLegendStep = function (step) { this.legendStep = step; }; ChartInternal.prototype.updateLegendItemWidth = function (w) { this.legendItemWidth = w; }; ChartInternal.prototype.updateLegendItemHeight = function (h) { this.legendItemHeight = h; }; ChartInternal.prototype.getLegendWidth = function () { var $$ = this; return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0; }; ChartInternal.prototype.getLegendHeight = function () { var $$ = this, h = 0; if ($$.config.legend_show) { if ($$.isLegendRight) { h = $$.currentHeight; } else { h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1); } } return h; }; ChartInternal.prototype.opacityForLegend = function (legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 1; }; ChartInternal.prototype.opacityForUnfocusedLegend = function (legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3; }; ChartInternal.prototype.toggleFocusLegend = function (targetIds, focus) { var $$ = this; targetIds = $$.mapToTargetIds(targetIds); $$.legend .selectAll('.' + CLASS.legendItem) .filter(function (id) { return targetIds.indexOf(id) >= 0; }) .classed(CLASS.legendItemFocused, focus) .transition() .duration(100) .style('opacity', function () { var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend; return opacity.call($$, $$.d3.select(this)); }); }; ChartInternal.prototype.revertLegend = function () { var $$ = this, d3 = $$.d3; $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemFocused, false) .transition() .duration(100) .style('opacity', function () { return $$.opacityForLegend(d3.select(this)); }); }; ChartInternal.prototype.showLegend = function (targetIds) { var $$ = this, config = $$.config; if (!config.legend_show) { config.legend_show = true; $$.legend.style('visibility', 'visible'); if (!$$.legendHasRendered) { $$.updateLegendWithDefaults(); } } $$.removeHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('visibility', 'visible') .transition() .style('opacity', function () { return $$.opacityForLegend($$.d3.select(this)); }); }; ChartInternal.prototype.hideLegend = function (targetIds) { var $$ = this, config = $$.config; if (config.legend_show && isEmpty(targetIds)) { config.legend_show = false; $$.legend.style('visibility', 'hidden'); } $$.addHiddenLegendIds(targetIds); $$.legend .selectAll($$.selectorLegends(targetIds)) .style('opacity', 0) .style('visibility', 'hidden'); }; ChartInternal.prototype.clearLegendItemTextBoxCache = function () { this.legendItemTextBox = {}; }; ChartInternal.prototype.updateLegend = function (targetIds, options, transitions) { var $$ = this, config = $$.config; var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile; var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5; var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0; var withTransition, withTransitionForTransform; var texts, rects, tiles, background; // Skip elements when their name is set to null targetIds = targetIds.filter(function (id) { return !isDefined(config.data_names[id]) || config.data_names[id] !== null; }); options = options || {}; withTransition = getOption(options, 'withTransition', true); withTransitionForTransform = getOption(options, 'withTransitionForTransform', true); function getTextBox(textElement, id) { if (!$$.legendItemTextBox[id]) { $$.legendItemTextBox[id] = $$.getTextRect(textElement.textContent, CLASS.legendItem, textElement); } return $$.legendItemTextBox[id]; } function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), margin, maxLength; // MEMO: care about condifion of step, totalLength function updateValues(id, withoutStep) { if (!withoutStep) { margin = (areaLength - totalLength - itemLength) / 2; if (margin < posMin) { margin = (areaLength - itemLength) / 2; totalLength = 0; step++; } } steps[id] = step; margins[step] = $$.isLegendInset ? 10 : margin; offsets[id] = totalLength; totalLength += itemLength; } if (reset) { totalLength = 0; step = 0; maxWidth = 0; maxHeight = 0; } if (config.legend_show && !$$.isLegendToShow(id)) { widths[id] = heights[id] = steps[id] = offsets[id] = 0; return; } widths[id] = itemWidth; heights[id] = itemHeight; if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth; } if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight; } maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth; if (config.legend_equally) { Object.keys(widths).forEach(function (id) { widths[id] = maxWidth; }); Object.keys(heights).forEach(function (id) { heights[id] = maxHeight; }); margin = (areaLength - maxLength * targetIds.length) / 2; if (margin < posMin) { totalLength = 0; step = 0; targetIds.forEach(function (id) { updateValues(id); }); } else { updateValues(id, true); } } else { updateValues(id); } } if ($$.isLegendInset) { step = config.legend_inset_step ? config.legend_inset_step : targetIds.length; $$.updateLegendStep(step); } if ($$.isLegendRight) { xForLegend = function (id) { return maxWidth * steps[id]; }; yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; } else if ($$.isLegendInset) { xForLegend = function (id) { return maxWidth * steps[id] + 10; }; yForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; } else { xForLegend = function (id) { return margins[steps[id]] + offsets[id]; }; yForLegend = function (id) { return maxHeight * steps[id]; }; } xForLegendText = function (id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width; }; yForLegendText = function (id, i) { return yForLegend(id, i) + 9; }; xForLegendRect = function (id, i) { return xForLegend(id, i); }; yForLegendRect = function (id, i) { return yForLegend(id, i) - 5; }; x1ForLegendTile = function (id, i) { return xForLegend(id, i) - 2; }; x2ForLegendTile = function (id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width; }; yForLegendTile = function (id, i) { return yForLegend(id, i) + 4; }; // Define g for legend area l = $$.legend .selectAll('.' + CLASS.legendItem) .data(targetIds) .enter() .append('g') .attr('class', function (id) { return $$.generateClass(CLASS.legendItem, id); }) .style('visibility', function (id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden'; }) .style('cursor', function () { return config.interaction_enabled ? 'pointer' : 'auto'; }) .on('click', config.interaction_enabled ? function (id) { if (config.legend_item_onclick) { config.legend_item_onclick.call($$, id); } else { if ($$.d3.event.altKey) { $$.api.hide(); $$.api.show(id); } else { $$.api.toggle(id); $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert(); } } } : null) .on('mouseover', config.interaction_enabled ? function (id) { if (config.legend_item_onmouseover) { config.legend_item_onmouseover.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, true); if (!$$.transiting && $$.isTargetToShow(id)) { $$.api.focus(id); } } } : null) .on('mouseout', config.interaction_enabled ? function (id) { if (config.legend_item_onmouseout) { config.legend_item_onmouseout.call($$, id); } else { $$.d3.select(this).classed(CLASS.legendItemFocused, false); $$.api.revert(); } } : null); l.append('text') .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) .each(function (id, i) { updatePositions(this, id, i); }) .style('pointer-events', 'none') .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText); l.append('rect') .attr('class', CLASS.legendItemEvent) .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect); l.append('line') .attr('class', CLASS.legendItemTile) .style('stroke', $$.color) .style('pointer-events', 'none') .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('stroke-width', config.legend_item_tile_height); // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect'); if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { background = $$.legend .insert('g', '.' + CLASS.legendItem) .attr('class', CLASS.legendBackground) .append('rect'); } texts = $$.legend .selectAll('text') .data(targetIds) .text(function (id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id; }) // MEMO: needed for update .each(function (id, i) { updatePositions(this, id, i); }); (withTransition ? texts.transition() : texts) .attr('x', xForLegendText) .attr('y', yForLegendText); rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds); (withTransition ? rects.transition() : rects) .attr('width', function (id) { return widths[id]; }) .attr('height', function (id) { return heights[id]; }) .attr('x', xForLegendRect) .attr('y', yForLegendRect); tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds); (withTransition ? tiles.transition() : tiles) .style('stroke', $$.levelColor ? function (id) { return $$.levelColor($$.cache[id].values.reduce(function (total, item) { return total + item.value; }, 0)); } : $$.color) .attr('x1', x1ForLegendTile) .attr('y1', yForLegendTile) .attr('x2', x2ForLegendTile) .attr('y2', yForLegendTile); if (background) { (withTransition ? background.transition() : background) .attr('height', $$.getLegendHeight() - 12) .attr('width', maxWidth * (step + 1) + 10); } // toggle legend state $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemHidden, function (id) { return !$$.isTargetToShow(id); }); // Update all to reflect change of legend $$.updateLegendItemWidth(maxWidth); $$.updateLegendItemHeight(maxHeight); $$.updateLegendStep(step); // Update size and scale $$.updateSizes(); $$.updateScales(); $$.updateSvgSize(); // Update g positions $$.transformAll(withTransitionForTransform, transitions); $$.legendHasRendered = true; }; ChartInternal.prototype.initRegion = function () { var $$ = this; $$.region = $$.main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.regions); }; ChartInternal.prototype.updateRegion = function (duration) { var $$ = this, config = $$.config; // hide if arc type $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible'); var mainRegion = $$.main .select('.' + CLASS.regions) .selectAll('.' + CLASS.region) .data(config.regions); var g = mainRegion.enter().append('g'); g.append('rect') .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }); g.append('text').text($$.labelRegion.bind($$)); $$.mainRegion = g.merge(mainRegion).attr('class', $$.classRegion.bind($$)); mainRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.redrawRegion = function (withTransition, transition) { var $$ = this, regions = $$.mainRegion, regionLabels = $$.mainRegion.selectAll('text'); return [ (withTransition ? regions.transition(transition) : regions) .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function (d) { return isValue(d.opacity) ? d.opacity : 0.1; }), (withTransition ? regionLabels.transition(transition) : regionLabels) .attr('x', $$.labelOffsetX.bind($$)) .attr('y', $$.labelOffsetY.bind($$)) .attr('transform', $$.labelTransform.bind($$)) .attr('style', 'text-anchor: left;') ]; }; ChartInternal.prototype.regionX = function (d) { var $$ = this, config = $$.config, xPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0; } else { xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0; } return xPos; }; ChartInternal.prototype.regionY = function (d) { var $$ = this, config = $$.config, yPos, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0; } else { yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0; } return yPos; }; ChartInternal.prototype.regionWidth = function (d) { var $$ = this, config = $$.config, start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width; } else { end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width; } return end < start ? 0 : end - start; }; ChartInternal.prototype.regionHeight = function (d) { var $$ = this, config = $$.config, start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2; if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height; } else { end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height; } return end < start ? 0 : end - start; }; ChartInternal.prototype.isRegionOnX = function (d) { return !d.axis || d.axis === 'x'; }; ChartInternal.prototype.labelRegion = function (d) { return 'label' in d ? d.label : ''; }; ChartInternal.prototype.labelTransform = function (d) { return 'vertical' in d && d.vertical ? 'rotate(90)' : ''; }; ChartInternal.prototype.labelOffsetX = function (d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? this.regionY(d) + paddingY : this.regionX(d) + paddingX; }; ChartInternal.prototype.labelOffsetY = function (d) { var paddingX = 'paddingX' in d ? d.paddingX : 3; var paddingY = 'paddingY' in d ? d.paddingY : 3; return 'vertical' in d && d.vertical ? -(this.regionX(d) + paddingX) : this.regionY(d) + 10 + paddingY; }; function c3LogScale(d3, linearScale, logScale) { var PROJECTION = [0.01, 10]; if (!linearScale) { linearScale = d3.scaleLinear(); linearScale.range(PROJECTION); } if (!logScale) { logScale = d3.scaleLog(); logScale.domain(PROJECTION); logScale.nice(); } // copied from https://github.com/compute-io/logspace function logspace(a, b, len) { var arr, end, tmp, d; if (arguments.length < 3) { len = 10; } else { if (len === 0) { return []; } } // Calculate the increment: end = len - 1; d = (b - a) / end; // Build the output array... arr = new Array(len); tmp = a; arr[0] = Math.pow(10, tmp); for (var i = 1; i < end; i++) { tmp += d; arr[i] = Math.pow(10, tmp); } arr[end] = Math.pow(10, b); return arr; } function scale(x) { return logScale(linearScale(x)); } scale.domain = function (x) { if (!arguments.length) { return linearScale.domain(); } linearScale.domain(x); return scale; }; scale.range = function (x) { if (!arguments.length) { return logScale.range(); } logScale.range(x); return scale; }; scale.ticks = function (m) { return logspace(-2, 1, m || 10).map(function (v) { return linearScale.invert(v); }); }; scale.copy = function () { return c3LogScale(d3, linearScale.copy(), logScale.copy()); }; return scale; } ChartInternal.prototype.getScale = function (min, max, forTimeseries) { return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([ min, max ]); }; ChartInternal.prototype.getX = function (min, max, domain, offset) { var $$ = this, scale = $$.getScale(min, max, $$.isTimeSeries()), _scale = domain ? scale.domain(domain) : scale, key; // Define customized scale if categorized axis if ($$.isCategorized()) { offset = offset || function () { return 0; }; scale = function (d, raw) { var v = _scale(d) + offset(d); return raw ? v : Math.ceil(v); }; } else { scale = function (d, raw) { var v = _scale(d); return raw ? v : Math.ceil(v); }; } // define functions for (key in _scale) { scale[key] = _scale[key]; } scale.orgDomain = function () { return _scale.domain(); }; // define custom domain() for categorized axis if ($$.isCategorized()) { scale.domain = function (domain) { if (!arguments.length) { domain = this.orgDomain(); return [domain[0], domain[1] + 1]; } _scale.domain(domain); return scale; }; } return scale; }; /** * Creates and configures a D3 scale instance for the given type. * * By defaults it returns a Linear scale. * * @param {String} type Type of d3-scale to create. Type can be 'linear', 'time', 'timeseries' or 'log'. * @param {Array} domain The scale domain such as [from, to] * @param {Array} range The scale's range such as [from, to] * * @return A d3-scale instance */ ChartInternal.prototype.getY = function (type, domain, range) { var scale; if (type === 'timeseries' || type === 'time') { scale = this.d3.scaleTime(); } else if (type === 'log') { scale = c3LogScale(this.d3); } else if (type === 'linear' || type === undefined) { scale = this.d3.scaleLinear(); } else { throw new Error("Invalid Y axis type: \"" + type + "\""); } if (domain) { scale.domain(domain); } if (range) { scale.range(range); } return scale; }; ChartInternal.prototype.getYScale = function (id) { return this.axis.getId(id) === 'y2' ? this.y2 : this.y; }; ChartInternal.prototype.getSubYScale = function (id) { return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY; }; ChartInternal.prototype.updateScales = function () { var $$ = this, config = $$.config, forInit = !$$.x; // update edges $$.xMin = config.axis_rotated ? 1 : 0; $$.xMax = config.axis_rotated ? $$.height : $$.width; $$.yMin = config.axis_rotated ? 0 : $$.height; $$.yMax = config.axis_rotated ? $$.width : 1; $$.subXMin = $$.xMin; $$.subXMax = $$.xMax; $$.subYMin = config.axis_rotated ? 0 : $$.height2; $$.subYMax = config.axis_rotated ? $$.width2 : 1; // update scales $$.x = $$.getX($$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function () { return $$.xAxis.tickOffset(); }); $$.y = $$.getY(config.axis_y_type, forInit ? config.axis_y_default : $$.y.domain(), [$$.yMin, $$.yMax]); $$.y2 = $$.getY(config.axis_y2_type, forInit ? config.axis_y2_default : $$.y2.domain(), [$$.yMin, $$.yMax]); $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function (d) { return d % 1 ? 0 : $$.subXAxis.tickOffset(); }); $$.subY = $$.getY(config.axis_y_type, forInit ? config.axis_y_default : $$.subY.domain(), [$$.subYMin, $$.subYMax]); $$.subY2 = $$.getY(config.axis_y2_type, forInit ? config.axis_y2_default : $$.subY2.domain(), [$$.subYMin, $$.subYMax]); // update axes $$.xAxisTickFormat = $$.axis.getXAxisTickFormat(); $$.xAxisTickValues = $$.axis.getXAxisTickValues(); $$.yAxisTickValues = $$.axis.getYAxisTickValues(); $$.y2AxisTickValues = $$.axis.getY2AxisTickValues(); $$.xAxis = $$.axis.getXAxis($$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.subXAxis = $$.axis.getXAxis($$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer); $$.yAxis = $$.axis.getYAxis('y', $$.y, $$.yOrient, $$.yAxisTickValues, config.axis_y_tick_outer); $$.y2Axis = $$.axis.getYAxis('y2', $$.y2, $$.y2Orient, $$.y2AxisTickValues, config.axis_y2_tick_outer); // Set initialized scales to brush and zoom if (!forInit) { if ($$.brush) { $$.brush.updateScale($$.subX); } } // update for arc if ($$.updateArc) { $$.updateArc(); } }; ChartInternal.prototype.selectPoint = function (target, d, i) { var $$ = this, config = $$.config, cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), r = $$.pointSelectR.bind($$); config.data_onselected.call($$.api, d, target.node()); // add selected-circle on low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .data([d]) .enter() .append('circle') .attr('class', function () { return $$.generateClass(CLASS.selectedCircle, i); }) .attr('cx', cx) .attr('cy', cy) .attr('stroke', function () { return $$.color(d); }) .attr('r', function (d) { return $$.pointSelectR(d) * 1.4; }) .transition() .duration(100) .attr('r', r); }; ChartInternal.prototype.unselectPoint = function (target, d, i) { var $$ = this; $$.config.data_onunselected.call($$.api, d, target.node()); // remove selected-circle from low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .transition() .duration(100) .attr('r', 0) .remove(); }; ChartInternal.prototype.togglePoint = function (selected, target, d, i) { selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i); }; ChartInternal.prototype.selectPath = function (target, d) { var $$ = this; $$.config.data_onselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function () { return $$.d3.rgb($$.color(d)).brighter(0.75); }); } }; ChartInternal.prototype.unselectPath = function (target, d) { var $$ = this; $$.config.data_onunselected.call($$, d, target.node()); if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function () { return $$.color(d); }); } }; ChartInternal.prototype.togglePath = function (selected, target, d, i) { selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i); }; ChartInternal.prototype.getToggle = function (that, d) { var $$ = this, toggle; if (that.nodeName === 'circle') { if ($$.isStepType(d)) { // circle is hidden in step chart, so treat as within the click area toggle = function () { }; // TODO: how to select step chart? } else { toggle = $$.togglePoint; } } else if (that.nodeName === 'path') { toggle = $$.togglePath; } return toggle; }; ChartInternal.prototype.toggleShape = function (that, d, i) { var $$ = this, d3 = $$.d3, config = $$.config, shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), toggle = $$.getToggle(that, d).bind($$); if (config.data_selection_enabled && config.data_selection_isselectable(d)) { if (!config.data_selection_multiple) { $$.main .selectAll('.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : '')) .selectAll('.' + CLASS.shape) .each(function (d, i) { var shape = d3.select(this); if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i); } }); } shape.classed(CLASS.SELECTED, !isSelected); toggle(!isSelected, shape, d, i); } }; ChartInternal.prototype.initBar = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); }; ChartInternal.prototype.updateTargetsForBar = function (targets) { var $$ = this, config = $$.config, mainBars, mainBarEnter, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classFocus = $$.classFocus.bind($$); mainBars = $$.main .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) .attr('class', function (d) { return classChartBar(d) + classFocus(d); }); mainBarEnter = mainBars .enter() .append('g') .attr('class', classChartBar) .style('pointer-events', 'none'); // Bars for each data mainBarEnter .append('g') .attr('class', classBars) .style('cursor', function (d) { return config.data_selection_isselectable(d) ? 'pointer' : null; }); }; ChartInternal.prototype.updateBar = function (durationForExit) { var $$ = this, barData = $$.barData.bind($$), classBar = $$.classBar.bind($$), initialOpacity = $$.initialOpacity.bind($$), color = function (d) { return $$.color(d.id); }; var mainBar = $$.main .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data(barData); var mainBarEnter = mainBar .enter() .append('path') .attr('class', classBar) .style('stroke', color) .style('fill', color); $$.mainBar = mainBarEnter.merge(mainBar).style('opacity', initialOpacity); mainBar .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawBar = function (drawBar, withTransition, transition) { var $$ = this; return [ (withTransition ? this.mainBar.transition(transition) : this.mainBar) .attr('d', drawBar) .style('stroke', this.color) .style('fill', this.color) .style('opacity', function (d) { return ($$.isTargetToShow(d.id) ? 1 : 0); }) ]; }; ChartInternal.prototype.getBarW = function (axis, barTargetsNum) { var $$ = this, config = $$.config, w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0; return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w; }; ChartInternal.prototype.getBars = function (i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')); }; ChartInternal.prototype.expandBars = function (i, id, reset) { var $$ = this; if (reset) { $$.unexpandBars(); } $$.getBars(i, id).classed(CLASS.EXPANDED, true); }; ChartInternal.prototype.unexpandBars = function (i) { var $$ = this; $$.getBars(i).classed(CLASS.EXPANDED, false); }; ChartInternal.prototype.generateDrawBar = function (barIndices, isSub) { var $$ = this, config = $$.config, getPoints = $$.generateGetBarPoints(barIndices, isSub); return function (d, i) { // 4 points that make a bar var points = getPoints(d, i); // switch points if axis is rotated, not applicable for sub chart var indexX = config.axis_rotated ? 1 : 0; var indexY = config.axis_rotated ? 0 : 1; var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z'; return path; }; }; ChartInternal.prototype.generateGetBarPoints = function (barIndices, isSub) { var $$ = this, axis = isSub ? $$.subXAxis : $$.xAxis, barTargetsNum = barIndices.__max__ + 1, barW = $$.getBarW(axis, barTargetsNum), barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), barY = $$.getShapeY(!!isSub), barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), barSpaceOffset = barW * ($$.config.bar_space / 2), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = barOffset(d, i) || y0, // offset is for stacked bar chart posX = barX(d), posY = barY(d); // fix posY not to overflow opposite quadrant if ($$.config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } posY -= y0 - offset; // 4 points that make a bar return [ [posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY], [posX + barW - barSpaceOffset, posY], [posX + barW - barSpaceOffset, offset] ]; }; }; /** * Returns whether the data point is within the given bar shape. * * @param mouse * @param barShape * @return {boolean} */ ChartInternal.prototype.isWithinBar = function (mouse, barShape) { return isWithinBox(mouse, getBBox(barShape), 2); }; ChartInternal.prototype.getShapeIndices = function (typeFilter) { var $$ = this, config = $$.config, indices = {}, i = 0, j, k; $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach(function (d) { for (j = 0; j < config.data_groups.length; j++) { if (config.data_groups[j].indexOf(d.id) < 0) { continue; } for (k = 0; k < config.data_groups[j].length; k++) { if (config.data_groups[j][k] in indices) { indices[d.id] = indices[config.data_groups[j][k]]; break; } } } if (isUndefined(indices[d.id])) { indices[d.id] = i++; } }); indices.__max__ = i - 1; return indices; }; ChartInternal.prototype.getShapeX = function (offset, targetsNum, indices, isSub) { var $$ = this, scale = isSub ? $$.subX : $$.x; return function (d) { var index = d.id in indices ? indices[d.id] : 0; return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0; }; }; ChartInternal.prototype.getShapeY = function (isSub) { var $$ = this; return function (d) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id); return scale($$.isTargetNormalized(d.id) ? $$.getRatio('index', d, true) : d.value); }; }; ChartInternal.prototype.getShapeOffset = function (typeFilter, indices, isSub) { var $$ = this, targets = $$.orderTargets($$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$))), targetIds = targets.map(function (t) { return t.id; }); return function (d, i) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), y0 = scale(0), offset = y0; targets.forEach(function (t) { var rowValues = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values; var isTargetNormalized = $$.isTargetNormalized(d.id); var values = rowValues.map(function (v) { return isTargetNormalized ? $$.getRatio('index', v, true) : v.value; }); if (t.id === d.id || indices[t.id] !== indices[d.id]) { return; } if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { // check if the x values line up if (isUndefined(rowValues[i]) || +rowValues[i].x !== +d.x) { // "+" for timeseries // if not, try to find the value that does line up i = -1; rowValues.forEach(function (v, j) { var x1 = v.x.constructor === Date ? +v.x : v.x; var x2 = d.x.constructor === Date ? +d.x : d.x; if (x1 === x2) { i = j; } }); } if (i in rowValues && rowValues[i].value * d.value >= 0) { offset += scale(values[i]) - y0; } } }); return offset; }; }; ChartInternal.prototype.isWithinShape = function (that, d) { var $$ = this, shape = $$.d3.select(that), isWithin; if (!$$.isTargetToShow(d.id)) { isWithin = false; } else if (that.nodeName === 'circle') { isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5); } else if (that.nodeName === 'path') { isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true; } return isWithin; }; ChartInternal.prototype.getInterpolate = function (d) { var $$ = this, d3 = $$.d3, types = { linear: d3.curveLinear, 'linear-closed': d3.curveLinearClosed, basis: d3.curveBasis, 'basis-open': d3.curveBasisOpen, 'basis-closed': d3.curveBasisClosed, bundle: d3.curveBundle, cardinal: d3.curveCardinal, 'cardinal-open': d3.curveCardinalOpen, 'cardinal-closed': d3.curveCardinalClosed, monotone: d3.curveMonotoneX, step: d3.curveStep, 'step-before': d3.curveStepBefore, 'step-after': d3.curveStepAfter }, type; if ($$.isSplineType(d)) { type = types[$$.config.spline_interpolation_type] || types.cardinal; } else if ($$.isStepType(d)) { type = types[$$.config.line_step_type]; } else { type = types.linear; } return type; }; ChartInternal.prototype.initLine = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); }; ChartInternal.prototype.updateTargetsForLine = function (targets) { var $$ = this, config = $$.config, mainLines, mainLineEnter, classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$), classCircles = $$.classCircles.bind($$), classFocus = $$.classFocus.bind($$); mainLines = $$.main .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) .attr('class', function (d) { return classChartLine(d) + classFocus(d); }); mainLineEnter = mainLines .enter() .append('g') .attr('class', classChartLine) .style('opacity', 0) .style('pointer-events', 'none'); // Lines for each data mainLineEnter.append('g').attr('class', classLines); // Areas mainLineEnter.append('g').attr('class', classAreas); // Circles for each data point on lines mainLineEnter.append('g').attr('class', function (d) { return $$.generateClass(CLASS.selectedCircles, d.id); }); mainLineEnter .append('g') .attr('class', classCircles) .style('cursor', function (d) { return config.data_selection_isselectable(d) ? 'pointer' : null; }); // Update date for selected circles targets.forEach(function (t) { $$.main .selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)) .selectAll('.' + CLASS.selectedCircle) .each(function (d) { d.value = t.values[d.index].value; }); }); // MEMO: can not keep same color... //mainLineUpdate.exit().remove(); }; ChartInternal.prototype.updateLine = function (durationForExit) { var $$ = this; var mainLine = $$.main .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var mainLineEnter = mainLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); $$.mainLine = mainLineEnter .merge(mainLine) .style('opacity', $$.initialOpacity.bind($$)) .style('shape-rendering', function (d) { return $$.isStepType(d) ? 'crispEdges' : ''; }) .attr('transform', null); mainLine .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawLine = function (drawLine, withTransition, transition) { return [ (withTransition ? this.mainLine.transition(transition) : this.mainLine) .attr('d', drawLine) .style('stroke', this.color) .style('opacity', 1) ]; }; ChartInternal.prototype.generateDrawLine = function (lineIndices, isSub) { var $$ = this, config = $$.config, line = $$.d3.line(), getPoints = $$.generateGetLinePoints(lineIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, yValue = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value); }; line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue); if (!config.line_connectNull) { line = line.defined(function (d) { return d.value != null; }); } return function (d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x = isSub ? $$.subX : $$.x, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path; if ($$.isLineType(d)) { if (config.data_regions[d.id]) { path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]); } else { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = line.curve($$.getInterpolate(d))(values); } } else { if (values[0]) { x0 = x(values[0].x); y0 = y(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0'; }; }; ChartInternal.prototype.generateGetLinePoints = function (lineIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, lineTargetsNum = lineIndices.__max__ + 1, x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), y = $$.getShapeY(!!isSub), lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = lineOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the line position return [ [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)] // needed for compatibility ]; }; }; ChartInternal.prototype.lineWithRegions = function (d, x, y, _regions) { var $$ = this, config = $$.config, prev = -1, i, j, s = 'M', sWithRegion, xp, yp, dx, dy, dd, diff, diffx2, xOffset = $$.isCategorized() ? 0.5 : 0, xValue, yValue, regions = []; function isWithinRegions(x, regions) { var i; for (i = 0; i < regions.length; i++) { if (regions[i].start < x && x <= regions[i].end) { return true; } } return false; } // Check start/end of regions if (isDefined(_regions)) { for (i = 0; i < _regions.length; i++) { regions[i] = {}; if (isUndefined(_regions[i].start)) { regions[i].start = d[0].x; } else { regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start; } if (isUndefined(_regions[i].end)) { regions[i].end = d[d.length - 1].x; } else { regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end; } } } // Set scales xValue = config.axis_rotated ? function (d) { return y(d.value); } : function (d) { return x(d.x); }; yValue = config.axis_rotated ? function (d) { return x(d.x); } : function (d) { return y(d.value); }; // Define svg generator function for region function generateM(points) { return ('M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1]); } if ($$.isTimeSeries()) { sWithRegion = function (d0, d1, j, diff) { var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, xv0 = new Date(x0 + x_diff * j), xv1 = new Date(x0 + x_diff * (j + diff)), points; if (config.axis_rotated) { points = [ [y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)] ]; } else { points = [ [x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))] ]; } return generateM(points); }; } else { sWithRegion = function (d0, d1, j, diff) { var points; if (config.axis_rotated) { points = [ [y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))] ]; } else { points = [ [x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))] ]; } return generateM(points); }; } // Generate for (i = 0; i < d.length; i++) { // Draw as normal if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { s += ' ' + xValue(d[i]) + ' ' + yValue(d[i]); } // Draw with region // TODO: Fix for horizotal charts else { xp = $$.getScale(d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries()); yp = $$.getScale(d[i - 1].value, d[i].value); dx = x(d[i].x) - x(d[i - 1].x); dy = y(d[i].value) - y(d[i - 1].value); dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)); diff = 2 / dd; diffx2 = diff * 2; for (j = diff; j <= 1; j += diffx2) { s += sWithRegion(d[i - 1], d[i], j, diff); } } prev = d[i].x; } return s; }; ChartInternal.prototype.updateArea = function (durationForExit) { var $$ = this, d3 = $$.d3; var mainArea = $$.main .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var mainAreaEnter = mainArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); $$.mainArea = mainAreaEnter .merge(mainArea) .style('opacity', $$.orgAreaOpacity); mainArea .exit() .transition() .duration(durationForExit) .style('opacity', 0); }; ChartInternal.prototype.redrawArea = function (drawArea, withTransition, transition) { return [ (withTransition ? this.mainArea.transition(transition) : this.mainArea) .attr('d', drawArea) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) ]; }; ChartInternal.prototype.generateDrawArea = function (areaIndices, isSub) { var $$ = this, config = $$.config, area = $$.d3.area(), getPoints = $$.generateGetAreaPoints(areaIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function (d) { return (isSub ? $$.subxx : $$.xx).call($$, d); }, value0 = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)); }, value1 = function (d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value); }; area = config.axis_rotated ? area .x0(value0) .x1(value1) .y(xValue) : area .x(xValue) .y0(config.area_above ? 0 : value0) .y1(value1); if (!config.line_connectNull) { area = area.defined(function (d) { return d.value !== null; }); } return function (d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path; if ($$.isAreaType(d)) { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values); } path = area.curve($$.getInterpolate(d))(values); } else { if (values[0]) { x0 = $$.x(values[0].x); y0 = $$.getYScale(d.id)(values[0].value); } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0; } return path ? path : 'M 0 0'; }; }; ChartInternal.prototype.getAreaBaseValue = function () { return 0; }; ChartInternal.prototype.generateGetAreaPoints = function (areaIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, areaTargetsNum = areaIndices.__max__ + 1, x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), y = $$.getShapeY(!!isSub), areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale; return function (d, i) { var y0 = yScale.call($$, d.id)(0), offset = areaOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d); // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0; } } // 1 point that marks the area position return [ [posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], [posX, offset] // needed for compatibility ]; }; }; ChartInternal.prototype.updateCircle = function (cx, cy) { var $$ = this; var mainCircle = $$.main .selectAll('.' + CLASS.circles) .selectAll('.' + CLASS.circle) .data($$.lineOrScatterOrStanfordData.bind($$)); var mainCircleEnter = mainCircle .enter() .append('circle') .attr('shape-rendering', $$.isStanfordGraphType() ? 'crispEdges' : '') .attr('class', $$.classCircle.bind($$)) .attr('cx', cx) .attr('cy', cy) .attr('r', $$.pointR.bind($$)) .style('color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color); $$.mainCircle = mainCircleEnter .merge(mainCircle) .style('opacity', $$.isStanfordGraphType() ? 1 : $$.initialOpacityForCircle.bind($$)); mainCircle.exit().style('opacity', 0); }; ChartInternal.prototype.redrawCircle = function (cx, cy, withTransition, transition) { var $$ = this, selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle); return [ (withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle) .style('opacity', this.opacityForCircle.bind($$)) .style('color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color) .attr('cx', cx) .attr('cy', cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles) .attr('cx', cx) .attr('cy', cy) ]; }; ChartInternal.prototype.circleX = function (d) { return d.x || d.x === 0 ? this.x(d.x) : null; }; ChartInternal.prototype.updateCircleY = function () { var $$ = this, lineIndices, getPoints; if ($$.config.data_groups.length > 0) { (lineIndices = $$.getShapeIndices($$.isLineType)), (getPoints = $$.generateGetLinePoints(lineIndices)); $$.circleY = function (d, i) { return getPoints(d, i)[0][1]; }; } else { $$.circleY = function (d) { return $$.getYScale(d.id)(d.value); }; } }; ChartInternal.prototype.getCircles = function (i, id) { var $$ = this; return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')); }; ChartInternal.prototype.expandCircles = function (i, id, reset) { var $$ = this, r = $$.pointExpandedR.bind($$); if (reset) { $$.unexpandCircles(); } $$.getCircles(i, id) .classed(CLASS.EXPANDED, true) .attr('r', r); }; ChartInternal.prototype.unexpandCircles = function (i) { var $$ = this, r = $$.pointR.bind($$); $$.getCircles(i) .filter(function () { return $$.d3.select(this).classed(CLASS.EXPANDED); }) .classed(CLASS.EXPANDED, false) .attr('r', r); }; ChartInternal.prototype.pointR = function (d) { var $$ = this, config = $$.config; return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r; }; ChartInternal.prototype.pointExpandedR = function (d) { var $$ = this, config = $$.config; if (config.point_focus_expand_enabled) { return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75; } else { return $$.pointR(d); } }; ChartInternal.prototype.pointSelectR = function (d) { var $$ = this, config = $$.config; return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4; }; ChartInternal.prototype.isWithinCircle = function (that, r) { var d3 = this.d3, mouse = d3.mouse(that), d3_this = d3.select(that), cx = +d3_this.attr('cx'), cy = +d3_this.attr('cy'); return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r; }; ChartInternal.prototype.isWithinStep = function (that, y) { return Math.abs(y - this.d3.mouse(that)[1]) < 30; }; ChartInternal.prototype.getCurrentWidth = function () { var $$ = this, config = $$.config; return config.size_width ? config.size_width : $$.getParentWidth(); }; ChartInternal.prototype.getCurrentHeight = function () { var $$ = this, config = $$.config, h = config.size_height ? config.size_height : $$.getParentHeight(); return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1); }; ChartInternal.prototype.getCurrentPaddingTop = function () { var $$ = this, config = $$.config, padding = isValue(config.padding_top) ? config.padding_top : 0; if ($$.title && $$.title.node()) { padding += $$.getTitlePadding(); } return padding; }; ChartInternal.prototype.getCurrentPaddingBottom = function () { var config = this.config; return isValue(config.padding_bottom) ? config.padding_bottom : 0; }; ChartInternal.prototype.getCurrentPaddingLeft = function (withoutRecompute) { var $$ = this, config = $$.config; if (isValue(config.padding_left)) { return config.padding_left; } else if (config.axis_rotated) { return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40); } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1; } else { return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)); } }; ChartInternal.prototype.getCurrentPaddingRight = function () { var $$ = this, config = $$.config, padding = 0, defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0; if (isValue(config.padding_right)) { padding = config.padding_right + 1; // 1 is needed not to hide tick line } else if (config.axis_rotated) { padding = defaultPadding + legendWidthOnRight; } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated padding = 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0); } else { padding = ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight; } if ($$.colorScale && $$.colorScale.node()) { padding += $$.getColorScalePadding(); } return padding; }; ChartInternal.prototype.getParentRectValue = function (key) { var parent = this.selectChart.node(), v; while (parent && parent.tagName !== 'BODY') { try { v = parent.getBoundingClientRect()[key]; } catch (e) { if (key === 'width') { // In IE in certain cases getBoundingClientRect // will cause an "unspecified error" v = parent.offsetWidth; } } if (v) { break; } parent = parent.parentNode; } return v; }; ChartInternal.prototype.getParentWidth = function () { return this.getParentRectValue('width'); }; ChartInternal.prototype.getParentHeight = function () { var h = this.selectChart.style('height'); return h.indexOf('px') > 0 ? +h.replace('px', '') : 0; }; ChartInternal.prototype.getSvgLeft = function (withoutRecompute) { var $$ = this, config = $$.config, hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, leftAxis = $$.main.select('.' + leftAxisClass).node(), svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, chartRect = $$.selectChart.node().getBoundingClientRect(), hasArc = $$.hasArcType(), svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)); return svgLeft > 0 ? svgLeft : 0; }; ChartInternal.prototype.getAxisWidthByAxisId = function (id, withoutRecompute) { var $$ = this, position = $$.axis.getLabelPositionById(id); return ($$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40)); }; ChartInternal.prototype.getHorizontalAxisHeight = function (axisId, isSubchart) { var $$ = this, config = $$.config, h = 30; if (axisId === 'x' && !(isDefined(isSubchart) && isSubchart ? config.subchart_axis_x_show : config.axis_x_show)) { return 8; } if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height; } if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1; } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top; } // Calculate x axis height when tick rotated if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_x_tick_rotate))) / 180); } // Calculate y axis height when tick rotated if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_y_tick_rotate))) / 180); } return (h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0)); }; ChartInternal.prototype.initBrush = function (scale) { var $$ = this, d3 = $$.d3; // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function () { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return; } $$.redrawForBrush(); }) .on('end', function () { var event = d3.event.sourceEvent; if (event && event.type === 'zoom') { return; } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear(); } }); $$.brush.updateExtent = function () { var range = this.scale.range(), extent; if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ]; } else { extent = [ [range[0], 0], [range[1], $$.height2] ]; } this.extent(extent); return this; }; $$.brush.updateScale = function (scale) { this.scale = scale; return this; }; $$.brush.update = function (scale) { this.updateScale(scale || $$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call(this); }; $$.brush.clear = function () { $$.context.select('.' + CLASS.brush).call($$.brush.move, null); }; $$.brush.selection = function () { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()); }; $$.brush.selectionAsValue = function (selectionAsValue, withTransition) { var selection, brush; if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ]; brush = $$.context.select('.' + CLASS.brush); if (withTransition) { brush = brush.transition(); } $$.brush.move(brush, selection); } return []; } selection = $$.brush.selection() || [0, 0]; return [this.scale.invert(selection[0]), this.scale.invert(selection[1])]; }; $$.brush.empty = function () { var selection = $$.brush.selection(); return !selection || selection[0] === selection[1]; }; return $$.brush.updateScale(scale); }; ChartInternal.prototype.initSubchart = function () { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))); // set style context.style('visibility', 'visible'); // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart); // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars); // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines); // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush); // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis) .style('visibility', config.subchart_axis_x_show ? 'visible' : 'hidden'); }; ChartInternal.prototype.initSubchartBrush = function () { var $$ = this; // Add extent rect for Brush $$.initBrush($$.subX).updateExtent(); $$.context.select('.' + CLASS.brush).call($$.brush); }; ChartInternal.prototype.updateTargetsForSubchart = function (targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$); //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets); contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0); contextBarEnter.merge(contextBar).attr('class', classChartBar); // Bars for each data contextBarEnter.append('g').attr('class', classBars); //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets); contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0); contextLineEnter.merge(contextLine).attr('class', classChartLine); // Lines for each data contextLineEnter.append('g').attr('class', classLines); // Area contextLineEnter.append('g').attr('class', classAreas); //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr(config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2); }; ChartInternal.prototype.updateBarForSubchart = function (durationForExit) { var $$ = this; var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)); var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color); contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawBarForSubchart = function (drawBarOnSub, withTransition, duration) { (withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar) .attr('d', drawBarOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateLineForSubchart = function (durationForExit) { var $$ = this; var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)); var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color); contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)); }; ChartInternal.prototype.redrawLineForSubchart = function (drawLineOnSub, withTransition, duration) { (withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine) .attr('d', drawLineOnSub) .style('opacity', 1); }; ChartInternal.prototype.updateAreaForSubchart = function (durationForExit) { var $$ = this, d3 = $$.d3; var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)); var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function () { $$.orgAreaOpacity = +d3.select(this).style('opacity'); return 0; }); contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove(); $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0); }; ChartInternal.prototype.redrawAreaForSubchart = function (drawAreaOnSub, withTransition, duration) { (withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity); }; ChartInternal.prototype.redrawSubchart = function (withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub; // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()); } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()); } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true); drawBarOnSub = $$.generateDrawBar(barIndices, true); drawLineOnSub = $$.generateDrawLine(lineIndices, true); $$.updateBarForSubchart(duration); $$.updateLineForSubchart(duration); $$.updateAreaForSubchart(duration); $$.redrawBarForSubchart(drawBarOnSub, duration, duration); $$.redrawLineForSubchart(drawLineOnSub, duration, duration); $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration); } }; ChartInternal.prototype.redrawForBrush = function () { var $$ = this, x = $$.x, d3 = $$.d3, s; $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }); // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range(); $$.main .select('.' + CLASS.eventRect) .call($$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0)); $$.config.subchart_onbrush.call($$.api, x.orgDomain()); }; ChartInternal.prototype.transformContext = function (withTransition, transitions) { var $$ = this, subXAxis; if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX; } else { subXAxis = $$.context.select('.' + CLASS.axisX); if (withTransition) { subXAxis = subXAxis.transition(); } } $$.context.attr('transform', $$.getTranslate('context')); subXAxis.attr('transform', $$.getTranslate('subx')); }; ChartInternal.prototype.getDefaultSelection = function () { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection; if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])]; } return selection; }; ChartInternal.prototype.removeSubchart = function () { var $$ = this; $$.brush = null; $$.context.remove(); $$.context = null; }; ChartInternal.prototype.initText = function () { var $$ = this; $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartTexts); $$.mainText = $$.d3.selectAll([]); }; ChartInternal.prototype.updateTargetsForText = function (targets) { var $$ = this, classChartText = $$.classChartText.bind($$), classTexts = $$.classTexts.bind($$), classFocus = $$.classFocus.bind($$); var mainText = $$.main .select('.' + CLASS.chartTexts) .selectAll('.' + CLASS.chartText) .data(targets); var mainTextEnter = mainText .enter() .append('g') .attr('class', classChartText) .style('opacity', 0) .style('pointer-events', 'none'); mainTextEnter.append('g').attr('class', classTexts); mainTextEnter.merge(mainText).attr('class', function (d) { return classChartText(d) + classFocus(d); }); }; ChartInternal.prototype.updateText = function (xForText, yForText, durationForExit) { var $$ = this, config = $$.config, barOrLineData = $$.barOrLineData.bind($$), classText = $$.classText.bind($$); var mainText = $$.main .selectAll('.' + CLASS.texts) .selectAll('.' + CLASS.text) .data(barOrLineData); var mainTextEnter = mainText .enter() .append('text') .attr('class', classText) .attr('text-anchor', function (d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle'; }) .style('stroke', 'none') .attr('x', xForText) .attr('y', yForText) .style('fill', function (d) { return $$.color(d); }) .style('fill-opacity', 0); $$.mainText = mainTextEnter.merge(mainText).text(function (d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j); }); mainText .exit() .transition() .duration(durationForExit) .style('fill-opacity', 0) .remove(); }; ChartInternal.prototype.redrawText = function (xForText, yForText, forFlow, withTransition, transition) { return [ (withTransition ? this.mainText.transition(transition) : this.mainText) .attr('x', xForText) .attr('y', yForText) .style('fill', this.color) .style('fill-opacity', forFlow ? 0 : this.opacityForText.bind(this)) ]; }; ChartInternal.prototype.getTextRect = function (text, cls, element) { var dummy = this.d3 .select('body') .append('div') .classed('c3', true), svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0), font = this.d3.select(element).style('font'), rect; svg .selectAll('.dummy') .data([text]) .enter() .append('text') .classed(cls ? cls : '', true) .style('font', font) .text(text) .each(function () { rect = getBBox(this); }); dummy.remove(); return rect; }; ChartInternal.prototype.generateXYForText = function (areaIndices, barIndices, lineIndices, forX) { var $$ = this, getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false), getter = forX ? $$.getXForText : $$.getYForText; return function (d, i) { var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints; return getter.call($$, getPoints(d, i), d, this); }; }; ChartInternal.prototype.getXForText = function (points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding; if ($$.config.axis_rotated) { padding = $$.isBarType(d) ? 4 : 6; xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); } else { xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0]; } // show labels regardless of the domain if value is null if (d.value === null) { if (xPos > $$.width) { xPos = $$.width - box.width; } else if (xPos < 0) { xPos = 4; } } return xPos; }; ChartInternal.prototype.getYForText = function (points, d, textElement) { var $$ = this, box = getBBox(textElement), yPos; if ($$.config.axis_rotated) { yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2; } else { yPos = points[2][1]; if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { yPos += box.height; if ($$.isBarType(d) && $$.isSafari()) { yPos -= 3; } else if (!$$.isBarType(d) && $$.isChrome()) { yPos += 3; } } else { yPos += $$.isBarType(d) ? -3 : -6; } } // show labels regardless of the domain if value is null if (d.value === null && !$$.config.axis_rotated) { if (yPos < box.height) { yPos = box.height; } else if (yPos > this.height) { yPos = this.height - 4; } } return yPos; }; ChartInternal.prototype.initTitle = function () { var $$ = this; $$.title = $$.svg .append('text') .text($$.config.title_text) .attr('class', $$.CLASS.title); }; ChartInternal.prototype.redrawTitle = function () { var $$ = this; $$.title.attr('x', $$.xForTitle.bind($$)).attr('y', $$.yForTitle.bind($$)); }; ChartInternal.prototype.xForTitle = function () { var $$ = this, config = $$.config, position = config.title_position || 'left', x; if (position.indexOf('right') >= 0) { x = $$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width - config.title_padding.right; } else if (position.indexOf('center') >= 0) { x = Math.max(($$.currentWidth - $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()).width) / 2, 0); } else { // left x = config.title_padding.left; } return x; }; ChartInternal.prototype.yForTitle = function () { var $$ = this; return ($$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()) .height); }; ChartInternal.prototype.getTitlePadding = function () { var $$ = this; return $$.yForTitle() + $$.config.title_padding.bottom; }; function powerOfTen(d) { return d / Math.pow(10, Math.ceil(Math.log(d) / Math.LN10 - 1e-12)) === 1; } ChartInternal.prototype.drawColorScale = function () { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], barWidth, barHeight, axis, points, legendAxis, axisScale, inverseScale, height; barWidth = !isNaN(config.stanford_scaleWidth) ? config.stanford_scaleWidth : 20; barHeight = 5; if (barHeight < 0 || barWidth < 0) { throw Error("Colorscale's barheight and barwidth must be greater than 0."); } height = $$.height - config.stanford_padding.bottom - config.stanford_padding.top; points = d3.range(config.stanford_padding.bottom, height, barHeight); inverseScale = d3 .scaleSequential(target.colors) .domain([points[points.length - 1], points[0]]); if ($$.colorScale) { $$.colorScale.remove(); } $$.colorScale = $$.svg .append('g') .attr('width', 50) .attr('height', height) .attr('class', CLASS.colorScale); $$.colorScale .append('g') .attr('transform', "translate(0, " + config.stanford_padding.top + ")") .selectAll('bars') .data(points) .enter() .append('rect') .attr('y', function (d, i) { return i * barHeight; }) .attr('x', 0) .attr('width', barWidth) .attr('height', barHeight) .attr('fill', function (d) { return inverseScale(d); }); // Legend Axis axisScale = d3 .scaleLog() .domain([target.minEpochs, target.maxEpochs]) .range([ points[0] + config.stanford_padding.top + points[points.length - 1] + barHeight - 1, points[0] + config.stanford_padding.top ]); legendAxis = d3.axisRight(axisScale); if (config.stanford_scaleFormat === 'pow10') { legendAxis.tickValues([1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]); } else if (isFunction(config.stanford_scaleFormat)) { legendAxis.tickFormat(config.stanford_scaleFormat); } else { legendAxis.tickFormat(d3.format('d')); } if (isFunction(config.stanford_scaleValues)) { legendAxis.tickValues(config.stanford_scaleValues(target.minEpochs, target.maxEpochs)); } // Draw Axis axis = $$.colorScale .append('g') .attr('class', 'legend axis') .attr('transform', "translate(" + barWidth + ",0)") .call(legendAxis); if (config.stanford_scaleFormat === 'pow10') { axis .selectAll('.tick text') .text(null) .filter(powerOfTen) .text(10) .append('tspan') .attr('dy', '-.7em') // https://bl.ocks.org/mbostock/6738229 .text(function (d) { return Math.round(Math.log(d) / Math.LN10); }); } $$.colorScale.attr('transform', "translate(" + ($$.currentWidth - $$.xForColorScale()) + ", 0)"); }; ChartInternal.prototype.xForColorScale = function () { var $$ = this; return $$.config.stanford_padding.right + getBBox($$.colorScale.node()).width; }; ChartInternal.prototype.getColorScalePadding = function () { var $$ = this; return $$.xForColorScale() + $$.config.stanford_padding.left + 20; }; ChartInternal.prototype.isStanfordGraphType = function () { var $$ = this; return $$.config.data_type === 'stanford'; }; ChartInternal.prototype.initStanfordData = function () { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], epochs, maxEpochs, minEpochs; // Make larger values appear on top target.values.sort(compareEpochs); // Get array of epochs epochs = target.values.map(function (a) { return a.epochs; }); minEpochs = !isNaN(config.stanford_scaleMin) ? config.stanford_scaleMin : d3.min(epochs); maxEpochs = !isNaN(config.stanford_scaleMax) ? config.stanford_scaleMax : d3.max(epochs); if (minEpochs > maxEpochs) { throw Error('Number of minEpochs has to be smaller than maxEpochs'); } target.colors = isFunction(config.stanford_colors) ? config.stanford_colors : d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)); target.colorscale = d3 .scaleSequentialLog(target.colors) .domain([minEpochs, maxEpochs]); target.minEpochs = minEpochs; target.maxEpochs = maxEpochs; }; ChartInternal.prototype.getStanfordPointColor = function (d) { var $$ = this, target = $$.data.targets[0]; return target.colorscale(d.epochs); }; // http://jsfiddle.net/Xotic750/KtzLq/ ChartInternal.prototype.getCentroid = function (points) { var area = getRegionArea(points); var x = 0, y = 0, i, j, f, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; f = point1.x * point2.y - point2.x * point1.y; x += (point1.x + point2.x) * f; y += (point1.y + point2.y) * f; } f = area * 6; return { x: x / f, y: y / f }; }; ChartInternal.prototype.getStanfordTooltipTitle = function (d) { var $$ = this, labelX = $$.axis.getLabelText('x'), labelY = $$.axis.getLabelText('y'); return "\n " + (labelX ? sanitise(labelX) : 'x') + "" + d.x + "\n " + (labelY ? sanitise(labelY) : 'y') + "" + d.value + "\n "; }; ChartInternal.prototype.countEpochsInRegion = function (region) { var $$ = this, target = $$.data.targets[0], total, count; total = target.values.reduce(function (accumulator, currentValue) { return accumulator + Number(currentValue.epochs); }, 0); count = target.values.reduce(function (accumulator, currentValue) { if (pointInRegion(currentValue, region)) { return accumulator + Number(currentValue.epochs); } return accumulator; }, 0); return { value: count, percentage: count !== 0 ? ((count / total) * 100).toFixed(1) : 0 }; }; var getRegionArea = function (points) { // thanks to: https://stackoverflow.com/questions/16282330/find-centerpoint-of-polygon-in-javascript var area = 0, i, j, point1, point2; for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i]; point2 = points[j]; area += point1.x * point2.y; area -= point1.y * point2.x; } area /= 2; return area; }; var pointInRegion = function (point, region) { // thanks to: http://bl.ocks.org/bycoffe/5575904 // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html var xi, yi, yj, xj, intersect, x = point.x, y = point.value, inside = false; for (var i = 0, j = region.length - 1; i < region.length; j = i++) { xi = region[i].x; yi = region[i].y; xj = region[j].x; yj = region[j].y; intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; if (intersect) { inside = !inside; } } return inside; }; var compareEpochs = function (a, b) { if (a.epochs < b.epochs) { return -1; } if (a.epochs > b.epochs) { return 1; } return 0; }; ChartInternal.prototype.initStanfordElements = function () { var $$ = this; // Avoid blocking eventRect $$.stanfordElements = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.stanfordElements); $$.stanfordElements.append('g').attr('class', CLASS.stanfordLines); $$.stanfordElements.append('g').attr('class', CLASS.stanfordTexts); $$.stanfordElements.append('g').attr('class', CLASS.stanfordRegions); }; ChartInternal.prototype.updateStanfordElements = function (duration) { var $$ = this, main = $$.main, config = $$.config, stanfordLine, stanfordLineEnter, stanfordRegion, stanfordRegionEnter, stanfordText, stanfordTextEnter, xvCustom = $$.xvCustom.bind($$), yvCustom = $$.yvCustom.bind($$), countPointsInRegion = $$.countEpochsInRegion.bind($$); // Stanford-Lines stanfordLine = main .select('.' + CLASS.stanfordLines) .style('shape-rendering', 'geometricprecision') .selectAll('.' + CLASS.stanfordLine) .data(config.stanford_lines); // enter stanfordLineEnter = stanfordLine .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordLine + (d['class'] ? ' ' + d['class'] : ''); }); stanfordLineEnter .append('line') .attr('x1', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1'); }) .attr('x2', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2'); }) .attr('y1', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1'); }) .attr('y2', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2'); }) .style('opacity', 0); // update $$.stanfordLines = stanfordLineEnter.merge(stanfordLine); $$.stanfordLines .select('line') .transition() .duration(duration) .attr('x1', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1'); }) .attr('x2', function (d) { return config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2'); }) .attr('y1', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1'); }) .attr('y2', function (d) { return config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2'); }) .style('opacity', 1); // exit stanfordLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Text stanfordText = main .select('.' + CLASS.stanfordTexts) .selectAll('.' + CLASS.stanfordText) .data(config.stanford_texts); // enter stanfordTextEnter = stanfordText .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordText + (d['class'] ? ' ' + d['class'] : ''); }); stanfordTextEnter .append('text') .attr('x', function (d) { return (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x')); }) .attr('y', function (d) { return (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y')); }) .style('opacity', 0); // update $$.stanfordTexts = stanfordTextEnter.merge(stanfordText); $$.stanfordTexts .select('text') .transition() .duration(duration) .attr('x', function (d) { return (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x')); }) .attr('y', function (d) { return (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y')); }) .text(function (d) { return d.content; }) .style('opacity', 1); // exit stanfordText .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); // Stanford-Regions stanfordRegion = main .select('.' + CLASS.stanfordRegions) .selectAll('.' + CLASS.stanfordRegion) .data(config.stanford_regions); // enter stanfordRegionEnter = stanfordRegion .enter() .append('g') .attr('class', function (d) { return CLASS.stanfordRegion + (d['class'] ? ' ' + d['class'] : ''); }); stanfordRegionEnter .append('polygon') .attr('points', function (d) { return d.points .map(function (value) { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(','); }) .join(' '); }) .style('opacity', 0); stanfordRegionEnter .append('text') .attr('x', function (d) { return $$.getCentroid(d.points).x; }) .attr('y', function (d) { return $$.getCentroid(d.points).y; }) .style('opacity', 0); // update $$.stanfordRegions = stanfordRegionEnter.merge(stanfordRegion); $$.stanfordRegions .select('polygon') .transition() .duration(duration) .attr('points', function (d) { return d.points .map(function (value) { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(','); }) .join(' '); }) .style('opacity', function (d) { return d.opacity ? d.opacity : 0.2; }); $$.stanfordRegions .select('text') .transition() .duration(duration) .attr('x', function (d) { return config.axis_rotated ? yvCustom($$.getCentroid(d.points), 'y') : xvCustom($$.getCentroid(d.points), 'x'); }) .attr('y', function (d) { return config.axis_rotated ? xvCustom($$.getCentroid(d.points), 'x') : yvCustom($$.getCentroid(d.points), 'y'); }) .text(function (d) { if (d.text) { var value, percentage, temp; if ($$.isStanfordGraphType()) { temp = countPointsInRegion(d.points); value = temp.value; percentage = temp.percentage; } return d.text(value, percentage); } return ''; }) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('opacity', 1); // exit stanfordRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove(); }; ChartInternal.prototype.initTooltip = function () { var $$ = this, config = $$.config, i; $$.tooltip = $$.selectChart .style('position', 'relative') .append('div') .attr('class', CLASS.tooltipContainer) .style('position', 'absolute') .style('pointer-events', 'none') .style('display', 'none'); // Show tooltip if needed if (config.tooltip_init_show) { if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { config.tooltip_init_x = $$.parseDate(config.tooltip_init_x); for (i = 0; i < $$.data.targets[0].values.length; i++) { if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { break; } } config.tooltip_init_x = i; } $$.tooltip.html(config.tooltip_contents.call($$, $$.data.targets.map(function (d) { return $$.addName(d.values[config.tooltip_init_x]); }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color)); $$.tooltip .style('top', config.tooltip_init_position.top) .style('left', config.tooltip_init_position.left) .style('display', 'block'); } }; ChartInternal.prototype.getTooltipSortFunction = function () { var $$ = this, config = $$.config; if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { // if data are not grouped or if an order is specified // for the tooltip values we sort them by their values var order = config.tooltip_order; if (order === undefined) { order = config.data_order; } var valueOf = function (obj) { return obj ? obj.value : null; }; // if data are not grouped, we sort them by their value if (isString(order) && order.toLowerCase() === 'asc') { return function (a, b) { return valueOf(a) - valueOf(b); }; } else if (isString(order) && order.toLowerCase() === 'desc') { return function (a, b) { return valueOf(b) - valueOf(a); }; } else if (isFunction(order)) { // if the function is from data_order we need // to wrap the returned function in order to format // the sorted value to the expected format var sortFunction = order; if (config.tooltip_order === undefined) { sortFunction = function (a, b) { return order(a ? { id: a.id, values: [a] } : null, b ? { id: b.id, values: [b] } : null); }; } return sortFunction; } else if (isArray(order)) { return function (a, b) { return order.indexOf(a.id) - order.indexOf(b.id); }; } } else { // if data are grouped, we follow the order of grouped targets var ids = $$.orderTargets($$.data.targets).map(function (i) { return i.id; }); // if it was either asc or desc we need to invert the order // returned by orderTargets if ($$.isOrderAsc() || $$.isOrderDesc()) { ids = ids.reverse(); } return function (a, b) { return ids.indexOf(a.id) - ids.indexOf(b.id); }; } }; ChartInternal.prototype.getTooltipContent = function (d, defaultTitleFormat, defaultValueFormat, color) { var $$ = this, config = $$.config, titleFormat = config.tooltip_format_title || defaultTitleFormat, nameFormat = config.tooltip_format_name || function (name) { return name; }, text, i, title, value, name, bgcolor; var valueFormat = config.tooltip_format_value; if (!valueFormat) { valueFormat = $$.isTargetNormalized(d.id) ? function (v, ratio) { return (ratio * 100).toFixed(2) + "%"; } : defaultValueFormat; } var tooltipSortFunction = this.getTooltipSortFunction(); if (tooltipSortFunction) { d.sort(tooltipSortFunction); } for (i = 0; i < d.length; i++) { if (!(d[i] && (d[i].value || d[i].value === 0))) { continue; } if ($$.isStanfordGraphType()) { // Custom tooltip for stanford plots if (!text) { title = $$.getStanfordTooltipTitle(d[i]); text = "" + title; } bgcolor = $$.getStanfordPointColor(d[i]); name = sanitise(config.data_epochs); // Epochs key name value = d[i].epochs; } else { // Regular tooltip if (!text) { title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x); text = "
" + (title || title === 0 ? "' : ''); } value = sanitise(valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d)); if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue; } name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)); bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id); } } if (value !== undefined) { text += ""; text += "'; text += "'; text += ''; } } return text + '
" + title + '
" + name + '" + value + '
'; }; ChartInternal.prototype.tooltipPosition = function (dataToShow, tWidth, tHeight, element) { var $$ = this, config = $$.config, d3 = $$.d3; var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight; var forArc = $$.hasArcType(), mouse = d3.mouse(element); // Determin tooltip position if (forArc) { tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0]; tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20; } else { svgLeft = $$.getSvgLeft(true); if (config.axis_rotated) { tooltipLeft = svgLeft + mouse[0] + 100; tooltipRight = tooltipLeft + tWidth; chartRight = $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = $$.x(dataToShow[0].x) + 20; } else { tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20; tooltipRight = tooltipLeft + tWidth; chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight(); tooltipTop = mouse[1] + 15; } if (tooltipRight > chartRight) { // 20 is needed for Firefox to keep tooltip width tooltipLeft -= tooltipRight - chartRight + 20; } if (tooltipTop + tHeight > $$.currentHeight) { tooltipTop -= tHeight + 30; } } if (tooltipTop < 0) { tooltipTop = 0; } return { top: tooltipTop, left: tooltipLeft }; }; ChartInternal.prototype.showTooltip = function (selectedData, element) { var $$ = this, config = $$.config; var tWidth, tHeight, position; var forArc = $$.hasArcType(), dataToShow = selectedData.filter(function (d) { return d && isValue(d.value); }), positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition; if (dataToShow.length === 0 || !config.tooltip_show) { $$.hideTooltip(); return; } $$.tooltip .html(config.tooltip_contents.call($$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color)) .style('display', 'block'); // Get tooltip dimensions tWidth = $$.tooltip.property('offsetWidth'); tHeight = $$.tooltip.property('offsetHeight'); position = positionFunction.call(this, dataToShow, tWidth, tHeight, element); // Set tooltip $$.tooltip .style('top', position.top + 'px') .style('left', position.left + 'px'); }; ChartInternal.prototype.hideTooltip = function () { this.tooltip.style('display', 'none'); }; ChartInternal.prototype.setTargetType = function (targetIds, type) { var $$ = this, config = $$.config; $$.mapToTargetIds(targetIds).forEach(function (id) { $$.withoutFadeIn[id] = type === config.data_types[id]; config.data_types[id] = type; }); if (!targetIds) { config.data_type = type; } }; ChartInternal.prototype.hasType = function (type, targets) { var $$ = this, types = $$.config.data_types, has = false; targets = targets || $$.data.targets; if (targets && targets.length) { targets.forEach(function (target) { var t = types[target.id]; if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { has = true; } }); } else if (Object.keys(types).length) { Object.keys(types).forEach(function (id) { if (types[id] === type) { has = true; } }); } else { has = $$.config.data_type === type; } return has; }; ChartInternal.prototype.hasArcType = function (targets) { return (this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets)); }; ChartInternal.prototype.isLineType = function (d) { var config = this.config, id = isString(d) ? d : d.id; return (!config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf(config.data_types[id]) >= 0); }; ChartInternal.prototype.isStepType = function (d) { var id = isString(d) ? d : d.id; return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0; }; ChartInternal.prototype.isSplineType = function (d) { var id = isString(d) ? d : d.id; return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0; }; ChartInternal.prototype.isAreaType = function (d) { var id = isString(d) ? d : d.id; return (['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0); }; ChartInternal.prototype.isBarType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'bar'; }; ChartInternal.prototype.isScatterType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'scatter'; }; ChartInternal.prototype.isStanfordType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'stanford'; }; ChartInternal.prototype.isPieType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'pie'; }; ChartInternal.prototype.isGaugeType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'gauge'; }; ChartInternal.prototype.isDonutType = function (d) { var id = isString(d) ? d : d.id; return this.config.data_types[id] === 'donut'; }; ChartInternal.prototype.isArcType = function (d) { return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d); }; ChartInternal.prototype.lineData = function (d) { return this.isLineType(d) ? [d] : []; }; ChartInternal.prototype.arcData = function (d) { return this.isArcType(d.data) ? [d] : []; }; /* not used function scatterData(d) { return isScatterType(d) ? d.values : []; } */ ChartInternal.prototype.barData = function (d) { return this.isBarType(d) ? d.values : []; }; ChartInternal.prototype.lineOrScatterOrStanfordData = function (d) { return this.isLineType(d) || this.isScatterType(d) || this.isStanfordType(d) ? d.values : []; }; ChartInternal.prototype.barOrLineData = function (d) { return this.isBarType(d) || this.isLineType(d) ? d.values : []; }; ChartInternal.prototype.isSafari = function () { var ua = window.navigator.userAgent; return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0; }; ChartInternal.prototype.isChrome = function () { var ua = window.navigator.userAgent; return ua.indexOf('Chrome') >= 0; }; ChartInternal.prototype.initZoom = function () { var $$ = this, d3 = $$.d3, config = $$.config, startEvent; $$.zoom = d3 .zoom() .on('start', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } startEvent = e; config.zoom_onzoomstart.call($$.api, e); }) .on('zoom', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } $$.redrawForZoom(); config.zoom_onzoom.call($$.api, $$.x.orgDomain()); }) .on('end', function () { if (config.zoom_type !== 'scroll') { return; } var e = d3.event.sourceEvent; if (e && e.type === 'brush') { return; } // if click, do nothing. otherwise, click interaction will be canceled. if (e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY) { return; } config.zoom_onzoomend.call($$.api, $$.x.orgDomain()); }); $$.zoom.updateDomain = function () { if (d3.event && d3.event.transform) { if (config.axis_rotated && config.zoom_type === 'scroll' && d3.event.sourceEvent.type === 'mousemove') { // we're moving the mouse in a rotated chart with zoom = "scroll", so we need rescaleY (i.e. vertical) $$.x.domain(d3.event.transform.rescaleY($$.subX).domain()); } else { $$.x.domain(d3.event.transform.rescaleX($$.subX).domain()); } } return this; }; $$.zoom.updateExtent = function () { this.scaleExtent([1, Infinity]) .translateExtent([ [0, 0], [$$.width, $$.height] ]) .extent([ [0, 0], [$$.width, $$.height] ]); return this; }; $$.zoom.update = function () { return this.updateExtent().updateDomain(); }; return $$.zoom.updateExtent(); }; ChartInternal.prototype.zoomTransform = function (range) { var $$ = this, s = [$$.x(range[0]), $$.x(range[1])]; return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0); }; ChartInternal.prototype.initDragZoom = function () { var $$ = this; var d3 = $$.d3; var config = $$.config; var context = ($$.context = $$.svg); var brushXPos = $$.margin.left + 20.5; var brushYPos = $$.margin.top + 0.5; if (!(config.zoom_type === 'drag' && config.zoom_enabled)) { return; } var getZoomedDomain = function (selection) { return selection && selection.map(function (x) { return $$.x.invert(x); }); }; var brush = ($$.dragZoomBrush = d3 .brushX() .on('start', function () { $$.api.unzoom(); $$.svg.select('.' + CLASS.dragZoom).classed('disabled', false); config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent); }) .on('brush', function () { config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection)); }) .on('end', function () { if (d3.event.selection == null) { return; } var zoomedDomain = getZoomedDomain(d3.event.selection); if (!config.zoom_disableDefaultBehavior) { $$.api.zoom(zoomedDomain); } $$.svg.select('.' + CLASS.dragZoom).classed('disabled', true); config.zoom_onzoomend.call($$.api, zoomedDomain); })); context .append('g') .classed(CLASS.dragZoom, true) .attr('clip-path', $$.clipPath) .attr('transform', 'translate(' + brushXPos + ',' + brushYPos + ')') .call(brush); }; ChartInternal.prototype.getZoomDomain = function () { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), max = d3.max([$$.orgXDomain[1], config.zoom_x_max]); return [min, max]; }; ChartInternal.prototype.redrawForZoom = function () { var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x; if (!config.zoom_enabled) { return; } if ($$.filterTargetsToShow($$.data.targets).length === 0) { return; } zoom.update(); if (config.zoom_disableDefaultBehavior) { return; } if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]); } $$.redraw({ withTransition: false, withY: config.zoom_rescale, withSubchart: false, withEventRect: false, withDimension: false }); if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') { $$.cancelClick = true; } }; return c3; }))); ================================================ FILE: docs/js/gettingstarted.js ================================================ c3.generate({ bindto: '#chart2_1', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } }); c3.generate({ bindto: '#chart3_1', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' } }, axis: { y2: { show: true } } }); c3.generate({ bindto: '#chart3_2', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' } }, axis: { y: { label: { text: 'Y Label', position: 'outer-middle' } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' } } } }); c3.generate({ bindto: '#chart3_3', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' }, types: { data2: 'bar' } }, axis: { y: { label: { text: 'Y Label', position: 'outer-middle' } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' } } } }); c3.generate({ bindto: '#chart3_4', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' }, types: { data2: 'bar' } }, axis: { y: { label: { text: 'Y Label', position: 'outer-middle' }, tick: { format: d3.format("$,") } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' } } } }); var chart4_1 = c3.generate({ bindto: '#chart4_1', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } }); function example4_1() { chart4_1.load({ columns: [ ['data1', 300, 100, 250, 150, 300, 150, 500], ['data2', 100, 200, 150, 50, 100, 250], ['data3', 600, 700, 350, 450, 800, 550] ] }); } var chart4_2 = c3.generate({ bindto: '#chart4_2', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 600, 700, 350, 450, 800, 550] ] } }); function example4_2() { chart4_2.unload({ ids: ['data2', 'data3'] }); } var chart4_3 = c3.generate({ bindto: '#chart4_3', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 600, 700, 350, 450, 800, 550] ] } }); function example4_3_1() { chart4_3.show(['data2', 'data3']); } function example4_3_2() { chart4_3.hide(['data2', 'data3']); } c3.generate({ bindto: '#chart5_1', data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 600, 700, 350, 450, 800, 550] ] } }); ================================================ FILE: docs/js/highlight.pack.js ================================================ var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs); ================================================ FILE: docs/js/index.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250, 50, 100, 250] ], selection: { enabled: true } } }); var defaultMessage = $('#message').html(), currentIndex = 0, timer, duration = 1500, demos = [ function () { chart.load({ columns: [['data2', 100, 30, 200, 320, 50, 150, 230, 80, 150]] }) setMessage('Load data2'); }, function () { chart.load({ columns: [['data3', 70, 90, 170, 220, 100, 110, 130, 40, 50]] }) setMessage('Load data3'); }, function () { chart.select(['data1'], [2]); setMessage('Select point for index 2 of data1'); }, function () { chart.select(['data1'], [4,6]); setMessage('Select point for index 4,6 of data1'); }, function () { chart.unselect(); setMessage('Unselect points'); }, function () { chart.focus('data2'); setMessage('Focus on data2'); }, function () { chart.focus('data3'); setMessage('Focus on data3'); }, function () { chart.revert(); setMessage('Defocus'); }, function () { chart.load({ columns: [['data1', 300, 230, 400, 520, 230, 250, 330, 280, 250]] }) setMessage('Update data1'); }, function () { chart.load({ columns: [['data2', 30, 50, 90, 120, 40, 50, 80, 70, 50]] }) setMessage('Update data2'); }, function () { chart.regions([{start:1,end:3}]); setMessage('Add region from 1 to 3'); }, function () { chart.regions.add([{start:6}]); setMessage('Add region from 6 to end'); }, function () { chart.regions([]); setMessage('Clear regions'); }, function () { chart.xgrids([{value: 1, text:'Label 1'}, {value: 4, text: 'Label 4'}]); setMessage('Add x grid lines for 1, 4'); }, function () { chart.ygrids.add([{value: 450, text:'Label 450'}]); setMessage('Add y grid lines for 450'); }, function () { chart.xgrids.remove({value: 1}); chart.xgrids.remove({value: 4}); setMessage('Remove grid lines for 1, 4'); }, function () { chart.ygrids.remove({value: 450}); setMessage('Remove grid line for 450'); }, function () { chart.transform('bar'); setMessage('Show as bar chart'); }, function () { chart.groups([['data2','data3']]); setMessage('Grouping data2 and data3'); }, function () { chart.groups([['data1', 'data2', 'data3']]); setMessage('Grouping data1, data2 and data3'); }, function () { chart.groups([['data2', 'data3']]); chart.transform('line', 'data1'); setMessage('Show data1 as line'); }, function () { chart.unload({ ids: 'data3' }); setMessage('Unload data3'); }, function () { chart.unload({ ids: 'data2' }); setMessage('Unload data2'); }, function () { chart.flow({ columns: [ ['data1', 390, 400, 200, 500] ], duration: 1000, }); setMessage('Flow 4 data'); }, function () { // wait for end of transition for flow }, function () { chart.flow({ columns: [ ['data1', 190, 230] ], }); setMessage('Flow 2 data'); }, function () { // wait for end of transition for flow }, function () { chart.transform('line', ['data1', 'data2', 'data3']); chart.groups([['data1'], ['data2'], ['data3']]); chart.load({ columns: [['data1', 30, 200, 100, 400, 150, 250, 50, 100, 250]] }) setMessage('Starting Demo..'); } ]; function setMessage(message) { document.getElementById('message').innerHTML = ''+message+''; // $('#demoMessage').tooltip('toggle'); } function startDemo() { setMessage('Starting Demo..'); timer = setInterval(function(){ if (currentIndex == demos.length) currentIndex = 0; demos[currentIndex++](); }, duration); } function stopDemo() { clearInterval(timer); document.getElementById('message').innerHTML = defaultMessage; } ================================================ FILE: docs/js/main.js ================================================ ================================================ FILE: docs/js/plugins.js ================================================ // Avoid `console` errors in browsers that lack a console. if (!(window.console && console.log)) { (function() { var noop = function() {}; var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn']; var length = methods.length; var console = window.console = {}; while (length--) { console[methods[length]] = noop; } }()); } // Place any jQuery/helper plugins in here. ================================================ FILE: docs/js/samples/api_axis_label.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y: { label: 'Y Axis Label' }, y2: { show: true, label: 'Y2 Axis Label' } } }); setTimeout(function () { chart.axis.labels({y2: 'New Y2 Axis Label'}); }, 1000); setTimeout(function () { chart.axis.labels({y: 'New Y Axis Label', y2: 'New Y2 Axis Label Again'}); }, 2000); ================================================ FILE: docs/js/samples/api_axis_range.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y2: { show: true, } } }); setTimeout(function () { chart.axis.max(500); }, 1000); setTimeout(function () { chart.axis.min(-500); }, 2000); setTimeout(function () { chart.axis.max({y: 600, y2: 100}); }, 3000); setTimeout(function () { chart.axis.min({y: -600, y2: -100}); }, 4000); setTimeout(function () { chart.axis.range({max: 1000, min: -1000}); }, 5000); setTimeout(function () { chart.axis.range({max: {y: 600, y2: 100}, min: {y: -100, y2: 0}}); }, 6000); setTimeout(function () { chart.axis.max({x: 10}); }, 7000); setTimeout(function () { chart.axis.min({x: -10}); }, 8000); setTimeout(function () { chart.axis.range({max: {x: 5}, min: {x: 0}}); }, 9000); ================================================ FILE: docs/js/samples/api_data_color.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 20, 50, 40, 60, 50], ['data2', 200, 130, 90, 240, 130, 220], ['data3', 300, 200, 160, 400, 250, 250] ], type: 'bar', colors: { data1: '#ff0000', data2: '#00ff00', data3: '#0000ff' }, labels: true } }); setTimeout(function () { chart.data.colors({ data1: d3.rgb('#ff0000').darker(1), data2: d3.rgb('#00ff00').darker(1), data3: d3.rgb('#0000ff').darker(1), }); }, 1000); setTimeout(function () { chart.data.colors({ data1: d3.rgb('#ff0000').darker(2), data2: d3.rgb('#00ff00').darker(2), data3: d3.rgb('#0000ff').darker(2), }); }, 2000); ================================================ FILE: docs/js/samples/api_data_name.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], names: { data1: 'Name 1', data2: 'Name 2' } } }); setTimeout(function () { chart.data.names({data1: 'New name for data1', data2: 'New name for data2'}); }, 1000); setTimeout(function () { chart.data.names({data1: 'New name for data1 again'}); }, 2000); ================================================ FILE: docs/js/samples/api_flow.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', '2012-12-29', '2012-12-30', '2012-12-31'], ['data1', 230, 300, 330], ['data2', 190, 230, 200], ['data3', 90, 130, 180], ] }, axis: { x: { type: 'timeseries', tick: { format: '%m/%d', } } } }); setTimeout(function () { chart.flow({ columns: [ ['x', '2013-01-11', '2013-01-21'], ['data1', 500, 200], ['data2', 100, 300], ['data3', 200, 120], ], duration: 1500, done: function () { chart.flow({ columns: [ ['x', '2013-02-11', '2013-02-12', '2013-02-13', '2013-02-14'], ['data1', 200, 300, 100, 250], ['data2', 100, 90, 40, 120], ['data3', 100, 100, 300, 500] ], length: 0, duration: 1500, done: function () { chart.flow({ columns: [ ['x', '2013-03-01', '2013-03-02'], ['data1', 200, 300], ['data2', 150, 250], ['data3', 100, 100] ], length: 2, duration: 1500, done: function () { chart.flow({ columns: [ ['x', '2013-03-21', '2013-04-01'], ['data1', 500, 200], ['data2', 100, 150], ['data3', 200, 400] ], to: '2013-03-01', duration: 1500, }); } }); } }); }, }); }, 1000); ================================================ FILE: docs/js/samples/api_grid_x.js ================================================ var chart = c3.generate({ bindto: '#chart', data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] } }); setTimeout(function () { chart.xgrids([{value: 1, text:'Label 1'}, {value: 4, text: 'Label 4'}]); }, 1000); setTimeout(function () { chart.xgrids([{value: 2, text:'Label 2'}]); }, 2000); setTimeout(function () { chart.xgrids.add([{value: 3, text:'Label 3', class:'hoge'}]); }, 3000); setTimeout(function () { chart.xgrids.remove({value:2}); }, 4000); setTimeout(function () { chart.xgrids.remove({class:'hoge'}); }, 5000); setTimeout(function () { chart.xgrids([{value: 1, text:'Label 1'}, {value: 4, text: 'Label 4'}]); }, 6000); setTimeout(function () { chart.xgrids.remove(); }, 7000); ================================================ FILE: docs/js/samples/api_resize.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } }); setTimeout(function () { chart.resize({height:100, width:300}) }, 1000); setTimeout(function () { chart.resize({height:200}) }, 2000); setTimeout(function () { chart.resize(); }, 3000); ================================================ FILE: docs/js/samples/axes_label.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250], ['sample2', 130, 300, 200, 500, 250, 350] ], axes: { sample2: 'y2' } }, axis: { x: { label: 'X Label' }, y: { label: 'Y Label' }, y2: { show: true, label: 'Y2 Label' } } }); ================================================ FILE: docs/js/samples/axes_label_position.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample1', 30, 200, 100, 400, 150, 250], ['sample2', 430, 300, 500, 400, 650, 250] ], axes: { sample1: 'y', sample2: 'y2' } }, axis: { x: { label: { text: 'X Label', position: 'outer-center' // inner-right : default // inner-center // inner-left // outer-right // outer-center // outer-left } }, y: { label: { text: 'Y Label', position: 'outer-middle' // inner-top : default // inner-middle // inner-bottom // outer-top // outer-middle // outer-bottom } }, y2: { show: true, label: { text: 'Y2 Label', position: 'outer-middle' // inner-top : default // inner-middle // inner-bottom // outer-top // outer-middle // outer-bottom } } } }); ================================================ FILE: docs/js/samples/axes_rotated.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], types: { data1: 'bar', } }, axis: { rotated: true } }); ================================================ FILE: docs/js/samples/axes_x_localtime.js ================================================ var chart = c3.generate({ data: { x: 'x', xFormat: '%Y', columns: [ // ['x', '2012-12-31', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', '2013-01-05'], ['x', '2010', '2011', '2012', '2013', '2014', '2015'], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 340, 200, 500, 250, 350] ] }, axis: { x: { type: 'timeseries', // if true, treat x value as localtime (Default) // if false, convert to UTC internally localtime: false, tick: { format: '%Y-%m-%d %H:%M:%S' } } } }); ================================================ FILE: docs/js/samples/axes_x_tick_count.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', '2013-01-05', '2013-01-06', '2013-01-07', '2013-01-08', '2013-01-09', '2013-01-10', '2013-01-11', '2013-01-12'], ['sample', 30, 200, 100, 400, 150, 250, 30, 200, 100, 400, 150, 250] ] }, axis: { x: { type: 'timeseries', tick: { count: 4, format: '%Y-%m-%d' } } } }); ================================================ FILE: docs/js/samples/axes_x_tick_culling.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250, 30, 200, 100, 400, 150, 250, 30, 200, 100, 400, 150, 250, 200, 100, 400, 150, 250] ] }, axis: { x: { type: 'category', tick: { culling: { max: 4 // the number of tick texts will be adjusted to less than this value } // for normal axis, default on // for category axis, default off } } } }); ================================================ FILE: docs/js/samples/axes_x_tick_fit.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', '2013-10-31', '2013-12-31', '2014-01-31', '2014-02-28'], ['sample', 30, 100, 400, 150], ] }, axis : { x : { type : 'timeseries', tick: { fit: true, format: "%e %b %y" } } } }); ================================================ FILE: docs/js/samples/axes_x_tick_format.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', '2010-01-01', '2011-01-01', '2012-01-01', '2013-01-01', '2014-01-01', '2015-01-01'], ['sample', 30, 200, 100, 400, 150, 250] ] }, axis : { x : { type : 'timeseries', tick: { format: function (x) { return x.getFullYear(); } //format: '%Y' // format string is also available for timeseries data } } } }); ================================================ FILE: docs/js/samples/axes_x_tick_rotate.js ================================================ var chart = c3.generate({ data: { x : 'x', columns: [ ['x', 'www.somesitename1.com', 'www.somesitename2.com', 'www.somesitename3.com', 'www.somesitename4.com', 'www.somesitename5.com', 'www.somesitename6.com', 'www.somesitename7.com', 'www.somesitename8.com', 'www.somesitename9.com', 'www.somesitename10.com', 'www.somesitename11.com', 'www.somesitename12.com'], ['pv', 90, 100, 140, 200, 100, 400, 90, 100, 140, 200, 100, 400], ], type: 'bar' }, axis: { x: { type: 'category', tick: { rotate: 75, multiline: false }, height: 130 } } }); ================================================ FILE: docs/js/samples/axes_x_tick_values.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', '2013-01-05', '2013-01-06', '2013-01-07', '2013-01-08', '2013-01-09', '2013-01-10', '2013-01-11', '2013-01-12'], ['sample', 30, 200, 100, 400, 150, 250, 30, 200, 100, 400, 150, 250] ] }, axis: { x: { type: 'timeseries', tick: { // this also works for non timeseries data values: ['2013-01-05', '2013-01-10'] } } } }); ================================================ FILE: docs/js/samples/axes_y2.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y2: { show: true } } }); ================================================ FILE: docs/js/samples/axes_y_padding.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y: { padding: {top: 200, bottom: 0} }, y2: { padding: {top: 100, bottom: 100}, show: true } } }); ================================================ FILE: docs/js/samples/axes_y_range.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] }, axis: { y: { max: 400, min: -400, // Range includes padding, set 0 if no padding needed // padding: {top:0, bottom:0} } } }); ================================================ FILE: docs/js/samples/axes_y_tick_format.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 2500] ] }, axis : { y : { tick: { format: d3.format("$,") // format: function (d) { return "$" + d; } } } } }); ================================================ FILE: docs/js/samples/categorized.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250, 50, 100, 250] ] }, axis: { x: { type: 'category', categories: ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6', 'cat7', 'cat8', 'cat9'] } } }); ================================================ FILE: docs/js/samples/chart_area.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 300, 350, 300, 0, 0, 0], ['data2', 130, 100, 140, 200, 150, 50] ], types: { data1: 'area', data2: 'area-spline' } } }); ================================================ FILE: docs/js/samples/chart_area_stacked.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 300, 350, 300, 0, 0, 120], ['data2', 130, 100, 140, 200, 150, 50] ], types: { data1: 'area-spline', data2: 'area-spline' // 'line', 'spline', 'step', 'area', 'area-step' are also available to stack }, groups: [['data1', 'data2']] } }); ================================================ FILE: docs/js/samples/chart_bar.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'bar' }, bar: { width: { ratio: 0.5 // this makes bar width 50% of length between ticks } // or //width: 100 // this makes bar width 100px } }); setTimeout(function () { chart.load({ columns: [ ['data3', 130, -150, 200, 300, -200, 100] ] }); }, 1000); ================================================ FILE: docs/js/samples/chart_bar_stacked.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', -30, 200, 200, 400, -150, 250], ['data2', 130, 100, -100, 200, -150, 50], ['data3', -230, 200, 200, -300, 250, 250] ], type: 'bar', groups: [ ['data1', 'data2'] ] }, grid: { y: { lines: [{value:0}] } } }); setTimeout(function () { chart.groups([['data1', 'data2', 'data3']]) }, 1000); setTimeout(function () { chart.load({ columns: [['data4', 100, -50, 150, 200, -300, -100]] }); }, 1500); setTimeout(function () { chart.groups([['data1', 'data2', 'data3', 'data4']]) }, 2000); ================================================ FILE: docs/js/samples/chart_combination.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 20, 50, 40, 60, 50], ['data2', 200, 130, 90, 240, 130, 220], ['data3', 300, 200, 160, 400, 250, 250], ['data4', 200, 130, 90, 240, 130, 220], ['data5', 130, 120, 150, 140, 160, 150], ['data6', 90, 70, 20, 50, 60, 120], ], type: 'bar', types: { data3: 'spline', data4: 'line', data6: 'area', }, groups: [ ['data1','data2'] ] } }); ================================================ FILE: docs/js/samples/chart_donut.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30], ['data2', 120], ], type : 'donut', onclick: function (d, i) { console.log("onclick", d, i); }, onmouseover: function (d, i) { console.log("onmouseover", d, i); }, onmouseout: function (d, i) { console.log("onmouseout", d, i); } }, donut: { title: "Iris Petal Width" } }); setTimeout(function () { chart.load({ columns: [ ["setosa", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2], ["versicolor", 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4, 1.0, 1.5, 1.0, 1.4, 1.3, 1.4, 1.5, 1.0, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 1.5, 1.0, 1.1, 1.0, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3], ["virginica", 2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5, 2.0, 1.9, 2.1, 2.0, 2.4, 2.3, 1.8, 2.2, 2.3, 1.5, 2.3, 2.0, 2.0, 1.8, 2.1, 1.8, 1.8, 1.8, 2.1, 1.6, 1.9, 2.0, 2.2, 1.5, 1.4, 2.3, 2.4, 1.8, 1.8, 2.1, 2.4, 2.3, 1.9, 2.3, 2.5, 2.3, 1.9, 2.0, 2.3, 1.8], ] }); }, 1500); setTimeout(function () { chart.unload({ ids: 'data1' }); chart.unload({ ids: 'data2' }); }, 2500); ================================================ FILE: docs/js/samples/chart_gauge.js ================================================ var chart = c3.generate({ data: { columns: [ ['data', 91.4] ], type: 'gauge', onclick: function (d, i) { console.log("onclick", d, i); }, onmouseover: function (d, i) { console.log("onmouseover", d, i); }, onmouseout: function (d, i) { console.log("onmouseout", d, i); } }, gauge: { // label: { // format: function(value, ratio) { // return value; // }, // show: false // to turn off the min/max labels. // }, // min: 0, // 0 is default, //can handle negative min e.g. vacuum / voltage / current flow / rate of change // max: 100, // 100 is default // units: ' %', // width: 39 // for adjusting arc thickness }, color: { pattern: ['#FF0000', '#F97600', '#F6C600', '#60B044'], // the three color levels for the percentage values. threshold: { // unit: 'value', // percentage is default // max: 200, // 100 is default values: [30, 60, 90, 100] } }, size: { height: 180 } }); setTimeout(function () { chart.load({ columns: [['data', 10]] }); }, 1000); setTimeout(function () { chart.load({ columns: [['data', 50]] }); }, 2000); setTimeout(function () { chart.load({ columns: [['data', 70]] }); }, 3000); setTimeout(function () { chart.load({ columns: [['data', 0]] }); }, 4000); setTimeout(function () { chart.load({ columns: [['data', 100]] }); }, 5000); ================================================ FILE: docs/js/samples/chart_pie.js ================================================ var chart = c3.generate({ data: { // iris data from R columns: [ ['data1', 30], ['data2', 120], ], type : 'pie', onclick: function (d, i) { console.log("onclick", d, i); }, onmouseover: function (d, i) { console.log("onmouseover", d, i); }, onmouseout: function (d, i) { console.log("onmouseout", d, i); } } }); setTimeout(function () { chart.load({ columns: [ ["setosa", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2], ["versicolor", 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4, 1.0, 1.5, 1.0, 1.4, 1.3, 1.4, 1.5, 1.0, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 1.5, 1.0, 1.1, 1.0, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3], ["virginica", 2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5, 2.0, 1.9, 2.1, 2.0, 2.4, 2.3, 1.8, 2.2, 2.3, 1.5, 2.3, 2.0, 2.0, 1.8, 2.1, 1.8, 1.8, 1.8, 2.1, 1.6, 1.9, 2.0, 2.2, 1.5, 1.4, 2.3, 2.4, 1.8, 1.8, 2.1, 2.4, 2.3, 1.9, 2.3, 2.5, 2.3, 1.9, 2.0, 2.3, 1.8], ] }); }, 1500); setTimeout(function () { chart.unload({ ids: 'data1' }); chart.unload({ ids: 'data2' }); }, 2500); ================================================ FILE: docs/js/samples/chart_scatter.js ================================================ var chart = c3.generate({ data: { xs: { setosa: 'setosa_x', versicolor: 'versicolor_x', }, // iris data from R columns: [ ["setosa_x", 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3.0, 3.0, 4.0, 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3.0, 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.6, 3.0, 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3.0, 3.8, 3.2, 3.7, 3.3], ["versicolor_x", 3.2, 3.2, 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2.0, 3.0, 2.2, 2.9, 2.9, 3.1, 3.0, 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3.0, 2.8, 3.0, 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3.0, 3.4, 3.1, 2.3, 3.0, 2.5, 2.6, 3.0, 2.6, 2.3, 2.7, 3.0, 2.9, 2.9, 2.5, 2.8], ["setosa", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2], ["versicolor", 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4, 1.0, 1.5, 1.0, 1.4, 1.3, 1.4, 1.5, 1.0, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 1.5, 1.0, 1.1, 1.0, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3], ], type: 'scatter' }, axis: { x: { label: 'Sepal.Width', tick: { fit: false } }, y: { label: 'Petal.Width' } } }); setTimeout(function () { chart.load({ xs: { virginica: 'virginica_x' }, columns: [ ["virginica_x", 3.3, 2.7, 3.0, 2.9, 3.0, 3.0, 2.5, 2.9, 2.5, 3.6, 3.2, 2.7, 3.0, 2.5, 2.8, 3.2, 3.0, 3.8, 2.6, 2.2, 3.2, 2.8, 2.8, 2.7, 3.3, 3.2, 2.8, 3.0, 2.8, 3.0, 2.8, 3.8, 2.8, 2.8, 2.6, 3.0, 3.4, 3.1, 3.0, 3.1, 3.1, 3.1, 2.7, 3.2, 3.3, 3.0, 2.5, 3.0, 3.4, 3.0], ["virginica", 2.5, 1.9, 2.1, 1.8, 2.2, 2.1, 1.7, 1.8, 1.8, 2.5, 2.0, 1.9, 2.1, 2.0, 2.4, 2.3, 1.8, 2.2, 2.3, 1.5, 2.3, 2.0, 2.0, 1.8, 2.1, 1.8, 1.8, 1.8, 2.1, 1.6, 1.9, 2.0, 2.2, 1.5, 1.4, 2.3, 2.4, 1.8, 1.8, 2.1, 2.4, 2.3, 1.9, 2.3, 2.5, 2.3, 1.9, 2.0, 2.3, 1.8], ] }); }, 1000); setTimeout(function () { chart.unload({ ids: 'setosa' }); }, 2000); setTimeout(function () { chart.load({ columns: [ ["virginica", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2], ] }); }, 3000); ================================================ FILE: docs/js/samples/chart_spline.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'spline' } }); ================================================ FILE: docs/js/samples/chart_stanford.js ================================================ // More samples available at: // - htdocs/samples/chart_stanford.html // - htdocs/samples/chart_stanford_custom_elements.html var chart = c3.generate({ data: { x: 'HPE', epochs: 'Epochs', columns: [ ['HPE', 2.5, 2.5, 2.5, 2.5, 2.5, 3, 3, 3, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 4.5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5.5, 5.5, 5.5, 2, 2.5, 2.5, 3, 3, 3.5, 3.5, 3.5, 3.5, 3.5, 3.5, 4, 4, 4, 4, 4, 4.5, 4.5, 4.5, 4.5, 4.5, 5, 5, 5, 5, 5, 5, 5.5, 5.5, 2.5, 3, 3, 3.5, 3.5, 3.5, 3.5, 4, 4, 4, 4, 4, 4.5, 4.5, 4.5, 5, 5, 5, 5, 5, 5, 5.5, 5.5, 5.5, 5.5, 2.5, 2.5, 2.5, 3, 3.5, 3.5, 3.5, 3.5, 3.5, 4, 4.5, 4.5, 4.5, 4.5, 4.5, 5, 5, 5, 5.5, 2, 3.5, 3.5, 3.5, 3.5, 3.5, 4, 4.5, 4.5, 4.5, 4.5, 5, 2, 2, 3, 3, 3.5, 3.5, 3.5, 3.5, 4, 4, 4, 4, 5, 2, 3, 3, 3.5, 3.5, 3.5, 3.5, 4, 4.5, 5, 5, 5, 5.5, 5.5, 2.5, 3, 3, 3, 3.5, 4, 2.5, 3, 3.5, 4, 4, 4.5, 5, 3.5, 4, 4, 4, 4, 4.5, 3.5, 4, 4.5, 5, 5, 2.5, 3, 3.5, 3.5, 4, 4.5, 4.5, 4, 5, 3, 4, 4, 2, 4.5, 3.5, 2.5, 3.5, 4, 4, 2.5, 2.5, 3, 3, 4, 4.5, 5, 5, 4.5, 2.5, 3, 4, 3, 3.5, 3.5, 4, 2.5, 3.5, 2.5, 3.5, 2.5, 2.5, 3.5, 2.5, 4.5, 3, 4, 2.5, 4.5, 2.5, 4, 4, 2.5, 3, 3.5, 2.5, 3.5, 3.5, 3.5, 2.5, 3.5, 3.5, 4, 4, 3.5, 4, 4, 4], ['HPL', 24.5, 24, 27.5, 56.5, 26.5, 26, 51.5, 50, 39, 39.5, 54, 48.5, 54.5, 53, 52, 13.5, 16.5, 15.5, 14.5, 19, 19.5, 41, 40, 42.5, 40.5, 41.5, 30, 56, 47, 11.5, 11, 12, 14.5, 55, 56.5, 54, 55.5, 56, 48.5, 19, 56, 56.5, 53.5, 51.5, 52, 31.5, 36.5, 38.5, 22, 21, 22.5, 37, 38, 38.5, 11, 55, 14.5, 12.5, 56, 22, 11, 48, 12.5, 14, 17, 13.5, 43, 55.5, 53.5, 10.5, 49.5, 54.5, 51.5, 19.5, 24, 52.5, 49.5, 47, 45.5, 46, 20, 34.5, 37.5, 28, 10, 26.5, 22.5, 13, 18.5, 20, 29, 39.5, 48.5, 50.5, 19.5, 29, 27.5, 52.5, 50.5, 53, 37, 36, 34.5, 20.5, 31.5, 33, 32, 36, 29, 28.5, 31.5, 29, 30, 11.5, 49, 52.5, 20.5, 49.5, 28, 24.5, 53, 50, 23.5, 47.5, 38, 35, 34, 12, 21, 36.5, 51, 12, 58.5, 36.5, 28.5, 51, 50.5, 20, 50, 56, 55, 29.5, 28.5, 23, 17.5, 38.5, 57.5, 29.5, 38.5, 49, 52.5, 34, 11.5, 27, 30, 10, 51.5, 50.5, 18, 20.5, 23, 49, 51, 48, 33.5, 32.5, 27, 28, 25.5, 57.5, 10.5, 52, 29.5, 27.5, 50, 28.5, 51.5, 21.5, 35.5, 49.5, 37.5, 39, 50, 51, 22.5, 58, 20, 25.5, 48.5, 32, 30, 24.5, 23.5, 29.5, 23, 25, 21, 38, 32.5, 12, 22, 37, 55.5, 22, 38, 55.5, 29, 23.5, 21, 12.5, 14, 11.5, 56.5, 21.5, 20.5, 33, 33.5, 27, 13, 10.5, 22.5, 57, 24, 28.5, 28, 10, 37, 56, 37.5, 11, 10.5, 28, 13.5, 26, 11, 27.5, 12, 26.5, 26, 24.5, 24, 25, 25, 25, 11.5, 25.5, 26.5, 26, 25.5, 27.5, 27, 25, 27, 24.5, 26, 26.5, 25.5], ['Epochs', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 18, 18, 18, 18, 19, 19, 19, 19, 19, 20, 20, 20, 22, 23, 23, 23, 24, 24, 24, 24, 25, 28, 29, 29, 36, 38, 39, 43, 44, 47, 50, 54, 54, 59, 62, 62, 70, 70, 81, 84, 85, 86, 88, 89, 93, 94, 95, 106, 110, 111, 115, 170] ], type: 'stanford', }, legend: { hide: true }, point: { focus: { expand: { r: 5 } }, r: 2 }, axis: { x: { show: true, label: { text: 'HPE (m)', position: 'outer-center' }, min: 0, max: 61, tick: { values: d3.range(0, 65, 10) }, padding: { top: 0, bottom: 0, left: 0, right: 0 }, }, y: { show: true, label: { text: 'HPL (m)', position: 'outer-middle' }, min: 0, max: 60, tick: { values: d3.range(0, 65, 10) }, padding: { top: 5, bottom: 0, left: 0, right: 0 }, } }, stanford: { scaleMin: 1, scaleMax: 10000, scaleFormat: 'pow10', padding: { top: 15, right: 0, bottom: 0, left: 0 } } }); ================================================ FILE: docs/js/samples/chart_step.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 300, 350, 300, 0, 0, 100], ['data2', 130, 100, 140, 200, 150, 50] ], types: { data1: 'step', data2: 'area-step' } } }); ================================================ FILE: docs/js/samples/data_color.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 20, 50, 40, 60, 50], ['data2', 200, 130, 90, 240, 130, 220], ['data3', 300, 200, 160, 400, 250, 250] ], type: 'bar', colors: { data1: '#ff0000', data2: '#00ff00', data3: '#0000ff' }, color: function (color, d) { // d will be 'id' when called for legends return d.id && d.id === 'data3' ? d3.rgb(color).darker(d.value / 150) : color; } } }); ================================================ FILE: docs/js/samples/data_columned.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 20, 50, 40, 60, 50], ['data2', 200, 130, 90, 240, 130, 220], ['data3', 300, 200, 160, 400, 250, 250] ] } }); ================================================ FILE: docs/js/samples/data_json.js ================================================ var chart = c3.generate({ data: { json: { data1: [30, 20, 50, 40, 60, 50], data2: [200, 130, 90, 240, 130, 220], data3: [300, 200, 160, 400, 250, 250] } } }); setTimeout(function () { chart = c3.generate({ data: { json: [ {name: 'www.site1.com', upload: 200, download: 200, total: 400}, {name: 'www.site2.com', upload: 100, download: 300, total: 400}, {name: 'www.site3.com', upload: 300, download: 200, total: 500}, {name: 'www.site4.com', upload: 400, download: 100, total: 500}, ], keys: { // x: 'name', // it's possible to specify 'x' when category axis value: ['upload', 'download'], } }, axis: { x: { // type: 'category' } } }); }, 1000); setTimeout(function () { chart.load({ json: [ {name: 'www.site1.com', upload: 800, download: 500, total: 400}, {name: 'www.site2.com', upload: 600, download: 600, total: 400}, {name: 'www.site3.com', upload: 400, download: 800, total: 500}, {name: 'www.site4.com', upload: 400, download: 700, total: 500}, ], keys: { value: ['upload', 'download'], } }); }, 2000); ================================================ FILE: docs/js/samples/data_label.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, -200, -100, 400, 150, 250], ['data2', -50, 150, -150, 150, -50, -150], ['data3', -100, 100, -40, 100, -150, -50] ], groups: [ ['data1', 'data2'] ], type: 'bar', labels: true }, grid: { y: { lines: [{value: 0}] } } }); ================================================ FILE: docs/js/samples/data_label_format.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, -200, -100, 400, 150, 250], ['data2', -50, 150, -150, 150, -50, -150], ['data3', -100, 100, -40, 100, -150, -50] ], groups: [ ['data1', 'data2'] ], type: 'bar', labels: { // format: function (v, id, i, j) { return "Default Format"; }, format: { data1: d3.format('$'), // data1: function (v, id, i, j) { return "Format for data1"; }, } } }, grid: { y: { lines: [{value: 0}] } } }); ================================================ FILE: docs/js/samples/data_load.js ================================================ var chart = c3.generate({ data: { url: '/data/c3_test.csv', type: 'line' } }); setTimeout(function () { chart.load({ url: '/data/c3_test2.csv' }); }, 1000); setTimeout(function () { chart.load({ columns: [ ['data1', 130, 120, 150, 140, 160, 150], ['data4', 30, 20, 50, 40, 60, 50], ], unload: ['data2', 'data3'], }); }, 2000); setTimeout(function () { chart.load({ rows: [ ['data2', 'data3'], [120, 300], [160, 240], [200, 290], [160, 230], [130, 300], [220, 320], ], unload: 'data4', }); }, 3000); setTimeout(function () { chart.load({ columns:[ ['data4', 30, 20, 50, 40, 60, 50,100,200] ], type: 'bar' }); }, 4000); setTimeout(function () { chart.unload({ ids: 'data4' }); }, 5000); setTimeout(function () { chart.load({ columns:[ ['data2', null, 30, 20, 50, 40, 60, 50] ] }); }, 6000); setTimeout(function () { chart.unload(); }, 7000); setTimeout(function () { chart.load({ rows: [ ['data4', 'data2', 'data3'], [90, 120, 300], [40, 160, 240], [50, 200, 290], [120, 160, 230], [80, 130, 300], [90, 220, 320], ], type: 'bar' }); }, 8000); setTimeout(function () { chart.load({ rows: [ ['data5', 'data6'], [190, 420], [140, 460], [150, 500], [220, 460], [180, 430], [190, 520], ], type: 'line' }); }, 9000); setTimeout(function () { chart.unload({ ids: ['data2', 'data3'] }); }, 10000); ================================================ FILE: docs/js/samples/data_name.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], names: { data1: 'Name 1', data2: 'Name 2' } } }); ================================================ FILE: docs/js/samples/data_number_format_l10n.js ================================================ // Locale for Russian (ru_RU) var d3locale = d3.formatDefaultLocale({ "decimal": ",", "thousands": "\u00A0", "grouping": [3], "currency": ["", " руб."], "dateTime": "%A, %e %B %Y г. %X", "date": "%d.%m.%Y", "time": "%H:%M:%S", "periods": ["AM", "PM"], "days": ["воскресенье", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"], "shortDays": ["вс", "пн", "вт", "ср", "чт", "пт", "сб"], "months": ["января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"], "shortMonths": ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"] }); // More about locale settings: https://github.com/mbostock/d3/wiki/Localization var chart = c3.generate({ data: { columns: [ ['data1', 30000, 20000, 10000, 40000, 15000, 250000], ['data2', 100.5, 1200.46, 100.1, 40.12, 150.1, 250] ], axes: { data2: 'y2' } }, axis : { y : { tick: { format: d3locale.format(",") } }, y2: { show: true, tick: { format: d3locale.format(",") } } } }); ================================================ FILE: docs/js/samples/data_order.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 130, 200, 320, 400, 530, 750], ['data2', -130, 10, 130, 200, 150, 250], ['data3', -130, -50, -10, -200, -250, -150] ], type: 'bar', groups: [ ['data1', 'data2', 'data3'] ], order: 'desc' // stack order by sum of values descendantly. this is default. // order: 'asc' // stack order by sum of values ascendantly. // order: null // stack order by data definition. }, grid: { y: { lines: [{value:0}] } } }); setTimeout(function () { chart.load({ columns: [ ['data4', 1200, 1300, 1450, 1600, 1520, 1820], ] }); }, 1000); setTimeout(function () { chart.load({ columns: [ ['data5', 200, 300, 450, 600, 520, 820], ] }); }, 2000); setTimeout(function () { chart.groups([['data1', 'data2', 'data3', 'data4', 'data5']]) }, 3000); ================================================ FILE: docs/js/samples/data_rowed.js ================================================ var chart = c3.generate({ data: { rows: [ ['data1', 'data2', 'data3'], [90, 120, 300], [40, 160, 240], [50, 200, 290], [120, 160, 230], [80, 130, 300], [90, 220, 320], ] } }); ================================================ FILE: docs/js/samples/data_stringx.js ================================================ var chart = c3.generate({ data: { x : 'x', columns: [ ['x', 'www.site1.com', 'www.site2.com', 'www.site3.com', 'www.site4.com'], ['download', 30, 200, 100, 400], ['loading', 90, 100, 140, 200], ], groups: [ ['download', 'loading'] ], type: 'bar' }, axis: { x: { type: 'category' // this needed to load string x value } } }); setTimeout(function () { chart.load({ columns: [ ['x', 'www.siteA.com', 'www.siteB.com', 'www.siteC.com', 'www.siteD.com'], ['download', 130, 200, 150, 350], ['loading', 190, 180, 190, 140], ], }); }, 1000); setTimeout(function () { chart.load({ columns: [ ['x', 'www.siteE.com', 'www.siteF.com', 'www.siteG.com'], ['download', 30, 300, 200], ['loading', 90, 130, 240], ], }); }, 2000); setTimeout(function () { chart.load({ columns: [ ['x', 'www.site1.com', 'www.site2.com', 'www.site3.com', 'www.site4.com'], ['download', 130, 300, 200, 470], ['loading', 190, 130, 240, 340], ], }); }, 3000); setTimeout(function () { chart.load({ columns: [ ['download', 30, 30, 20, 170], ['loading', 90, 30, 40, 40], ], }); }, 4000); setTimeout(function () { chart.load({ url: '/data/c3_string_x.csv' }); }, 5000); ================================================ FILE: docs/js/samples/data_url.js ================================================ var chart = c3.generate({ data: { url: '/data/c3_test.csv' } }); setTimeout(function () { c3.generate({ data: { url: '/data/c3_test.json', mimeType: 'json' } }); }, 1000); ================================================ FILE: docs/js/samples/data_xformat.js ================================================ var chart = c3.generate({ data: { x: 'date', xFormat : '%Y%m%d', // default '%Y-%m-%d' columns: [ ['date', '20130101', '20130102', '20130103', '20130104', '20130105', '20130106'], ['sample', 30, 200, 100, 400, 150, 250] ] }, axis : { x : { type : 'timeseries' } } }); ================================================ FILE: docs/js/samples/grid_x_lines.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] }, grid: { x: { lines: [ {value: 1, text: 'Label 1'}, {value: 3, text: 'Label 3', position: 'middle'}, {value: 4.5, text: 'Lable 4.5', position: 'start'} ] } } }); ================================================ FILE: docs/js/samples/grid_y_lines.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250], ['sample2', 1300, 1200, 1100, 1400, 1500, 1250], ], axes: { sample2: 'y2' } }, axis: { y2: { show: true } }, grid: { y: { lines: [ {value: 50, text: 'Label 50 for y'}, {value: 1300, text: 'Label 1300 for y2', axis: 'y2', position: 'start'}, {value: 350, text: 'Label 350 for y', position: 'middle'} ] } } }); ================================================ FILE: docs/js/samples/interaction_zoom.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250, 150, 200, 170, 240, 350, 150, 100, 400, 150, 250, 150, 200, 170, 240, 100, 150, 250, 150, 200, 170, 240, 30, 200, 100, 400, 150, 250, 150, 200, 170, 240, 350, 150, 100, 400, 350, 220, 250, 300, 270, 140, 150, 90, 150, 50, 120, 70, 40] ] }, zoom: { enabled: true } }); ================================================ FILE: docs/js/samples/interaction_zoom_by_drag.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250, 150, 200, 170, 240, 350, 150, 100, 400, 150, 250, 150, 200, 170, 240, 100, 150, 250, 150, 200, 170, 240, 30, 200, 100, 400, 150, 250, 150, 200, 170, 240, 350, 150, 100, 400, 350, 220, 250, 300, 270, 140, 150, 90, 150, 50, 120, 70, 40] ] }, zoom: { enabled: true, type: 'drag', } }); ================================================ FILE: docs/js/samples/legend_custom.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 100], ['data2', 300], ['data3', 200] ], type: 'pie' }, legend: { show: false } }); function toggle(id) { chart.toggle(id); } d3.select('.container').insert('div', '.chart').attr('class', 'legend').selectAll('span') .data(['data1', 'data2', 'data3']) .enter().append('span') .attr('data-id', function (id) { return id; }) .html(function (id) { return id; }) .each(function (id) { d3.select(this).style('background-color', chart.color(id)); }) .on('mouseover', function (id) { chart.focus(id); }) .on('mouseout', function (id) { chart.revert(); }) .on('click', function (id) { chart.toggle(id); }); ================================================ FILE: docs/js/samples/legend_position.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, legend: { position: 'right' } }); setTimeout(function () { chart.load({ columns: [ ['data3', 130, 150, 200, 300, 200, 100] ] }); }, 1000); setTimeout(function () { chart.unload({ ids: 'data1' }); }, 2000); setTimeout(function () { chart.transform('pie'); }, 3000); setTimeout(function () { chart.transform('line'); }, 4000); ================================================ FILE: docs/js/samples/options_color.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 130, 220, 140, 200, 250, 450], ['data4', 250, 320, 210, 240, 215, 225], ['data5', 430, 500, 400, 280, 290, 350], ['data6', 100, 120, 310, 340, 415, 225] ] }, color: { pattern: ['#1f77b4', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728', '#ff9896', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'] } }); ================================================ FILE: docs/js/samples/options_gridline.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250, 120, 200] ] }, grid: { x: { show: true }, y: { show: true } } }); ================================================ FILE: docs/js/samples/options_legend.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] }, legend: { show: false } }); ================================================ FILE: docs/js/samples/options_padding.js ================================================ var chart = c3.generate({ padding: { top: 40, right: 100, bottom: 40, left: 100, }, data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250000000000] ] } }); ================================================ FILE: docs/js/samples/options_size.js ================================================ var chart = c3.generate({ size: { height: 240, width: 480 }, data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] } }); ================================================ FILE: docs/js/samples/options_subchart.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] }, subchart: { show: true } }); ================================================ FILE: docs/js/samples/pie_label_format.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30], ['data2', 50] ], type: 'pie' }, pie: { label: { format: function (value, ratio, id) { return d3.format('$')(value); } } } }); ================================================ FILE: docs/js/samples/point_show.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, point: { show: false } }); ================================================ FILE: docs/js/samples/region.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250, 400], ['data2', 830, 1200, 1100, 1400, 1150, 1250, 1500], ], axes: { data2: 'y2' } }, axis: { y2: { show: true } }, regions: [ {axis: 'x', end: 1, class: 'regionX'}, {axis: 'x', start: 2, end: 4, class: 'regionX'}, {axis: 'x', start: 5, class: 'regionX'}, {axis: 'y', end: 50, class: 'regionY'}, {axis: 'y', start: 80, end: 140, class: 'regionY'}, {axis: 'y', start: 400, class: 'regionY'}, {axis: 'y2', end: 900, class: 'regionY2'}, {axis: 'y2', start: 1150, end: 1250, class: 'regionY2'}, {axis: 'y2', start: 1300, class: 'regionY2'}, ] }); ================================================ FILE: docs/js/samples/region_timeseries.js ================================================ var chart = c3.generate({ data: { x: 'date', columns: [ ['date', '2014-01-01', '2014-01-10', '2014-01-20', '2014-01-30', '2014-02-01'], ['sample', 30, 200, 100, 400, 150, 250] ] }, axis: { x: { type: 'timeseries' } }, regions: [ {start: '2014-01-05', end: '2014-01-10'}, {start: new Date('2014/01/15'), end: new Date('20 Jan 2014')}, {start: 1390575600000, end: 1391007600000} // start => 2014-01-25 00:00:00, end => 2014-01-30 00:00:00 ] }); ================================================ FILE: docs/js/samples/simple.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] } }); ================================================ FILE: docs/js/samples/simple_multiple.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } }); setTimeout(function () { chart.load({ columns: [ ['data1', 230, 190, 300, 500, 300, 400] ] }); }, 1000); setTimeout(function () { chart.load({ columns: [ ['data3', 130, 150, 200, 300, 200, 100] ] }); }, 1500); setTimeout(function () { chart.unload({ ids: 'data1' }); }, 2000); ================================================ FILE: docs/js/samples/simple_regions.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], regions: { 'data1': [{'start':1, 'end':2, 'style':'dashed'},{'start':3}], // currently 'dashed' style only 'data2': [{'end':3}] } } }); ================================================ FILE: docs/js/samples/simple_xy.js ================================================ var chart = c3.generate({ data: { x: 'x', columns: [ ['x', 30, 50, 100, 230, 300, 310], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 300, 200, 300, 250, 450] ] } }); setTimeout(function () { chart.load({ columns: [ ['data1', 100, 250, 150, 200, 100, 350] ] }); }, 1000); setTimeout(function () { chart.load({ columns: [ ['data3', 80, 150, 100, 180, 80, 150] ] }); }, 1500); setTimeout(function () { chart.unload({ ids: 'data2' }); }, 2000); ================================================ FILE: docs/js/samples/simple_xy_multiple.js ================================================ var chart = c3.generate({ data: { xs: { 'data1': 'x1', 'data2': 'x2', }, columns: [ ['x1', 10, 30, 45, 50, 70, 100], ['x2', 30, 50, 75, 100, 120], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ] } }); ================================================ FILE: docs/js/samples/style_grid.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 100, 200, 1000, 900, 500] ] }, grid: { x: { lines: [{value: 2}, {value: 4, class: 'grid4', text: 'LABEL 4'}] }, y: { lines: [{value: 500}, {value: 800, class: 'grid800', text: 'LABEL 800'}] } } }); ================================================ FILE: docs/js/samples/style_region.js ================================================ var chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] }, regions: [ {start:0, end:1}, {start:2, end:4, class:'foo'} ] }); ================================================ FILE: docs/js/samples/timeseries.js ================================================ var chart = c3.generate({ data: { x: 'x', // xFormat: '%Y%m%d', // 'xFormat' can be used as custom format of 'x' columns: [ ['x', '2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', '2013-01-05', '2013-01-06'], // ['x', '20130101', '20130102', '20130103', '20130104', '20130105', '20130106'], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 340, 200, 500, 250, 350] ] }, axis: { x: { type: 'timeseries', tick: { format: '%Y-%m-%d' } } } }); setTimeout(function () { chart.load({ columns: [ ['data3', 400, 500, 450, 700, 600, 500] ] }); }, 1000); ================================================ FILE: docs/js/samples/tooltip_format.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30000, 20000, 10000, 40000, 15000, 250000], ['data2', 100, 200, 100, 40, 150, 250] ], axes: { data2: 'y2' } }, axis : { y : { tick: { format: d3.format("s") } }, y2: { show: true, tick: { format: d3.format("$") } } }, tooltip: { format: { title: function (d) { return 'Data ' + d; }, value: function (value, ratio, id) { var format = id === 'data1' ? d3.format(',') : d3.format('$'); return format(value); } // value: d3.format(',') // apply this format to both y and y2 } } }); ================================================ FILE: docs/js/samples/tooltip_grouped.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 500, 320, 210, 340, 215, 125] ] }, tooltip: { grouped: false // Default true } }); ================================================ FILE: docs/js/samples/tooltip_horizontal.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, tooltip: { horizontal: true } }); ================================================ FILE: docs/js/samples/tooltip_show.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, tooltip: { show: false } }); ================================================ FILE: docs/js/samples/transform_area.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'bar' } }); setTimeout(function () { chart.transform('area', 'data1'); }, 1000); setTimeout(function () { chart.transform('area', 'data2'); }, 2000); setTimeout(function () { chart.transform('bar'); }, 3000); setTimeout(function () { chart.transform('area'); }, 4000); ================================================ FILE: docs/js/samples/transform_areaspline.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'bar' } }); setTimeout(function () { chart.transform('area-spline', 'data1'); }, 1000); setTimeout(function () { chart.transform('area-spline', 'data2'); }, 2000); setTimeout(function () { chart.transform('bar'); }, 3000); setTimeout(function () { chart.transform('area-spline'); }, 4000); ================================================ FILE: docs/js/samples/transform_bar.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'line' } }); setTimeout(function () { chart.transform('bar', 'data1'); }, 1000); setTimeout(function () { chart.transform('bar', 'data2'); }, 2000); setTimeout(function () { chart.transform('line'); }, 3000); setTimeout(function () { chart.transform('bar'); }, 4000); ================================================ FILE: docs/js/samples/transform_donut.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ] } }); setTimeout(function () { chart.transform('donut'); }, 1000); setTimeout(function () { chart.transform('line'); }, 2000); setTimeout(function () { chart.transform('pie'); }, 3000); setTimeout(function () { chart.transform('donut'); }, 4000); ================================================ FILE: docs/js/samples/transform_line.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'bar' } }); setTimeout(function () { chart.transform('line', 'data1'); }, 1000); setTimeout(function () { chart.transform('line', 'data2'); }, 2000); setTimeout(function () { chart.transform('bar'); }, 3000); setTimeout(function () { chart.transform('line'); }, 4000); ================================================ FILE: docs/js/samples/transform_pie.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ] } }); setTimeout(function () { chart.transform('pie'); }, 1000); setTimeout(function () { chart.transform('line'); }, 2000); setTimeout(function () { chart.transform('pie'); }, 3000); ================================================ FILE: docs/js/samples/transform_scatter.js ================================================ var chart = c3.generate({ data: { xs: { setosa: 'setosa_x', versicolor: 'versicolor_x', }, // iris data from R columns: [ ["setosa_x", 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3.0, 3.0, 4.0, 4.4, 3.9, 3.5, 3.8, 3.8, 3.4, 3.7, 3.6, 3.3, 3.4, 3.0, 3.4, 3.5, 3.4, 3.2, 3.1, 3.4, 4.1, 4.2, 3.1, 3.2, 3.5, 3.6, 3.0, 3.4, 3.5, 2.3, 3.2, 3.5, 3.8, 3.0, 3.8, 3.2, 3.7, 3.3], ["versicolor_x", 3.2, 3.2, 3.1, 2.3, 2.8, 2.8, 3.3, 2.4, 2.9, 2.7, 2.0, 3.0, 2.2, 2.9, 2.9, 3.1, 3.0, 2.7, 2.2, 2.5, 3.2, 2.8, 2.5, 2.8, 2.9, 3.0, 2.8, 3.0, 2.9, 2.6, 2.4, 2.4, 2.7, 2.7, 3.0, 3.4, 3.1, 2.3, 3.0, 2.5, 2.6, 3.0, 2.6, 2.3, 2.7, 3.0, 2.9, 2.9, 2.5, 2.8], ["setosa", 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0.1, 0.1, 0.2, 0.4, 0.4, 0.3, 0.3, 0.3, 0.2, 0.4, 0.2, 0.5, 0.2, 0.2, 0.4, 0.2, 0.2, 0.2, 0.2, 0.4, 0.1, 0.2, 0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.3, 0.3, 0.2, 0.6, 0.4, 0.3, 0.2, 0.2, 0.2, 0.2], ["versicolor", 1.4, 1.5, 1.5, 1.3, 1.5, 1.3, 1.6, 1.0, 1.3, 1.4, 1.0, 1.5, 1.0, 1.4, 1.3, 1.4, 1.5, 1.0, 1.5, 1.1, 1.8, 1.3, 1.5, 1.2, 1.3, 1.4, 1.4, 1.7, 1.5, 1.0, 1.1, 1.0, 1.2, 1.6, 1.5, 1.6, 1.5, 1.3, 1.3, 1.3, 1.2, 1.4, 1.2, 1.0, 1.3, 1.2, 1.3, 1.3, 1.1, 1.3], ], type: 'pie' }, axis: { x: { label: 'Sepal.Width', tick: { fit: false } }, y: { label: 'Petal.Width' } } }); setTimeout(function () { chart.transform('scatter'); }, 1000); setTimeout(function () { chart.transform('pie'); }, 2000); setTimeout(function () { chart.transform('scatter'); }, 3000) ================================================ FILE: docs/js/samples/transform_spline.js ================================================ var chart = c3.generate({ data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 140, 200, 150, 50] ], type: 'bar' } }); setTimeout(function () { chart.transform('spline', 'data1'); }, 1000); setTimeout(function () { chart.transform('spline', 'data2'); }, 2000); setTimeout(function () { chart.transform('bar'); }, 3000); setTimeout(function () { chart.transform('spline'); }, 4000); ================================================ FILE: docs/js/samples/transition_duration.js ================================================ var chart = c3.generate({ data: { url: '/data/c3_test.csv' }, transition: { duration: 100 } }); setTimeout(function () { chart.load({ url: '/data/c3_test2.csv' }); }, 500); setTimeout(function () { chart.load({ columns: [ ['data1', 30, 20, 50, 40, 60, 50], ['data2', 200, 130, 90, 240, 130, 220], ['data3', 300, 200, 160, 400, 250, 250] ] }); }, 1000); setTimeout(function () { chart.load({ rows: [ ['data1', 'data2', 'data3'], [90, 120, 300], [40, 160, 240], [50, 200, 290], [120, 160, 230], [80, 130, 300], [90, 220, 320], ] }); }, 1500); setTimeout(function () { chart.load({ columns:[ ['data1', null, 30, 20, 50, 40, 60, 50, 100, 200] ] }); }, 2000); ================================================ FILE: docs/layouts/layout.haml ================================================ !!! %html.no-js %head %meta( charset="utf-8" ) %meta( http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" ) %meta( name="description" content="D3 based reusable chart library" ) %meta( name="author" content="c3js.org" ) %meta( name="viewport" content="width=device-width" ) %link( rel="icon" href="/img/favicon.png" ) %title C3.js | D3-based reusable chart library -# Place favicon.ico and apple-touch-icon.png in the root directory -#= stylesheet_link_tag 'bootstrap.min' = stylesheet_link_tag 'normalize' = stylesheet_link_tag 'foundation.min' = stylesheet_link_tag 'tomorrow' = stylesheet_link_tag 'c3' = stylesheet_link_tag 'style' = stylesheet_link_tag get_css_name(current_page.path) :javascript (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-42896059-3', 'c3js.org'); ga('send', 'pageview'); = javascript_include_tag 'vendor/modernizr-2.6.1.min.js' %body.antialiased .sticky %nav.top-bar( data-topbar data-options="sticky_on: large" ) %ul.title-area %li.name %h1 %a( href="/" ) C3.js | D3-based reusable chart library %li.toggle-topbar.menu-icon %a( href="#" ) %span Menu %section.top-bar-section %ul.right %li.has-form %a.button( href="/gettingstarted.html" ) Getting Started %li.divider %li %a( href="/examples.html") Examples %li.divider %li %a( href="/reference.html") Reference %li.divider %li %a( href="https://groups.google.com/forum/#!forum/c3js" target="_blank") Forum %li.divider %li %a( href="https://github.com/c3js/c3" target="_blank") Source = yield ================================================ FILE: docs/reference.html.haml ================================================ .container.sidemenu .row .large-3.medium-4.columns.column-menu .side-bar %ul.side-nav %li %strong OPTIONS %li Chart = partial :reference_menu_item, locals: { id: 'bindto' } = partial :reference_menu_item, locals: { id: 'size.width' } = partial :reference_menu_item, locals: { id: 'size.height' } = partial :reference_menu_item, locals: { id: 'padding.top' } = partial :reference_menu_item, locals: { id: 'padding.right' } = partial :reference_menu_item, locals: { id: 'padding.bottom' } = partial :reference_menu_item, locals: { id: 'padding.left' } = partial :reference_menu_item, locals: { id: 'color.pattern' } = partial :reference_menu_item, locals: { id: 'color.threshold' } = partial :reference_menu_item, locals: { id: 'interaction.enabled' } = partial :reference_menu_item, locals: { id: 'transition.duration' } = partial :reference_menu_item, locals: { id: 'oninit' } = partial :reference_menu_item, locals: { id: 'onrendered' } = partial :reference_menu_item, locals: { id: 'onmouseover' } = partial :reference_menu_item, locals: { id: 'onmouseout' } = partial :reference_menu_item, locals: { id: 'onresize' } = partial :reference_menu_item, locals: { id: 'onresized' } %li Data = partial :reference_menu_item, locals: { id: 'data.url' } = partial :reference_menu_item, locals: { id: 'data.json' } = partial :reference_menu_item, locals: { id: 'data.rows' } = partial :reference_menu_item, locals: { id: 'data.columns' } = partial :reference_menu_item, locals: { id: 'data.mimeType' } = partial :reference_menu_item, locals: { id: 'data.keys' } = partial :reference_menu_item, locals: { id: 'data.x' } = partial :reference_menu_item, locals: { id: 'data.xs' } = partial :reference_menu_item, locals: { id: 'data.xFormat' } = partial :reference_menu_item, locals: { id: 'data.xLocaltime' } = partial :reference_menu_item, locals: { id: 'data.xSort' } -#= partial :reference_menu_item, locals: { id: 'data.idConverter' } = partial :reference_menu_item, locals: { id: 'data.names' } = partial :reference_menu_item, locals: { id: 'data.classes' } = partial :reference_menu_item, locals: { id: 'data.groups' } = partial :reference_menu_item, locals: { id: 'data.axes' } = partial :reference_menu_item, locals: { id: 'data.type' } = partial :reference_menu_item, locals: { id: 'data.types' } = partial :reference_menu_item, locals: { id: 'data.labels' } = partial :reference_menu_item, locals: { id: 'data.labels.format' } = partial :reference_menu_item, locals: { id: 'data.order' } = partial :reference_menu_item, locals: { id: 'data.regions' } = partial :reference_menu_item, locals: { id: 'data.color' } = partial :reference_menu_item, locals: { id: 'data.colors' } = partial :reference_menu_item, locals: { id: 'data.hide' } = partial :reference_menu_item, locals: { id: 'data.empty.label.text' } = partial :reference_menu_item, locals: { id: 'data.selection.enabled' } = partial :reference_menu_item, locals: { id: 'data.selection.grouped' } = partial :reference_menu_item, locals: { id: 'data.selection.multiple' } = partial :reference_menu_item, locals: { id: 'data.selection.draggable' } = partial :reference_menu_item, locals: { id: 'data.selection.isselectable' } = partial :reference_menu_item, locals: { id: 'data.stack.normalize' } = partial :reference_menu_item, locals: { id: 'data.onclick' } = partial :reference_menu_item, locals: { id: 'data.onmouseover' } = partial :reference_menu_item, locals: { id: 'data.onmouseout' } = partial :reference_menu_item, locals: { id: 'data.onselected' } = partial :reference_menu_item, locals: { id: 'data.onunselected' } = partial :reference_menu_item, locals: { id: 'data.ondragstart' } = partial :reference_menu_item, locals: { id: 'data.ondragend' } %li Axis = partial :reference_menu_item, locals: { id: 'axis.rotated' } = partial :reference_menu_item, locals: { id: 'axis.x.show' } = partial :reference_menu_item, locals: { id: 'axis.x.type' } = partial :reference_menu_item, locals: { id: 'axis.x.localtime' } = partial :reference_menu_item, locals: { id: 'axis.x.categories' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.centered' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.format' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.culling' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.culling.max' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.count' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.fit' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.values' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.rotate' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.outer' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.multiline' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.multilineMax' } = partial :reference_menu_item, locals: { id: 'axis.x.tick.width' } = partial :reference_menu_item, locals: { id: 'axis.x.max' } = partial :reference_menu_item, locals: { id: 'axis.x.min' } = partial :reference_menu_item, locals: { id: 'axis.x.padding' } = partial :reference_menu_item, locals: { id: 'axis.x.height' } = partial :reference_menu_item, locals: { id: 'axis.x.extent' } = partial :reference_menu_item, locals: { id: 'axis.x.label' } = partial :reference_menu_item, locals: { id: 'axis.y.show' } = partial :reference_menu_item, locals: { id: 'axis.y.inner' } = partial :reference_menu_item, locals: { id: 'axis.y.type' } = partial :reference_menu_item, locals: { id: 'axis.y.max' } = partial :reference_menu_item, locals: { id: 'axis.y.min' } = partial :reference_menu_item, locals: { id: 'axis.y.inverted' } = partial :reference_menu_item, locals: { id: 'axis.y.center' } = partial :reference_menu_item, locals: { id: 'axis.y.label' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.format' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.outer' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.values' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.count' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.time.value' } = partial :reference_menu_item, locals: { id: 'axis.y.tick.time.interval' } = partial :reference_menu_item, locals: { id: 'axis.y.padding' } = partial :reference_menu_item, locals: { id: 'axis.y.default' } = partial :reference_menu_item, locals: { id: 'axis.y2.show' } = partial :reference_menu_item, locals: { id: 'axis.y2.inner' } = partial :reference_menu_item, locals: { id: 'axis.y2.type' } = partial :reference_menu_item, locals: { id: 'axis.y2.max' } = partial :reference_menu_item, locals: { id: 'axis.y2.min' } = partial :reference_menu_item, locals: { id: 'axis.y2.inverted' } = partial :reference_menu_item, locals: { id: 'axis.y2.center' } = partial :reference_menu_item, locals: { id: 'axis.y2.label' } = partial :reference_menu_item, locals: { id: 'axis.y2.tick.format' } = partial :reference_menu_item, locals: { id: 'axis.y2.tick.outer' } = partial :reference_menu_item, locals: { id: 'axis.y2.tick.values' } = partial :reference_menu_item, locals: { id: 'axis.y2.tick.count' } = partial :reference_menu_item, locals: { id: 'axis.y2.padding' } = partial :reference_menu_item, locals: { id: 'axis.y2.default' } %li Grid = partial :reference_menu_item, locals: { id: 'grid.x.show' } = partial :reference_menu_item, locals: { id: 'grid.x.lines' } = partial :reference_menu_item, locals: { id: 'grid.y.show' } = partial :reference_menu_item, locals: { id: 'grid.y.lines' } = partial :reference_menu_item, locals: { id: 'grid.y.ticks' } %li Region = partial :reference_menu_item, locals: { id: 'regions' } %li Legend = partial :reference_menu_item, locals: { id: 'legend.show' } = partial :reference_menu_item, locals: { id: 'legend.hide' } = partial :reference_menu_item, locals: { id: 'legend.position' } = partial :reference_menu_item, locals: { id: 'legend.inset' } = partial :reference_menu_item, locals: { id: 'legend.item.onclick' } = partial :reference_menu_item, locals: { id: 'legend.item.onmouseover' } = partial :reference_menu_item, locals: { id: 'legend.item.onmouseout' } %li Tooltip = partial :reference_menu_item, locals: { id: 'tooltip.show' } = partial :reference_menu_item, locals: { id: 'tooltip.grouped' } = partial :reference_menu_item, locals: { id: 'tooltip.format.title' } = partial :reference_menu_item, locals: { id: 'tooltip.format.name' } = partial :reference_menu_item, locals: { id: 'tooltip.format.value' } = partial :reference_menu_item, locals: { id: 'tooltip.position' } = partial :reference_menu_item, locals: { id: 'tooltip.contents' } = partial :reference_menu_item, locals: { id: 'tooltip.horizontal' } %li Subchart = partial :reference_menu_item, locals: { id: 'subchart.show', experimental: true } = partial :reference_menu_item, locals: { id: 'subchart.size.height', experimental: true } = partial :reference_menu_item, locals: { id: 'subchart.onbrush', experimental: true } = partial :reference_menu_item, locals: { id: 'subchart.axis.x.show', experimental: true } %li Zoom = partial :reference_menu_item, locals: { id: 'zoom.enabled', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.type', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.rescale', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.extent', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.onzoom', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.onzoomstart', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.onzoomend', experimental: true } = partial :reference_menu_item, locals: { id: 'zoom.disableDefaultBehavior', experimental: true } %li Point = partial :reference_menu_item, locals: { id: 'point.show' } = partial :reference_menu_item, locals: { id: 'point.r' } = partial :reference_menu_item, locals: { id: 'point.focus.expand.enabled' } = partial :reference_menu_item, locals: { id: 'point.focus.expand.r' } = partial :reference_menu_item, locals: { id: 'point.select.r' } %li Line = partial :reference_menu_item, locals: { id: 'line.connectNull' } = partial :reference_menu_item, locals: { id: 'line.step.type' } %li Area = partial :reference_menu_item, locals: { id: 'area.zerobased' } %li Bar = partial :reference_menu_item, locals: { id: 'bar.width' } = partial :reference_menu_item, locals: { id: 'bar.width.ratio' } = partial :reference_menu_item, locals: { id: 'bar.zerobased' } %li Pie = partial :reference_menu_item, locals: { id: 'pie.label.show' } = partial :reference_menu_item, locals: { id: 'pie.label.format' } = partial :reference_menu_item, locals: { id: 'pie.label.threshold' } = partial :reference_menu_item, locals: { id: 'pie.expand' } = partial :reference_menu_item, locals: { id: 'pie.padAngle' } %li Donut = partial :reference_menu_item, locals: { id: 'donut.label.show' } = partial :reference_menu_item, locals: { id: 'donut.label.format' } = partial :reference_menu_item, locals: { id: 'donut.label.threshold' } = partial :reference_menu_item, locals: { id: 'donut.expand' } = partial :reference_menu_item, locals: { id: 'donut.padAngle' } = partial :reference_menu_item, locals: { id: 'donut.width' } = partial :reference_menu_item, locals: { id: 'donut.title' } %li Gauge = partial :reference_menu_item, locals: { id: 'gauge.label.show' } = partial :reference_menu_item, locals: { id: 'gauge.label.format' } = partial :reference_menu_item, locals: { id: 'gauge.expand' } = partial :reference_menu_item, locals: { id: 'gauge.min' } = partial :reference_menu_item, locals: { id: 'gauge.max' } = partial :reference_menu_item, locals: { id: 'gauge.units' } = partial :reference_menu_item, locals: { id: 'gauge.width' } %li Spline = partial :reference_menu_item, locals: { id: 'spline.interpolation.type' } %li Stanford = partial :reference_menu_item, locals: { id: 'stanford.scaleMin' } = partial :reference_menu_item, locals: { id: 'stanford.scaleMax' } = partial :reference_menu_item, locals: { id: 'stanford.scaleWidth' } = partial :reference_menu_item, locals: { id: 'stanford.scaleFormat' } = partial :reference_menu_item, locals: { id: 'stanford.colors' } = partial :reference_menu_item, locals: { id: 'stanford.padding' } = partial :reference_menu_item, locals: { id: 'stanford.texts' } = partial :reference_menu_item, locals: { id: 'stanford.lines' } = partial :reference_menu_item, locals: { id: 'stanford.regions' } %li.margin-medium-top %strong API = partial :reference_menu_item, locals: { id: 'api.focus' } = partial :reference_menu_item, locals: { id: 'api.defocus' } = partial :reference_menu_item, locals: { id: 'api.revert' } = partial :reference_menu_item, locals: { id: 'api.show' } = partial :reference_menu_item, locals: { id: 'api.hide' } = partial :reference_menu_item, locals: { id: 'api.toggle' } = partial :reference_menu_item, locals: { id: 'api.load' } = partial :reference_menu_item, locals: { id: 'api.unload' } = partial :reference_menu_item, locals: { id: 'api.flow' } = partial :reference_menu_item, locals: { id: 'api.select' } = partial :reference_menu_item, locals: { id: 'api.unselect' } = partial :reference_menu_item, locals: { id: 'api.selected' } = partial :reference_menu_item, locals: { id: 'api.transform' } = partial :reference_menu_item, locals: { id: 'api.groups' } = partial :reference_menu_item, locals: { id: 'api.xgrids' } = partial :reference_menu_item, locals: { id: 'api.xgrids.add' } = partial :reference_menu_item, locals: { id: 'api.xgrids.remove' } = partial :reference_menu_item, locals: { id: 'api.ygrids' } = partial :reference_menu_item, locals: { id: 'api.ygrids.add' } = partial :reference_menu_item, locals: { id: 'api.ygrids.remove' } = partial :reference_menu_item, locals: { id: 'api.regions' } = partial :reference_menu_item, locals: { id: 'api.regions.add' } = partial :reference_menu_item, locals: { id: 'api.regions.remove' } = partial :reference_menu_item, locals: { id: 'api.data' } = partial :reference_menu_item, locals: { id: 'api.data.shown' } = partial :reference_menu_item, locals: { id: 'api.data.values' } = partial :reference_menu_item, locals: { id: 'api.data.names' } = partial :reference_menu_item, locals: { id: 'api.data.colors' } = partial :reference_menu_item, locals: { id: 'api.data.axes' } = partial :reference_menu_item, locals: { id: 'api.x' } = partial :reference_menu_item, locals: { id: 'api.xs' } = partial :reference_menu_item, locals: { id: 'api.axis.labels' } = partial :reference_menu_item, locals: { id: 'api.axis.min' } = partial :reference_menu_item, locals: { id: 'api.axis.max' } = partial :reference_menu_item, locals: { id: 'api.axis.range' } = partial :reference_menu_item, locals: { id: 'api.axis.types' } = partial :reference_menu_item, locals: { id: 'api.legend.show' } = partial :reference_menu_item, locals: { id: 'api.legend.hide' } = partial :reference_menu_item, locals: { id: 'api.subchart.isShown' } = partial :reference_menu_item, locals: { id: 'api.subchart.show' } = partial :reference_menu_item, locals: { id: 'api.subchart.hide' } = partial :reference_menu_item, locals: { id: 'api.zoom' } = partial :reference_menu_item, locals: { id: 'api.unzoom' } = partial :reference_menu_item, locals: { id: 'api.zoom.enable' } = partial :reference_menu_item, locals: { id: 'api.resize' } = partial :reference_menu_item, locals: { id: 'api.flush' } = partial :reference_menu_item, locals: { id: 'api.destroy' } %li.margin-medium-top %strong CLASS = partial :reference_menu_item, locals: { id: 'class.c3-chart' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-line' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-lines' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-bar' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-bars' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-text' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-texts' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arc' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs-title' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs-background' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs-gauge-unit' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs-gauge-max' } = partial :reference_menu_item, locals: { id: 'class.c3-chart-arcs-gauge-min' } = partial :reference_menu_item, locals: { id: 'class.c3-selected-circle' } = partial :reference_menu_item, locals: { id: 'class.c3-selected-circles' } = partial :reference_menu_item, locals: { id: 'class.c3-event-rect' } = partial :reference_menu_item, locals: { id: 'class.c3-event-rects' } = partial :reference_menu_item, locals: { id: 'class.c3-event-rects-single' } = partial :reference_menu_item, locals: { id: 'class.c3-event-rects-multiple' } = partial :reference_menu_item, locals: { id: 'class.c3-zoom-rect' } = partial :reference_menu_item, locals: { id: 'class.c3-brush' } = partial :reference_menu_item, locals: { id: 'class.c3-focused' } = partial :reference_menu_item, locals: { id: 'class.c3-region' } = partial :reference_menu_item, locals: { id: 'class.c3-regions' } = partial :reference_menu_item, locals: { id: 'class.c3-tooltip' } = partial :reference_menu_item, locals: { id: 'class.c3-tooltip-name' } = partial :reference_menu_item, locals: { id: 'class.c3-shape' } = partial :reference_menu_item, locals: { id: 'class.c3-shapes' } = partial :reference_menu_item, locals: { id: 'class.c3-line' } = partial :reference_menu_item, locals: { id: 'class.c3-lines' } = partial :reference_menu_item, locals: { id: 'class.c3-bar' } = partial :reference_menu_item, locals: { id: 'class.c3-bars' } = partial :reference_menu_item, locals: { id: 'class.c3-circle' } = partial :reference_menu_item, locals: { id: 'class.c3-circles' } = partial :reference_menu_item, locals: { id: 'class.c3-arc' } = partial :reference_menu_item, locals: { id: 'class.c3-arcs' } = partial :reference_menu_item, locals: { id: 'class.c3-area' } = partial :reference_menu_item, locals: { id: 'class.c3-areas' } = partial :reference_menu_item, locals: { id: 'class.c3-empty' } = partial :reference_menu_item, locals: { id: 'class.c3-text' } = partial :reference_menu_item, locals: { id: 'class.c3-texts' } = partial :reference_menu_item, locals: { id: 'class.c3-gauge-value' } = partial :reference_menu_item, locals: { id: 'class.c3-grid' } = partial :reference_menu_item, locals: { id: 'class.c3-xgrid' } = partial :reference_menu_item, locals: { id: 'class.c3-xgrids' } = partial :reference_menu_item, locals: { id: 'class.c3-xgrid-line' } = partial :reference_menu_item, locals: { id: 'class.c3-xgrid-lines' } = partial :reference_menu_item, locals: { id: 'class.c3-xgrid-focus' } = partial :reference_menu_item, locals: { id: 'class.c3-ygrid' } = partial :reference_menu_item, locals: { id: 'class.c3-ygrids' } = partial :reference_menu_item, locals: { id: 'class.c3-ygrid-line' } = partial :reference_menu_item, locals: { id: 'class.c3-ygrid-lines' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-elements' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-line' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-lines' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-region' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-regions' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-text' } = partial :reference_menu_item, locals: { id: 'class.c3-stanford-texts' } = partial :reference_menu_item, locals: { id: 'class.c3-axis' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-x' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-x-label' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-y' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-y-label' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-y2' } = partial :reference_menu_item, locals: { id: 'class.c3-axis-y2-label' } = partial :reference_menu_item, locals: { id: 'class.c3-legend-item' } = partial :reference_menu_item, locals: { id: 'class.c3-legend-item-event' } = partial :reference_menu_item, locals: { id: 'class.c3-legend-item-tile' } = partial :reference_menu_item, locals: { id: 'class.c3-legend-item-hidden' } = partial :reference_menu_item, locals: { id: 'class.c3-legend-item-focused' } .large-9.medium-8.columns.column-content %h2 Options %hr %section %h3 = partial :reference_item_link, locals: { id: 'bindto' } %p The CSS selector or the element which the chart will be set to. D3 selection object can be specified. If other chart is set already, it will be replaced with the new one (only one chart can be set in one element). %br %p If this option is not specified, the chart will be generated but not be set. Instead, we can access the element by chart.element and set it by ourselves. %h5 Note: %p When chart is not binded, c3 starts observing if chart.element is binded by MutationObserver. In this case, polyfill is required in IE9 and IE10 because they do not support MutationObserver. On the other hand, if chart always will be binded, polyfill will not be required because MutationObserver will never be called. %h5 Default: #chart %h5 Format: %div.sourcecode %pre %code.html.javascript bindto: '#myContainer' \// or element bindto: document.getElementById('myContainer') \// or D3 selection object bindto: d3.select('#myContainer') %hr %section %h3 = partial :reference_item_link, locals: { id: 'size.width' } %p The desired width of the chart element. %br %p If this option is not specified, the width of the chart will be calculated by the size of the parent element it's appended to. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript size: {   width: 640 } %h5 Note: %p This option should be specified if possible because it can improve its performance because some size calculations will be skipped by an explicit value. %hr %section %h3 = partial :reference_item_link, locals: { id: 'size.height' } %p The desired height of the chart element. %br %p If this option is not specified, the height of the chart will be calculated by the size of the parent element it's appended to. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript size: {   height: 480 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'padding.top' } %p The padding on the top of the chart. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript padding: {   top: 20 } %h5 Example: %ul %li %a( href="/samples/options_padding.html" ) Padding for the chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'padding.right' } %p The padding on the right of the chart. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript padding: {   right: 20 } %h5 Example: %ul %li %a( href="/samples/options_padding.html" ) Padding for the chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'padding.bottom' } %p The padding on the bottom of the chart. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript padding: {   bottom: 20 } %h5 Example: %ul %li %a( href="/samples/options_padding.html" ) Padding for the chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'padding.left' } %p The padding on the left of the chart. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript padding: {   left: 20 } %h5 Example: %ul %li %a( href="/samples/options_padding.html" ) Padding for the chart %h5 Note: %p This option should be specified if possible because it can improve its performance because some size calculations will be skipped by an explicit value. %hr %section %h3 = partial :reference_item_link, locals: { id: 'color.pattern' } %p Set custom color pattern. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript color: {   pattern: ['#1f77b4', '#aec7e8', ...] } %h5 Example: %ul %li %a( href="/samples/options_color.html" ) Custom color pattern %hr %section %h3 = partial :reference_item_link, locals: { id: 'color.threshold' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'interaction.enabled' } %p Indicate if the chart should have interactions. %br %p If false is set, all of interactions (showing/hiding tooltip, selection, mouse events, etc) will be disabled. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript interaction: {   enabled: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'transition.duration' } %p Set duration of transition (in milliseconds) for chart animation. %h5 Note: %p If 0 or null set, transition will be skipped. So, this makes initial rendering faster especially in case you have a lot of data. %h5 Default: 350 %h5 Format: %div.sourcecode %pre %code.html.javascript transition: {   duration: 500 } %h5 Example: %ul %li %a( href="/samples/transition_duration.html" ) Change the duration of transition %hr %section %h3 = partial :reference_item_link, locals: { id: 'oninit' } %p Set a callback to execute when the chart is initialized. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript oninit: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'onrendered' } %p Set a callback which is executed when the chart is rendered. Basically, this callback will be called in each time when the chart is redrawed. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript onrendered: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'onmouseover' } %p Set a callback to execute when mouse enters the chart. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript onmouseover: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'onmouseout' } %p Set a callback to execute when mouse leaves the chart. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript onmouseout: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'onresize' } %p Set a callback to execute when user resizes the screen. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript onresize: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'onresized' } %p Set a callback to execute when screen resize finished. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript onresized: function () { ... } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.url' } %p Load a CSV or JSON file from a URL. Note that this will not work if loading via the "file://" protocol as the most browsers will block XMLHTTPRequests. %h5 Format: %div.sourcecode %pre %code.html.javascript var chart = c3.generate({   data: {     url: '/data/c3_test.csv'   } }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.json' } %p Parse a JSON object for data. See also data.keys. %h5 Format: %div.sourcecode %pre %code.html.javascript var chart = c3.generate({   data: {     json: [       {name: 'www.site1.com', upload: 200, download: 200, total: 400},       {name: 'www.site2.com', upload: 100, download: 300, total: 400},       {name: 'www.site3.com', upload: 300, download: 200, total: 500},       {name: 'www.site4.com', upload: 400, download: 100, total: 500}     ],     keys: {       // x: 'name', // it's possible to specify 'x' when category axis       value: ['upload', 'download']     }   },   axis: {     x: {       // type: 'category'     }   } }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.rows' } %p Load data from a multidimensional array, with the first element containing the data names, the following containing related data in that order. %h5 Format: %div.sourcecode %pre %code.html.javascript rows: [   ['data1', 'data2', 'data3'],   [90, 120, 300],   [40, 160, 240],   [50, 200, 290],   [120, 160, 230],   [80, 130, 300],   [90, 220, 320] ] %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.columns' } %p Load data from a multidimensional array, with each element containing an array consisting of a datum name and associated data values. %h5 Format: %div.sourcecode %pre %code.html.javascript columns: [   ['data1', 30, 20, 50, 40, 60, 50],   ['data2', 200, 130, 90, 240, 130, 220],   ['data3', 300, 200, 160, 400, 250, 250] ] %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.mimeType' } %p Used if loading JSON via data.url: %div.sourcecode %pre %code.html.javascript {data: {mimeType: 'json'}} %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.keys' } %p Choose which JSON object keys correspond to desired data. %h5 Format: %div.sourcecode %pre %code.html.javascript var chart = c3.generate({   data: {     json: [       {name: 'www.site1.com', upload: 200, download: 200, total: 400},       {name: 'www.site2.com', upload: 100, download: 300, total: 400},       {name: 'www.site3.com', upload: 300, download: 200, total: 500},       {name: 'www.site4.com', upload: 400, download: 100, total: 500}     ],     keys: {       // x: 'name', // it's possible to specify 'x' when category axis       value: ['upload', 'download']     }   },   axis: {     x: {       // type: 'category'     }   } }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.x' } %p Specify the key of x values in the data. %br %p We can show the data with non-index x values by this option. This option is required when the type of x axis is timeseries. If this option is set on category axis, the values of the data on the key will be used for category names. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   x: 'date' } %h5 Example: %ul %li %a( href="/samples/simple_xy.html") XY Chart %li %a( href="/samples/timeseries.html" ) Timeseries Chart %li %a( href="/samples/data_stringx.html" ) Category Names %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.xs' } %p Specify the keys of the x values for each data. %br %p This option can be used if we want to show the data that has different x values. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   xs: {     data1: 'x1',     data2: 'x2'   } } %h5 Example: %ul %li %a( href="/samples/simple_xy_multiple.html" ) Multiple XY Chart %h5 Note: %p data.x should be used if the all of data have same x values. %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.xFormat' } %p Set a format to parse string specified as x. %h5 Default: %Y-%m-%d %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   xFormat: '%Y-%m-%d %H:%M:%S' } %h5 Example: %ul %li %a( href="/samples/timeseries.html" ) Timeseries Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.xLocaltime' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.xSort' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.names' } %p Set custom data name. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   names: {     data1: 'Data Name 1',     data2: 'Data Name 2'   } } %h5 Example: %ul %li %a( href="/samples/data_name.html" ) Data Name %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.classes' } %p Set custom data class. %br %p If this option is specified, the element g for the data has an additional class that has the prefix c3-target- (e.g. c3-target-additional-data1-class). %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   classes: {     data1: 'additional-data1-class',     data2: 'additional-data2-class'   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.groups' } %p Set groups for the data for stacking. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   groups: [     ['data1', 'data2'],     ['data3']   ] } %h5 Example: %ul %li %a( href="/samples/chart_bar_stacked.html" ) Stacked Bar Chart %li %a( href="/samples/chart_area_stacked.html" ) Stacked Area Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.axes' } %p Set y axis the data related to. y and y2 can be used. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   axes: {     data1: 'y',     data2: 'y2'   } } %h5 Example: %ul %li %a( href="/samples/axes_y2.html" ) Additional Y Axis %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.type' } %p Set chart type at once. %br %p If this option is specified, the type will be applied to every data. This setting can be overwritten by data.types. %h5 Available Values: %ul %li line %li spline %li step %li area %li area-spline %li area-step %li bar %li scatter %li stanford %li pie %li donut %li gauge %h5 Default: line %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   type: 'bar' } %h5 Example: %ul %li %a( href="/samples/chart_spline.html" ) Spline Chart %li %a( href="/samples/chart_step.html" ) Step Chart %li %a( href="/samples/chart_area.html" ) Area Chart %li %a( href="/samples/chart_bar.html" ) Bar Chart %li %a( href="/samples/chart_scatter.html" ) Scatter Chart %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %li %a( href="/samples/chart_pie.html" ) Pie Chart %li %a( href="/samples/chart_donut.html" ) Donut Chart %li %a( href="/samples/chart_gauge.html" ) Gauge Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.types' } %p Set chart type for each data. %br %p This setting overwrites data.type setting. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   types: {     data1: 'bar'     data2: 'spline'   } } %h5 Example: %ul %li %a( href="/samples/chart_combination.html" ) Combination Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.labels' } %p Show labels on each data points. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   labels: true } %h5 Example: %ul %li %a( href="/samples/data_label.html" ) Data Label %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.labels.format' } %p Set formatter function for data labels. %br %p The formatter function receives 4 arguments such as v, id, i, j and it must return a string that will be shown as the label. The arguments are: %br %ul %li v is the value of the data point where the label is shown. %li id is the id of the data where the label is shown. %li i is the index of the data point where the label is shown. %li j is the sub index of the data point where the label is shown. %p Formatter function can be defined for each data by specifying as an object and D3 formatter function can be set (e.g. d3.format('$')) %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   labels: {     format: function (v, id, i, j) { ... }     // it's possible to set for each data     //format: {     //    data1: function (v, id, i, j) { ... },     //    ...     //}   } } %h5 Example: %ul %li %a( href="/samples/data_label_format.html" ) Data Label Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.order' } %p Define the order of the data. %br %p This option changes the order of stacking the data and pieces of pie/donut. If null specified, it will be the order the data loaded. If function specified, it will be used to sort the data and it will receive the data as argument. %h5 Available Values: %ul %li desc %li asc %li function (data1, data2) { ... } %li null %h5 Default: desc %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   order: 'asc' } %h5 Example: %ul %li %a( href="/samples/data_order.html" ) Data Order %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.regions' } %p Define regions for each data. %br %p The values must be an array for each data and it should include an object that has start, end, style. If start is not set, the start will be the first data point. If end is not set, the end will be the last data point. %br %p Currently this option supports only line chart and dashed style. If this option specified, the line will be dashed only in the regions. %br %p An optional label property can be provided to display a label for the region. If a label option is not specified, no label will be displayed for the region. For each region, you may also specify the paddingY and paddingX options to control the position of label text. Finally, a vertical option can be used to identify whether or not the label text should be rotated 90 degrees. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   regions: {     data1: [       {'start':1, 'end':2, 'style':'dashed'},       {'start':3, label:"Region 2", paddingX:2, paddingY:2, vertical=true}     ],     ...   } } %h5 Example: %ul %li %a( href="/samples/simple_regions.html" ) Line Chart with Region %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.color' } %p Set color converter function. %br %p This option should a function and the specified function receives color (e.g. '#ff0000') and d that has data parameters like id, value, index, etc. And it must return a string that represents color (e.g. '#00ff00'). %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   color: function (color, d) { ... } } %h5 Example: %ul %li %a( href="/samples/data_color.html" ) Data Color %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.colors' } %p Set color for each data. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   colors: {     data1: '#ff0000',     ...   } } %h5 Example: %ul %li %a( href="/samples/data_color.html" ) Data Color %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.hide' } %p Hide each data when the chart appears. %br %p If true specified, all of data will be hidden. If multiple ids specified as an array, those will be hidden. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   // all of data will be hidden   hide: true   // specified data will be hidden   hide: ['data1', ...] } %h5 Note: %p This option does not hide legends, so we need to use legend.hide option together if we want to hide legend too. %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.empty.label.text' } %p Set text displayed when empty data. %h5 Default: "" %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   empty: {     label: {       text: "No Data"     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.selection.enabled' } %p Set data selection enabled. %br %p If this option is set true, we can select the data points and get/set its state of selection by API (e.g. select, unselect, selected). %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   selection: {     enabled: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.selection.grouped' } %p Set grouped selection enabled. %br %p If this option set true, multiple data points that have same x value will be selected by one selection. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   selection: {     grouped: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.selection.multiple' } %p Set multiple data points selection enabled. %br %p If this option set true, multiple data points can have the selected state at the same time. If false set, only one data point can have the selected state and the others will be unselected when the new data point is selected. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   selection: {     multiple: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.selection.draggable' } %p Enable to select data points by dragging. %br %p If this option set true, data points can be selected by dragging. %h5 Note: %p If this option set true, scrolling on the chart will be disabled because dragging event will handle the event. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   selection: {     draggable: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.selection.isselectable' } %p Set a callback for each data point to determine if it's selectable or not. %br %p The callback will receive d as an argument and it has some parameters like id, value, index. This callback should return boolean. %h5 Default: function () { return true; } %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   selection: {     isselectable: function (d) { ... }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.stack.normalize' } %p Set the stacking to be normalized %br %p For stacking, the `data.groups` option should be set and have positive values. The yAxis will be set in percentage value (0 ~ 100%). %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   stack: {     normalize: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.onclick' } %p Set a callback for click event on each data point. %br %p This callback will be called when each data point clicked and will receive d and element as the arguments. d is the data clicked and element is the element clicked. In this callback, this will be the Chart object. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   onclick: function (d, element) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.onmouseover' } %p Set a callback for mouseover event on each data point. %br %p This callback will be called when mouse cursor moves onto each data point and will receive d as the argument. d is the data where mouse cursor moves onto. In this callback, this will be the Chart object. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   onmouseover: function (d) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.onmouseout' } %p Set a callback for mouseout event on each data point. %br %p This callback will be called when mouse cursor moves out each data point and will receive d as the argument. d is the data where mouse cursor moves out. In this callback, this will be the Chart object. %h5 Default: function () {} %h5 Format: %div.sourcecode %pre %code.html.javascript data: {   onmouseout: function (d) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.onselected' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.onunselected' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.ondragstart' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'data.ondragend' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.rotated' } %p Switch x and y axis position. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   rotated: true } %h5 Example: %ul %li %a( href="/samples/axes_rotated.html" ) Rotated Axis %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.show' } %p Show or hide x axis. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     show: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.type' } %p Set type of x axis. %h5 Available Values %ul %li timeseries %li category %li indexed %h5 Default: indexed %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     type: 'timeseries'   } } %h5 Example: %ul %li %a( href="/samples/timeseries.html" ) Timeseries Chart %li %a( href="/samples/categorized.html" ) Category Axis %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.localtime' } %p Set how to treat the timezone of x values. %br %p If true, treat x value as localtime. If false, convert to UTC internally. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     localtime: true   } } %h5 Example: %ul %li %a( href="/samples/axes_x_localtime.html" ) X Axis Timezone %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.categories' } %p Set category names on category axis. %br %p This must be an array that includes category names in string. If category names are included in the date by data.x option, this is not required. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     categories: ['Category 1', 'Category 2', ...]   } } %h5 Example: %ul %li %a( href="/samples/categorized.html" ) Category Axis %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.centered' } %p Centerise ticks on category axis. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       centered: true     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.format' } %p A function to format tick value. Format string is also available for timeseries data. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       format: function (x) { return x.getFullYear(); }     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.culling' } %p Setting for culling ticks. %br %p If true is set, the ticks will be culled, then only limitted tick text will be shown. This option does not hide the tick lines. If false is set, all of ticks will be shown. %br %p We can change the number of ticks to be shown by axis.x.tick.culling.max. %h5 Default: %ul %li true for indexed axis and timeseries axis %li false for category axis %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       culling: false     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_culling.html" ) X Axis Tick Culling %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.culling.max' } %p The number of tick texts will be adjusted to less than this value. %h5 Default: 10 %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       culling: {         max: 5       }     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_culling.html" ) X Axis Tick Culling %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.count' } %p The number of x axis ticks to show. %br %p This option hides tick lines together with tick text. If this option is used on timeseries axis, the ticks position will be determined precisely and not nicely positioned (e.g. it will have rough second value). %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       count: 5     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_count.html" ) X Axis Tick Count %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.fit' } %p Fit x axis ticks. %br %p If true set, the ticks will be positioned nicely. If false set, the ticks will be positioned according to x value of the data points. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       fit: true     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_fit.html" ) X Axis Tick Fit %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.values' } %p Set the x values of ticks manually. %br %p If this option is provided, the position of the ticks will be determined based on those values. This option works with timeseries data and the x values will be parsed accoding to the type of the value and data.xFormat option. %h5 Default: null %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       values: [1, 2, 4, 8, 16, 32, ...]     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_values.html" ) X Axis Tick Values %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.rotate' } %p Rotate x axis tick text. %br %p If you set negative value, it will rotate to opposite direction. %h5 Default: 0 %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       rotate: 60     }   } } %h5 Example: %ul %li %a( href="/samples/axes_x_tick_rotate.html" ) Rotate X Axis Tick Text %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.outer' } %p Show x axis outer tick. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {       outer: false     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.multiline' } %p Enable multiline. %br %p If this option is set true, when a tick's text on the x-axis is too long, it splits the text into multiple lines in order to avoid text overlapping. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {      multiline: true    }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.multilineMax' } %p If this option is set and is above 0, the number of lines will be adjusted to less than this value and tick's text is ellipsified. %br %h5 Default: 0 %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     tick: {      multiline: true,      multilineMax: 2,    }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.tick.width' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.max' } %p Set max value of x axis range. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     max: 100   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.min' } %p Set min value of x axis range. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     min: -100   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.padding' } %p Set padding for x axis. %br %p If this option is set, the range of the x axis will increase/decrease by the values. If no padding is needed for the x axis, set the values to 0. This option is ignored when the axis type is category. %h5 Default: {} %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     padding: {       left: 0,       right: 0     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.height' } %p Set height of x axis. %br %p The height of x axis can be set manually by this option. If you need more space for x axis, please use this option for that. The unit is pixel. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     height: 20   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.extent' } %p Set default extent for subchart and zoom. This can be an array or function that returns an array. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     extent: [5, 10]   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.x.label' } %p Set label on x axis. %br %p You can set x axis label and change its position by this option. string and object can be passed and we can change the position by passing object that has position key. Available position differs according to the axis direction (vertical or horizontal). If string set, the position will be the default. %br %p If it's horizontal axis: %ul %li inner-right [default] %li inner-center %li inner-left %li outer-right %li outer-center %li outer-left %p If it's vertical axis: %ul %li inner-top [default] %li inner-middle %li inner-bottom %li outer-top %li outer-middle %li outer-bottom %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   x: {     label: 'Your X Axis'   } } %div.sourcecode %pre %code.html.javascript axis: {   x: {     label: {       text: 'Your X Axis',       position: 'outer-center'     }   } } %h5 Example: %ul %li %a( href="/samples/axes_label.html" ) Axis Label %li %a( href="/samples/axes_label_position.html" ) Axis Label Position %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.show' } %p Show or hide y axis. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     show: false   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.inner' } %p Show y axis inside of the chart. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     inner: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.type' } %p Scale type for Y axis. %h5 Available Values: %ul %li linear %li time¹ %li timeseries¹ %li log² Experimental %p ¹: The timeseries scale is an alias of time. %p ²: The log scale is experimental and may not work in all cases (stacked, etc.) %h5 Default: linear %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     type: 'linear'   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.max' } %p Set max value of y axis. %h5 Note: %p Padding will be added based on this value, so if you don't need the padding, please set axis.y.padding to disable it (e.g. axis.y.padding = 0). %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     max: 1000   } } %h5 Example: %ul %li %a( href="/samples/axes_y_range.html" ) Y Axis Range %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.min' } %p Set min value of y axis. %h5 Note: %p Padding will be added based on this value, so if you don't need the padding, please set axis.y.padding to disable it (e.g. axis.y.padding = 0). %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     min: 1000   } } %h5 Example: %ul %li %a( href="/samples/axes_y_range.html" ) Y Axis Range %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.inverted' } %p Change the direction of y axis. %br %p If true set, the direction will be from the top to the bottom. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     inverted: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.center' } %p Set center value of y axis. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     center: 0   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.label' } %p Set label on y axis. %br %p You can set y axis label and change its position by this option. This option works in the same way as axis.x.label. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     label: 'Your Y Axis'   } } %div.sourcecode %pre %code.html.javascript axis: {   y: {     label: {       text: 'Your Y Axis',       position: 'outer-middle'     }   } } %h5 Example: %ul %li %a( href="/samples/axes_label.html" ) Axis Label %li %a( href="/samples/axes_label_position.html" ) Axis Label Position %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.format' } %p Set formatter for y axis tick text. %br %p This option accepts d3.format object as well as a function you define. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     tick: {       format: d3.format('$,')       //or format: function (d) { return '$' + d; }     }   } } %h5 Example: %ul %li %a( href="/samples/axes_y_tick_format.html" ) Y Axis Tick Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.outer' } %p Show or hide outer tick. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     tick: {       outer: false     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.values' } %p Set y axis tick values manually. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     tick: {       values: [100, 1000, 10000]     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.count' } %p Set the number of y axis ticks. %h5 Note: %p The position of the ticks will be calculated precisely, so the values on the ticks will not be rounded nicely. In the case, axis.y.tick.format or axis.y.tick.values will be helpful. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     tick: {       count: 5     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.time.value' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.tick.time.interval' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.padding' } %p Set padding for y axis. %br %p You can set padding for y axis to create more space on the edge of the axis. This option accepts object and it can include top and bottom. top, bottom will be treated as pixels. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     padding: {       top: 100,       bottom: 100     }   } } %h5 Example: %ul %li %a( href="/samples/axes_y_padding.html" ) Y Axis Tick Padding %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y.default' } %p Set default range of y axis. %br %p This option set the default value for y axis when there is no data on init. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y: {     default: [0, 1000]   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.show' } %p Show or hide y2 axis. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     show: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.inner' } %p Show y2 axis inside of the chart. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     inner: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.type' } %p Scale type for Y2 axis. %h5 Available Values: %ul %li linear %li time¹ %li timeseries¹ %li log² Experimental %p ¹: The timeseries scale is an alias of time. %p ²: The log scale is experimental and may not work in all cases (stacked, etc.) %h5 Default: linear %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     type: 'linear'   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.max' } %p Set max value of y2 axis. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     max: 1000   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.min' } %p Set min value of y2 axis. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     min: -1000   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.inverted' } %p Change the direction of y2 axis. %br %p If true set, the direction will be from the top to the bottom. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     inverted: true   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.center' } %p Set center value of y2 axis. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     center: 0   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.label' } %p Set label on y2 axis. %br %p You can set y2 axis label and change its position by this option. This option works in the same way as axis.x.label. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     label: 'Your Y2 Axis'   } } %div.sourcecode %pre %code.html.javascript axis: {   y2: {     label: {       text: 'Your Y2 Axis',       position: 'outer-middle'     }   } } %h5 Example: %ul %li %a( href="/samples/axes_label.html" ) Axis Label %li %a( href="/samples/axes_label_position.html" ) Axis Label Position %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.tick.format' } %p Set formatter for y axis tick text. %br %p This option works in the same way as axis.y.format. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     tick: {       format: d3.format('$,')       //or format: function (d) { return '$' + d; }     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.tick.outer' } %p Show or hide y2 axis outer tick. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     tick: {       outer: false     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.tick.values' } %p Set y2 axis tick values manually. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     tick: {       values: [100, 1000, 10000]     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.tick.count' } %p Set the number of y2 axis ticks. %h5 Note: %p This works in the same way as axis.y.tick.count. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     tick: {       count: 5     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.padding' } %p Set padding for y2 axis. %br %p This works in the same way as axis.y.padding. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     padding: {       top: 100,       bottom: 100     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'axis.y2.default' } %p Set default range of y2 axis. %br %p This option set the default value for y2 axis when there is no data on init. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript axis: {   y2: {     default: [0, 1000]   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'grid.x.show' } %p Show grids along x axis. %br %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript grid: {   x: {     show: true   } } %h5 Example: %ul %li %a( href="/samples/options_gridline.html" ) Grid Lines %hr %section %h3 = partial :reference_item_link, locals: { id: 'grid.x.lines' } %p Show additional grid lines along x axis. %br %p This option accepts array including object that has value, text, position and class. text, position and class are optional. For position, start, middle and end (default) are available. %p If x axis is category axis, value can be category name. If x axis is timeseries axis, value can be date string, Date object and unixtime integer. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript grid: {   x: {     lines: [       {value: 2, text: 'Label on 2'},       {value: 5, text: 'Label on 5', class: 'label-5'},       {value: 6, text: 'Label on 6', position: 'start'}     ]   } } %h5 Example: %ul %li %a( href="/samples/grid_x_lines.html" ) Additional X Grid Lines %hr %section %h3 = partial :reference_item_link, locals: { id: 'grid.y.show' } %p Show grids along y axis. %br %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript grid: {   y: {     show: true   } } %h5 Example: %ul %li %a( href="/samples/options_gridline.html" ) Grid Lines %hr %section %h3 = partial :reference_item_link, locals: { id: 'grid.y.lines' } %p Show additional grid lines along y axis. %br %p This option accepts array including object that has value, text, position and class. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript grid: {   y: {     lines: [       {value: 100, text: 'Label on 100'},       {value: 200, text: 'Label on 200', class: 'label-200'},       {value: 300, text: 'Label on 300', position: 'middle'}     ]   } } %h5 Example: %ul %li %a( href="/samples/grid_y_lines.html" ) Additional Y Grid Lines %hr %section %h3 = partial :reference_item_link, locals: { id: 'grid.y.ticks' } %p not yet %hr %section %h3 = partial :reference_item_link, locals: { id: 'regions' } %p Show rectangles inside the chart. %br %p This option accepts array including object that has axis, start, end and class. The keys start, end and class are optional. %p axis must be x, y or y2. start and end should be the value where regions start and end. If not specified, the edge values will be used. If timeseries x axis, date string, Date object and unixtime integer can be used. If class is set, the region element will have it as class. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript regions: [   {axis: 'x', start: 1, end: 4, class: 'region-1-4'} ] %h5 Example: %ul %li %a( href="/samples/region.html" ) Regions %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.show' } %p Show or hide legend. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   show: true } %h5 Example: %ul %li %a( href="/samples/options_legend.html" ) Hide Legend %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.hide' } %p Hide legend %br %p If true given, all legend will be hidden. If string or array given, only the legend that has the id will be hidden. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   hide: true   //or hide: 'data1'   //or hide: ['data1', 'data2'] } %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.position' } %p Change the position of legend. %br %p Currently bottom, right and inset are supported. %h5 Default: bottom %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   position: 'bottom' } %h5 Example: %ul %li %a( href="/samples/legend_position.html" ) Legend Position %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.inset' } %p Change inset legend attributes. %br %p This option accepts object that has the keys anchor, x, y and step. %p anchor decides the position of the legend. These anchors are available: %ul %li top-left %li top-right %li bottom-left %li bottom-right %p x and y set the position of the legend based on the anchor. %p step defines the max step the legend has (e.g. If 2 set and legend has 3 legend item, the legend 2 columns). %h5 Default: %div.sourcecode %pre %code.html.javascript {   anchor: 'top-left',   x: 10,   y: 0,   step: undefined } %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   inset: {     anchor: 'top-right',     x: 20,     y: 10,     step: 2   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.item.onclick' } %p Set click event handler to the legend item. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   item: {     onclick: function (id) { ... }   } } %h5 Example: %ul %li %a( href="/samples/legend_custom.html" ) Custom Legend %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.item.onmouseover' } %p Set mouseover event handler to the legend item. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   item: {     onmouseover: function (id) { ... }   } } %h5 Example: %ul %li %a( href="/samples/legend_custom.html" ) Custom Legend %hr %section %h3 = partial :reference_item_link, locals: { id: 'legend.item.onmouseout' } %p Set mouseout event handler to the legend item. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript legend: {   item: {     onmouseout: function (id) { ... }   } } %h5 Example: %ul %li %a( href="/samples/legend_custom.html" ) Custom Legend %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.show' } %p Show or hide tooltip. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   show: false } %h5 Example: %ul %li %a( href="/samples/tooltip_show.html" ) Hide Tooltip %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.grouped' } %p Set if tooltip is grouped or not for the data points. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   grouped: false } %h5 Example: %ul %li %a( href="/samples/tooltip_grouped.html" ) Tooltip Grouping %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.format.title' } %p Set format for the title of tooltip. %br %p Specified function receives x and index of the data point to show. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   format: {     title: function (x, index) { return 'Data ' + x; }   } } %h5 Example: %ul %li %a( href="/samples/tooltip_format.html" ) Tooltip Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.format.name' } %p Set format for the name of each data in tooltip. %br %p Specified function receives name, ratio, id and index of the data point to show. ratio will be undefined if the chart is not donut/pie/gauge. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   format: {     name: function (name, ratio, id, index) { return name; }   } } %h5 Example: %ul %li %a( href="/samples/tooltip_format.html" ) Tooltip Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.format.value' } %p Set format for the value of each data in tooltip. %br %p Specified function receives name, ratio, id and index of the data point to show. ratio will be undefined if the chart is not donut/pie/gauge. %p If undefined returned, the row of that value will be skipped. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   format: {     value: function (value, ratio, id, index) { return ratio; }   } } %h5 Example: %ul %li %a( href="/samples/tooltip_format.html" ) Tooltip Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.position' } %p Set custom position for the tooltip. %br %p This option can be used to modify the tooltip position by returning object that has top and left. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   position: function (data, width, height, element) {     return {top: 0, left: 0};   } } %h5 Reference: %ul %li %a( href="https://github.com/c3js/c3/pull/833" ) Introduce tooltip.position callback function #833 %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.contents' } %p Set custom HTML for the tooltip. %br %p Specified function receives data, defaultTitleFormat, defaultValueFormat and color of the data point to show. If tooltip.grouped is true, data includes multiple data points. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   contents: function (d, defaultTitleFormat, defaultValueFormat, color) {     return ... // formatted html as you want   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'tooltip.horizontal' } %p Show the tooltips based on the horizontal position of the mouse. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript tooltip: {   horizontal: true } %h5 Example: %ul %li %a( href="/samples/tooltip_horizontal.html" ) Horizontal tooltip %hr %section %h3 = partial :reference_item_link, locals: { id: 'subchart.show', experimental: true } %p Show sub chart on the bottom of the chart. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript subchart: {   show: true } %h5 Example: %ul %li %a( href="/samples/options_subchart.html" ) Subchart %hr %section %h3 = partial :reference_item_link, locals: { id: 'subchart.size.height', experimental: true } %p Change the height of the subchart. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript subchart: {   size: {     height: 20   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'subchart.onbrush', experimental: true } %p Set callback for brush event. %br %p Specified function receives the current zoomed x domain. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript subchart: {   onbrush: function (domain) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'subchart.axis.x.show', experimental: true } %p Show or hide x axis of subchart. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript subchart: {   axis: {     x: {       show: true     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.enabled', experimental: true } %p Enable zooming. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   enabled: false } %h5 Example: %ul %li %a( href="/samples/interaction_zoom.html" ) Zoom %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.type', experimental: true } %p There are two types of zoom behavior: 'scroll' and 'drag' %h5 Default: 'scroll' %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   type: 'drag' } %h5 Example: %ul %li %a( href="/samples/interaction_zoom_by_drag.html" ) Zoom by Drag %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.rescale', experimental: true } %p Enable to rescale after zooming. %br %p If true set, y domain will be updated according to the zoomed region. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   rescale: true } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.extent', experimental: true } %p Change zoom extent. %h5 Default: [1, 10] %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   extent: [1, 100] // enable more zooming } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.onzoom', experimental: true } %p Set callback that is called when the chart is zooming. %br %p Specified function receives the zoomed domain. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   onzoom: function (domain) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.onzoomstart', experimental: true } %p Set callback that is called when zooming starts. %br %p Specified function receives the zoom event. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   onzoomstart: function (event) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.onzoomend', experimental: true } %p Set callback that is called when zooming ends. %br %p Specified function receives the zoomed domain. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   onzoomend: function (domain) { ... } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'zoom.disableDefaultBehavior', experimental: true } %p Disable the default animation of zoom. This option is useful when you want to get the zoomed domain by onzoom or onzoomend handlers and override the default animation behavior. See #2439 for details. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript zoom: {   enabled: true,   disableDefaultBehavior: true,   onzoomend: d => console.log(d) } %hr %section %h3 = partial :reference_item_link, locals: { id: 'point.show' } %p Whether to show each point in line. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript point: {   show: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'point.r' } %p The radius size of each point. %h5 Default: 2.5 %h5 Format: %div.sourcecode %pre %code.html.javascript point: {   r: 5 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'point.focus.expand.enabled' } %p Whether to expand each point on focus. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript point: {   focus: {     expand: {       enabled: true     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'point.focus.expand.r' } %p The radius size of each point on focus. %h5 Default: point.r * 1.75 %h5 Format: %div.sourcecode %pre %code.html.javascript point: {   focus: {     expand: {       r: 1     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'point.select.r' } %p The radius size of each point on selected. %h5 Default: point.r * 4 %h5 Format: %div.sourcecode %pre %code.html.javascript point: {   select: {     r: 3   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'line.connectNull' } %p Set if null data point will be connected or not. %br %p If true set, the region of null data will be connected without any data point. If false set, the region of null data will not be connected and get empty. %h5 Default: false %h5 Format: %div.sourcecode %pre %code.html.javascript line: {   connectNull: true } %hr %section %h3 = partial :reference_item_link, locals: { id: 'line.step.type' } %p Change step type for step chart. %br %p step, step-before and step-after can be used. %h5 Default: 'step' %h5 Format: %div.sourcecode %pre %code.html.javascript line: {   step: {     type: 'step-after'   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'area.zerobased' } %p Set if min or max value will be 0 on area chart. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript area: {   zerobased: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'bar.width' } %p Change the width of bar chart. %h5 Default: auto %h5 Format: %div.sourcecode %pre %code.html.javascript bar: {   width: 10 } %h5 Example: %ul %li %a( href="/samples/chart_bar.html" ) Bar Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'bar.width.ratio' } %p Change the width of bar chart by ratio. %h5 Default: 0.6 %h5 Format: %div.sourcecode %pre %code.html.javascript bar: {   width: {     ratio: 0.2   } } %h5 Example: %ul %li %a( href="/samples/chart_bar.html" ) Bar Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'bar.zerobased' } %p Set if min or max value will be 0 on bar chart. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript bar: {   zerobased: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'pie.label.show' } %p Show or hide label on each pie piece. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript pie: {   label: {     show: false   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'pie.label.format' } %p Set formatter for the label on each pie piece. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript pie: {   label: {     format: function (value, ratio, id) {       return d3.format('$')(value);     }   } } %h5 Example: %ul %li %a( href="/samples/pie_label_format.html" ) Pie Label Format %hr %section %h3 = partial :reference_item_link, locals: { id: 'pie.label.threshold' } %p Set threshold to show/hide labels. %h5 Default: 0.05 %h5 Format: %div.sourcecode %pre %code.html.javascript pie: {   label: {     threshold: 0.1   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'pie.expand' } %p Enable or disable expanding pie pieces. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript pie: {   expand: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'pie.padAngle' } %p Sets the angular separation between each adjacent arc. %h5 Default: 0 %h5 Format: %div.sourcecode %pre %code.html.javascript pie: {   padAngle: .1 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.label.show' } %p Show or hide label on each donut piece. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   label: {     show: false   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.label.format' } %p Set formatter for the label on each donut piece. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   label: {     format: function (value, ratio, id) {       return d3.format('$')(value);     }   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.label.threshold' } %p Set threshold to show/hide labels. %h5 Default: 0.05 %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   label: {     threshold: 0.1   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.expand' } %p Enable or disable expanding donut pieces. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   expand: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.padAngle' } %p Sets the angular separation between each adjacent arc. %h5 Default: 0 %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   padAngle: .1 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.width' } %p Set width of donut chart. %h5 Default: auto %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   width: 10 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'donut.title' } %p Set title of donut chart. %h5 Default: '' %h5 Format: %div.sourcecode %pre %code.html.javascript donut: {   title: 'Title' } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.label.show' } %p Show or hide label on gauge. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   label: {     show: false   } } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.label.format' } %p Set formatter for the label on gauge. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   label: {     format: function (value, ratio) {       return value;     }   } } %h5 Example: %ul %li %a( href="/samples/chart_gauge.html" ) Gauge Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.expand' } %p Enable or disable expanding gauge. %h5 Default: true %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   expand: false } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.min' } %p Set min value of the gauge. %h5 Default: 0 %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   min: -100 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.max' } %p Set max value of the gauge. %h5 Default: 100 %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   max: 200 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.units' } %p Set units of the gauge. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   units: ' %' } %hr %section %h3 = partial :reference_item_link, locals: { id: 'gauge.width' } %p Set width of gauge chart. %h5 Default: auto %h5 Format: %div.sourcecode %pre %code.html.javascript gauge: {   width: 10 } %hr %section %h3 = partial :reference_item_link, locals: { id: 'spline.interpolation.type' } %p Set type of curve interpolation. %h5 Default: cardinal %p Available interpolation are: %ul %li linear %li linear-closed %li basis %li basis-open %li basis-closed %li bundle %li cardinal [default] %li cardinal-open %li cardinal-closed %li monotone %li step %li step-before %li step-after %h5 Format: %div.sourcecode %pre %code.html.javascript interpolation: {   type: "monotone" } %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.scaleMin' } %p Change the minimum value of the stanford color scale. %h5 Default: auto %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   scaleMin: 1 } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.scaleMax' } %p Change the maximum value of the stanford color scale. %h5 Default: auto %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   scaleMax: 10000 } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.scaleWidth' } %p Change the width of the stanford color scale. %h5 Default: 20 %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   scaleWidth: 20 } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.scaleFormat' } %p Set formatter for stanford color scale axis tick text. %br %p This option accepts the string 'pow10', a d3.format object and any function you define. %h5 Default: d3.format("d") - decimal notation, rounded to integer %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   scaleFormat: 'pow10'   // or d3.format("d")   // or a function } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.scaleValues' } %p Set the values for stanford color scale axis tick text. %br %p This option accepts a function that returns an array of numbers. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   scaleValues: (minValue, maxValue) => {     const step = (maxValue - minValue) / 10;     return d3.range(minValue, maxValue + step, step);   } } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.colors' } %p Set the color interpolator for stanford color scale. %br %p This option is a d3.interpolate* object or any function you define that receives a value between 0 and 1, and returns a color as string. %h5 Default: d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)) %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   colors: d3.interpolatePlasma } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.padding' } %p Set the padding for the stanford color scale. %p This option accepts array including object that has top, right, bottom and left. %h5 Default: undefined %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   padding: {     top: 15,     right: 0,     bottom: 0,     left: 0   } } %h5 Example: %ul %li %a( href="/samples/chart_stanford.html" ) Stanford Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.texts' } %p Show text anywhere inside the chart. %br %p This option accepts array including object that has x, y, content and class. The key class is optional. %p x and y are the starting position of the text, content is the text content to show. If class is set, the text element will have it as class. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   texts: [     {x: 1, y: 4, content: 'my custom text here', class: 'text-1-4'}   ] } %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.lines' } %p Show lines anywhere inside the chart. %br %p This option accepts array including object that has value_x1, value_y1, value_x2, value_y2 and class. The key class is optional. %p value_x1 and value_y1 are the starting position of the line, value_x2 and value_y2 are the ending position of the line. If class is set, the line element will have it as class. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   lines: [     {value_x1: 0, value_y1: 0, value_x2: 65, value_y2: 65, class: "line-0-65"}   ] } %hr %section %h3 = partial :reference_item_link, locals: { id: 'stanford.regions' } %p Show regions anywhere inside the chart. %br %p This option accepts array including object that has points, text, opacity and class. The keys text, opacity and class are optional. %p points accepts array including object that has x and y that represent the coordinates of each point. %p text accepts function that returns a string with the text to show. If the current chart type is stanford the function receives value and percentage as parameters that represent the number of points in this region. %p opacity accepts a number between 0 and 1, the default opacity is 0.2. %p If class is set, the line element will have it as class. %b %h5 Note: %p Points should be added in a counter-clockwise direction to close the polygon. %h5 Default: [] %h5 Format: %div.sourcecode %pre %code.html.javascript stanford: {   regions: [     {       points: [ // add points counter-clockwise         {x: 0, y: 0},         {x: 40, y: 40},         {x: 0, y: 40}       ],       text: function (value, percentage) {         return "Normal Operations: " + value + " (" + percentage + "%)";       },       opacity: 0.2, // 0 to 1       class: "region-triangle-1"     }   ] } %hr %br %section %h2 API %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.focus' } %p This API highlights specified targets and fade out the others. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be highlighted. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.focus(targetIds) %p targetIds  String or Array %p Target ids to be highlighted. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be highlighted and the others will be faded out chart.focus('data1'); \ \// data1 and data2 will be highlighted and the others will be faded out chart.focus(['data1', 'data2']); \ \// all targets will be highlighted chart.focus(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.defocus' } %p This API fades out specified targets and reverts the others. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be faded out. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.defocus(targetIds) %p targetIds  String or Array %p Target ids to be faded out. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be faded out and the others will be reverted. chart.defocus('data1'); \ \// data1 and data2 will be faded out and the others will be reverted. chart.defocus(['data1', 'data2']); \ \// all targets will be faded out. chart.defocus(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.revert' } %p This API reverts specified targets. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be reverted. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.revert(targetIds) %p targetIds  String or Array %p Target ids to be reverted. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be reverted. chart.revert('data1'); \ \// data1 and data2 will be reverted. chart.revert(['data1', 'data2']); \ \// all targets will be reverted. chart.revert(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.show' } %p This API shows specified targets. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be shown. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.show(targetIds, options) %p targetIds  String or Array %p Target ids to be shown. %br %p options  Object %p If withLegend is set true, legend will be shown together with the specified data. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be shown. chart.show('data1'); \ \// data1 and data2 will be shown. chart.show(['data1', 'data2']); \ \// all targets will be shown. chart.show(); \ \// data1 will be shown together with its legend. chart.show('data1', {withLegend: true}); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.hide' } %p This API hides specified targets. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be hidden. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.hide(targetIds, options) %p targetIds  String or Array %p Target ids to be hidden. %br %p options  Object %p If withLegend is set true, legend will be hidden together with the specified data. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be hidden. chart.hide('data1'); \ \// data1 and data2 will be hidden. chart.hide(['data1', 'data2']); \ \// all targets will be hidden. chart.hide(); \ \// data1 will be hidden together with its legend. chart.hide('data1', {withLegend: true}); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.toggle' } %p This API toggles (shows or hides) specified targets. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be toggles. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.toggle(targetIds, options) %p targetIds  String or Array %p Target ids to be toggled. %br %p options  Object %p If withLegend is set true, legend will be toggled together with the specified data. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 will be toggled. chart.toggle('data1'); \ \// data1 and data2 will be toggled. chart.toggle(['data1', 'data2']); \ \// all targets will be toggled. chart.toggle(); \ \// data1 will be toggled together with its legend. chart.toggle('data1', {withLegend: true}); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.load' } %p Load data to the chart. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be toggles. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.load(args) %p args  Object %p If url, json, rows and columns given, the data will be loaded. If data that has the same target id is given, the chart will be updated. Otherwise, new target will be added. %br %p If classes given, the classes specified by data.classes will be updated. classes must be Object that has target id as keys. %br %p If categories given, the categories specified by axis.x.categories or data.x will be updated. categories must be Array. %br %p If axes given, the axes specified by data.axes will be updated. axes must be Object that has target id as keys. %br %p If colors given, the colors specified by data.colors will be updated. colors must be Object that has target id as keys. %br %p If type or types given, the type of targets will be updated. type must be String and types must be Object. %br %p If unload given, data will be unloaded before loading new data. If true given, all of data will be unloaded. If target ids given as String or Array, specified targets will be unloaded. %br %p If done given, the specified function will be called after data loded. %h5 Note: %p unload should be used if some data needs to be unloaded simultaneously. If you call unload API soon after/before load instead of unload param, chart will not be rendered properly because of cancel of animation. %br %p done will be called after data loaded, but it's not after rendering. It's because rendering will finish after some transition and there is some time lag between loading and rendering. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Load data1 and unload data2 and data3 chart.load({   columns: [     ['data1', 100, 200, 150, ...],     ...   ],   unload: ['data2', 'data3'] }); %ul %li %a( href="/samples/data_load.html" ) Load Data %li %a( href="/samples/data_stringx.html" ) Load Category Data %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.unload' } %p Unload data to the chart. %br %p You can specify multiple targets by giving an array that includes id as String. If no argument is given, all of targets will be toggles. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.unload(args) %p args  Object %p If ids given, the data that has specified target id will be unloaded. ids should be String or Array. If ids is not specified, all data will be unloaded. %br %p If done given, the specified function will be called after data loded. %h5 Note: %p If you call load API soon after/before unload, unload param of load should be used. Otherwise chart will not be rendered properly because of cancel of animation. %br %p done will be called after data loaded, but it's not after rendering. It's because rendering will finish after some transition and there is some time lag between loading and rendering. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Unload data2 and data3 chart.unload({   ids: ['data2', 'data3'] }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.flow' } %p Flow data to the chart. %br %p By this API, you can append new data points to the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.flow(args) %p args  Object %p If json, rows and columns given, the data will be loaded. If data that has the same target id is given, the chart will be appended. Otherwise, new target will be added. One of these is required when calling. If json specified, keys is required as well as data.json %br %p If to is given, the lower x edge will move to that point. If not given, the lower x edge will move by the number of given data points. %br %p If length is given, the lower x edge will move by the number of this argument. %br %p If duration is given, the duration of the transition will be specified value. If not given, transition.duration will be used as default. %br %p If done is given, the specified function will be called when flow ends. %h5 Example: %div.sourcecode %pre %code.html.javascript \// 2 data points will be apprended to the tail and popped from the head. \// After that, 4 data points will be appended and no data points will be poppoed. chart.flow({   columns: [     ['x', '2013-01-11', '2013-01-21'],     ['data1', 500, 200],     ['data2', 100, 300],     ['data3', 200, 120]   ],   done: function () {     chart.flow({       columns: [         ['x', '2013-02-11', '2013-02-12', '2013-02-13', '2013-02-14'],         ['data1', 200, 300, 100, 250],         ['data2', 100, 90, 40, 120],         ['data3', 100, 100, 300, 500]       ],       length: 0     });   } }); %ul %li %a( href="/samples/api_flow.html" ) Flow %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.select' } %p Change data point state to selected. %br %p By this API, you can select data points. To use this API, data.selection.enabled needs to be set true. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.select(ids, indices, resetOthers) %p ids  Array %p Specify target ids to be selected. If this argument is not given, all targets will be the candidate. %br %p indices  Array %p Specify indices to be selected. If this argument is not given, all data points will be the candidate. %br %p resetOthers  boolean %p If this argument is set true, the data points that are not specified by ids, indices will be unselected. %h5 Example: %div.sourcecode %pre %code.html.javascript \// all data points of data1 will be selected. chart.select(['data1']); \ \// 3 data points on index 1, 3, 5 of data1 will be selected. chart.select(['data1'], [1,3,5]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.unselect' } %p Change data point state to unselected. %br %p By this API, you can unselect data points. To use this API, data.selection.enabled needs to be set true. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.unselect(ids, indices) %p ids  Array %p Specify target ids to be unselected. If this argument is not given, all targets will be the candidate. %br %p indices  Array %p Specify indices to be unselected. If this argument is not given, all data points will be the candidate. %h5 Example: %div.sourcecode %pre %code.html.javascript \// all data points of data1 will be unselected. chart.unselect(['data1']); \ \// 3 data points on index 1, 3, 5 of data1 will be unselected. chart.unselect(['data1'], [1,3,5]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.selected' } %p Get selected data points. %br %p By this API, you can get selected data points information. To use this API, data.selection.enabled needs to be set true. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.selected(targetId) %p targetId  String %p You can filter the result by giving target id that you want to get. If not given, all of data points will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// all selected data points will be returned. chart.selected(); \ \// all selected data points of data1 will be returned. chart.selected('data1'); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.transform' } %p Change the type of the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.transform(type, targetIds) %p type  String %p Specify the type to be transformed. The types listed in data.type can be used. %br %p targetIds  String or Array %p Specify targets to be transformed. If not given, all targets will be the candidate. %h5 Example: %div.sourcecode %pre %code.html.javascript \// all targets will be bar chart. chart.transform('bar'); \ \// only data1 will be bar chart. chart.transform('bar', 'data1'); \ \// only data1 and data2 will be bar chart. chart.transform('bar', ['data1', 'data2']); %ul %li %a( href="/samples/transform_line.html" ) Transform to Line Chart %a( href="/samples/transform_bar.html" ) Transform to Bar Chart %a( href="/samples/transform_pie.html" ) Transform to Pie Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.groups' } %p Update groups for the targets. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.groups(groups) %p groups  Array %p This argument needs to be an Array that includes one or more Array that includes target ids to be grouped. %h5 Example: %div.sourcecode %pre %code.html.javascript \// data1 and data2 will be a new group. chart.groups([['data1', 'data2']]); %ul %li %a( href="/samples/chart_bar_stacked.html" ) Stacked Bar Chart %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.xgrids' } %p Update x grid lines. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.xgrids(grids) %p grids  Array %p X grid lines will be replaced with this argument. The format of this argument is the same as grid.x.lines. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Show 2 x grid lines chart.xgrids([   {value: 1, text:'Label 1'},   {value: 4, text: 'Label 4'} ]); %ul %li %a( href="/samples/api_grid_x.html" ) X Grids %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.xgrids.add' } %p Add x grid lines. %br %p This API adds new x grid lines instead of replacing like xgrids. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.xgrids.add(grids) %p grids  Array or Object %p New x grid lines will be added. The format of this argument is the same as grid.x.lines and it's possible to give an Object if only one line will be added. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Add a new x grid line chart.xgrids.add(   {value: 4, text: 'Label 4'} ); \ \// Add new x grid lines chart.xgrids.add([   {value: 2, text: 'Label 2'},   {value: 4, text: 'Label 4'} ]); %ul %li %a( href="/samples/api_grid_x.html" ) X Grids %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.xgrids.remove' } %p Remove x grid lines. %br %p This API removes x grid lines. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.xgrids.remove(args) %p args  Object %p This argument should include value or class. If value is given, the x grid lines that have specified x value will be removed. If class is given, the x grid lines that have specified class will be removed. If args is not given, all of x grid lines will be removed. %h5 Example: %div.sourcecode %pre %code.html.javascript \// x grid line on x = 2 will be removed chart.xgrids.remove({value: 2}); \ \// x grid lines that have 'grid-A' will be removed chart.xgrids.remove({class: 'grid-A'}); \ \// all of x grid lines will be removed chart.xgrids.remove(); %ul %li %a( href="/samples/api_grid_x.html" ) X Grids %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.ygrids' } %p Update y grid lines. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.ygrids(grids) %p grids  Array %p X grid lines will be replaced with this argument. The format of this argument is the same as grid.y.lines. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Show 2 y grid lines chart.ygrids([   {value: 100, text:'Label 1'},   {value: 400, text: 'Label 4'} ]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.ygrids.add' } %p Add y grid lines. %br %p This API adds new y grid lines instead of replacing like ygrids. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.ygrids.add(grids) %p grids  Array or Object %p New y grid lines will be added. The format of this argument is the same as grid.y.lines and it's possible to give an Object if only one line will be added. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Add a new y grid line chart.ygrids.add(   {value: 400, text: 'Label 4'} ); \ \// Add new y grid lines chart.ygrids.add([   {value: 200, text: 'Label 2'},   {value: 400, text: 'Label 4'} ]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.ygrids.remove' } %p Remove y grid lines. %br %p This API removes y grid lines. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.ygrids.remove(args) %p args  Object %p This argument should include value or class. If value is given, the y grid lines that have specified y value will be removed. If class is given, the y grid lines that have specified class will be removed. If args is not given, all of y grid lines will be removed. %h5 Example: %div.sourcecode %pre %code.html.javascript \// y grid line on y = 200 will be removed chart.ygrids.remove({value: 200}); \ \// y grid lines that have 'grid-A' will be removed chart.ygrids.remove({class: 'grid-A'}); \ \// all of y grid lines will be removed chart.ygrids.remove(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.regions' } %p Update regions. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.regions(regions) %p regions  Array %p Regions will be replaced with this argument. The format of this argument is the same as regions. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Show 2 regions chart.regions([   {axis: 'x', start: 5, class: 'regionX'},   {axis: 'y', end: 50, class: 'regionY'} ]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.regions.add' } %p Add new region. %br %p This API adds new region instead of replacing like regions. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.regions.add(regions) %p regions  Array or Object %p New region will be added. The format of this argument is the same as regions and it's possible to give an Object if only one region will be added. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Add a new region chart.regions.add(   {axis: 'x', start: 5, class: 'regionX'} ); \ \// Add new regions chart.regions.add([   {axis: 'x', start: 5, class: 'regionX'},   {axis: 'y', end: 50, class: 'regionY'} ]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.regions.remove' } %p Remove regions. %br %p This API removes regions. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.regions.remove(args) %p args  Object %p This argument should include classes. If classes is given, the regions that have one of the specified classes will be removed. If args is not given, all of regions will be removed. %h5 Example: %div.sourcecode %pre %code.html.javascript \// regions that have 'region-A' or 'region-B' will be removed. chart.regions.remove({classes: ['region-A', 'region-B']}); \ \// all of regions will be removed. chart.regions.remove(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data' } %p Get data loaded in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data(targetIds) %p targetIds  String or Array %p If this argument is given, this API returns the specified target data. If this argument is not given, all of data will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get only data1 data chart.data('data1'); \ \// Get data1 and data2 data chart.data(['data1', 'data2']); \ \// Get all data chart.data(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data.shown' } %p Get data shown in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data.shown(targetIds) %p targetIds  String or Array %p If this argument is given, this API filters the data with specified target ids. If this argument is not given, all shown data will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get shown data by filtering to include only data1 data chart.data.shown('data1'); \ \// Get shown data by filtering to include data1 and data2 data chart.data.shown(['data1', 'data2']); \ \// Get all shown data chart.data.shown(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data.values' } %p Get values of the data loaded in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data.values(targetId) %p targetId  String %p This API returns the values of specified target. If this argument is not given, null will be retruned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get data1 values chart.data.values('data1'); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data.names' } %p Get and set names of the data loaded in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data.names(names) %p names  Object %p If this argument is given, the names of data will be updated. If not given, the current names will be returned. The format of this argument is the same as data.names. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get current names chart.data.names(); \ \// Update names chart.data.names({   data1: 'New Name 1',   data2: 'New Name 2' }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data.colors' } %p Get and set colors of the data loaded in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data.colors(colors) %p colors  Object %p If this argument is given, the colors of data will be updated. If not given, the current colors will be returned. The format of this argument is the same as data.colors. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get current colors chart.data.colors(); \ \// Update colors chart.data.colors({   data1: '#FFFFFF',   data2: '#000000' }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.data.axes' } %p Get and set axes of the data loaded in the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.data.axes(axes) %p axes  Object %p If this argument is given, the axes of data will be updated. If not given, the current axes will be returned. The format of this argument is the same as data.axes. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get current axes chart.data.axes(); \ \// Update axes chart.data.axes({   data1: 'y',   data2: 'y2' }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.x' } %p Get and set x values for the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.x(x) %p x  Array %p If x is given, x values of every target will be updated. If no argument is given, current x values will be returned as an Object whose keys are the target ids. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get current x values chart.x(); \ \// Update x values for all targets chart.x([100, 200, 300, 400, ...]); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.xs' } %p Get and set x values for the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.xs(xs) %p xs  Object %p If xs is given, specified target's x values will be updated. If no argument is given, current x values will be returned as an Object whose keys are the target ids. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Get current x values chart.xs(); \ \// Update x values for all targets chart.xs({   data1: [10, 20, 30, 40, ...],   data2: [100, 200, 300, 400, ...] }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.axis.labels' } %p Get and set axis labels. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.axis.labels(labels) %p labels  Object %p If labels is given, specified axis' label will be updated. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Update axis' label chart.axis.labels({   x: 'New X Axis Label',   y: 'New Y Axis Label' }); %ul %li %a( href="/samples/api_axis_label.html" ) Update Axis Label %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.axis.min' } %p Get and set axis min value. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.axis.min(min) %p min  Object %p If min is given, specified axis' min value will be updated. If no argument is given, the current min values for each axis will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Update axis' min chart.axis.min({   x: -10,   y: 1000,   y2: 100 }); %ul %li %a( href="/samples/api_axis_range.html" ) Update Axis Range %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.axis.max' } %p Get and set axis max value. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.axis.max(max) %p max  Object %p If max is given, specified axis' max value will be updated. If no argument is given, the current max values for each axis will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Update axis' max chart.axis.max({   x: 100,   y: 1000,   y2: 10000 }); %ul %li %a( href="/samples/api_axis_range.html" ) Update Axis Range %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.axis.range' } %p Get and set axis min and max value. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.axis.range(range) %p range  Object %p If range is given, specified axis' min and max value will be updated. If no argument is given, the current min and max values for each axis will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Update axis' min and max values chart.axis.range({   min: {     x: -10,     y: -1000,     y2: -10000   },   max: {     x: 100,     y: 1000,     y2: 10000   } }); %ul %li %a( href="/samples/api_axis_range.html" ) Update Axis Range %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.axis.types' } %p Get and set axis y/y2 types. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.axis.types(types) %p types  Object %p If types is given, specified axis' type value will be updated. If no argument is given, the current types for y/y2 axis will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Update axis' types chart.axis.types({   y: 'linear',   y2: 'log' }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.legend.show' } %p Show legend for each target. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.legend.show(targetIds) %p targetIds  Array or String %p If targetIds is given, specified target's legend will be shown. If only one target is the candidate, String can be passed. If no argument is given, all of target's legend will be shown. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Show legend for data1. chart.legend.show('data1'); \ \// Show legend for data1 and data2. chart.legend.show(['data1', 'data2']); \ \// Show all legend. chart.legend.show(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.legend.hide' } %p Hide legend for each target. %div.sourcecode %pre %code.html.javascript \.legend.hide(targetIds) %p targetIds  Array or String %p If targetIds is given, specified target's legend will be hidden. If only one target is the candidate, String can be passed. If no argument is given, all of target's legend will be hidden. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Hide legend for data1. chart.legend.hide('data1'); \ \// Hide legend for data1 and data2. chart.legend.hide(['data1', 'data2']); \ \// Hide all legend. chart.legend.hide(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.subchart.isShown' } %p Returns true if the sub chart is shown. %h5 Example: %div.sourcecode %pre %code.html.javascript if (chart.subchart.isShown()) {   // Sub chart is shown } %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.subchart.show' } %p Shows sub chart at the bottom of the chart. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Show sub chart chart.subchart.show(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.subchart.hide' } %p Hides sub chart. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Hide sub chart chart.subchart.hide(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.zoom' } %p Zoom by giving x domain. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.zoom(domain) %p domain  Array %p If domain is given, the chart will be zoomed to the given domain. If no argument is given, the current zoomed domain will be returned. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Zoom to specified domain chart.zoom([10, 20]); \ \// Get the current zoomed domain chart.zoom(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.unzoom' } %p Unzoom to the original domain. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.unzoom() %h5 Example: %div.sourcecode %pre %code.html.javascript \// Unzoom to the original domain chart.unzoom(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.zoom.enable' } %p Enable and disable zooming. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.zoom.enable(enabled) %p enabled  Boolean %p If enabled is true, the feature of zooming will be enabled. If false is given, it will be disabled. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Enable zooming chart.zoom.enable(true); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.resize' } %p Resize the chart. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.resize(size) %p size  Object %p This argument should include width and height in pixels. %h5 Example: %div.sourcecode %pre %code.html.javascript \// Resize to 640x480 chart.resize({   height: 640,   width: 480 }); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.flush' } %p Force to redraw. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.flush() %h5 Example: %div.sourcecode %pre %code.html.javascript \// Force to redraw chart.flush(); %hr %section %h3 = partial :reference_item_link, locals: { id: 'api.destroy' } %p Reset the chart object and remove element and events completely. %h5 Arguments: %div.sourcecode %pre %code.html.javascript \.destroy() %h5 Example: %div.sourcecode %pre %code.html.javascript \// Destroy the chart chart.destroy(); \ \// If you have a reference to the chart make sure to call destroy in the following manner chart = chart.destroy(); %hr %br %section %h2 CLASS %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-line' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-lines' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-bar' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-bars' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-text' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-texts' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arc' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs-title' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs-background' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs-gauge-unit' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs-gauge-max' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-chart-arcs-gauge-min' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-selected-circle' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-selected-circles' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-event-rect' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-event-rects' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-event-rects-single' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-event-rects-multiple' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-zoom-rect' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-brush' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-focused' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-region' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-regions' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-tooltip' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-tooltip-name' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-shape' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-shapes' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-line' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-lines' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-bar' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-bars' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-circle' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-circles' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-arc' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-arcs' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-area' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-areas' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-empty' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-text' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-texts' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-gauge-value' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-grid' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-xgrid' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-xgrids' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-xgrid-line' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-xgrid-lines' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-xgrid-focus' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-ygrid' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-ygrids' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-ygrid-line' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-ygrid-lines' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-elements' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-line' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-lines' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-region' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-regions' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-text' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-stanford-texts' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-x' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-x-label' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-y' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-y-label' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-y2' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-axis-y2-label' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-legend-item' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-legend-item-event' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-legend-item-tile' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-legend-item-hidden' } %p ... %hr %section %h3 = partial :reference_item_link, locals: { id: 'class.c3-legend-item-focused' } %p ... %hr = partial :footer = partial :script = partial :script_scroll ================================================ FILE: docs/robots.txt ================================================ ================================================ FILE: docs/samples/api_axis_label.html.haml ================================================ = partial :sample, locals: { id: 'api_axis_label' } ================================================ FILE: docs/samples/api_axis_range.html.haml ================================================ = partial :sample, locals: { id: 'api_axis_range' } ================================================ FILE: docs/samples/api_data_color.html.haml ================================================ = partial :sample, locals: { id: 'api_data_color' } ================================================ FILE: docs/samples/api_data_name.html.haml ================================================ = partial :sample, locals: { id: 'api_data_name' } ================================================ FILE: docs/samples/api_flow.html.haml ================================================ = partial :sample, locals: { id: 'api_flow' } ================================================ FILE: docs/samples/api_grid_x.html.haml ================================================ = partial :sample, locals: { id: 'api_grid_x' } ================================================ FILE: docs/samples/api_resize.html.haml ================================================ = partial :sample, locals: { id: 'api_resize' } ================================================ FILE: docs/samples/axes_label.html.haml ================================================ = partial :sample, locals: { id: 'axes_label' } ================================================ FILE: docs/samples/axes_label_position.html.haml ================================================ = partial :sample, locals: { id: 'axes_label_position' } ================================================ FILE: docs/samples/axes_rotated.html.haml ================================================ = partial :sample, locals: { id: 'axes_rotated' } ================================================ FILE: docs/samples/axes_x_localtime.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_localtime' } ================================================ FILE: docs/samples/axes_x_tick_count.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_count' } ================================================ FILE: docs/samples/axes_x_tick_culling.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_culling' } ================================================ FILE: docs/samples/axes_x_tick_fit.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_fit' } ================================================ FILE: docs/samples/axes_x_tick_format.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_format' } ================================================ FILE: docs/samples/axes_x_tick_rotate.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_rotate' } ================================================ FILE: docs/samples/axes_x_tick_values.html.haml ================================================ = partial :sample, locals: { id: 'axes_x_tick_values' } ================================================ FILE: docs/samples/axes_y2.html.haml ================================================ = partial :sample, locals: { id: 'axes_y2' } ================================================ FILE: docs/samples/axes_y_padding.html.haml ================================================ = partial :sample, locals: { id: 'axes_y_padding' } ================================================ FILE: docs/samples/axes_y_range.html.haml ================================================ = partial :sample, locals: { id: 'axes_y_range' } ================================================ FILE: docs/samples/axes_y_tick_format.html.haml ================================================ = partial :sample, locals: { id: 'axes_y_tick_format' } ================================================ FILE: docs/samples/categorized.html.haml ================================================ = partial :sample, locals: { id: 'categorized' } ================================================ FILE: docs/samples/chart_area.html.haml ================================================ = partial :sample, locals: { id: 'chart_area' } ================================================ FILE: docs/samples/chart_area_stacked.html.haml ================================================ = partial :sample, locals: { id: 'chart_area_stacked' } ================================================ FILE: docs/samples/chart_bar.html.haml ================================================ = partial :sample, locals: { id: 'chart_bar' } ================================================ FILE: docs/samples/chart_bar_stacked.html.haml ================================================ = partial :sample, locals: { id: 'chart_bar_stacked' } ================================================ FILE: docs/samples/chart_combination.html.haml ================================================ = partial :sample, locals: { id: 'chart_combination' } ================================================ FILE: docs/samples/chart_donut.html.haml ================================================ = partial :sample, locals: { id: 'chart_donut' } ================================================ FILE: docs/samples/chart_gauge.html.haml ================================================ = partial :sample, locals: { id: 'chart_gauge' } ================================================ FILE: docs/samples/chart_pie.html.haml ================================================ = partial :sample, locals: { id: 'chart_pie' } ================================================ FILE: docs/samples/chart_scatter.html.haml ================================================ = partial :sample, locals: { id: 'chart_scatter' } ================================================ FILE: docs/samples/chart_spline.html.haml ================================================ = partial :sample, locals: { id: 'chart_spline' } ================================================ FILE: docs/samples/chart_stanford.html.haml ================================================ = partial :sample, locals: { id: 'chart_stanford' } ================================================ FILE: docs/samples/chart_step.html.haml ================================================ = partial :sample, locals: { id: 'chart_step' } ================================================ FILE: docs/samples/data_color.html.haml ================================================ = partial :sample, locals: { id: 'data_color' } ================================================ FILE: docs/samples/data_columned.html.haml ================================================ = partial :sample, locals: { id: 'data_columned' } ================================================ FILE: docs/samples/data_json.html.haml ================================================ = partial :sample, locals: { id: 'data_json' } ================================================ FILE: docs/samples/data_label.html.haml ================================================ = partial :sample, locals: { id: 'data_label' } ================================================ FILE: docs/samples/data_label_format.html.haml ================================================ = partial :sample, locals: { id: 'data_label_format' } ================================================ FILE: docs/samples/data_load.html.haml ================================================ = partial :sample, locals: { id: 'data_load', other_files: ['c3_test.csv', 'c3_test2.csv'] } ================================================ FILE: docs/samples/data_name.html.haml ================================================ = partial :sample, locals: { id: 'data_name' } ================================================ FILE: docs/samples/data_number_format_l10n.html.haml ================================================ = partial :sample, locals: { id: 'data_number_format_l10n' } ================================================ FILE: docs/samples/data_order.html.haml ================================================ = partial :sample, locals: { id: 'data_order' } ================================================ FILE: docs/samples/data_rowed.html.haml ================================================ = partial :sample, locals: { id: 'data_rowed' } ================================================ FILE: docs/samples/data_stringx.html.haml ================================================ = partial :sample, locals: { id: 'data_stringx', other_files: ['c3_string_x.csv'] } ================================================ FILE: docs/samples/data_url.html.haml ================================================ = partial :sample, locals: { id: 'data_url', other_files: ['c3_test.csv', 'c3_test.json'] } ================================================ FILE: docs/samples/grid_x_lines.html.haml ================================================ = partial :sample, locals: { id: 'grid_x_lines' } ================================================ FILE: docs/samples/grid_y_lines.html.haml ================================================ = partial :sample, locals: { id: 'grid_y_lines' } ================================================ FILE: docs/samples/interaction_zoom.html.haml ================================================ = partial :sample, locals: { id: 'interaction_zoom' } ================================================ FILE: docs/samples/interaction_zoom_by_drag.html.haml ================================================ = partial :sample, locals: { id: 'interaction_zoom_by_drag' } ================================================ FILE: docs/samples/legend_custom.html.haml ================================================ = partial :sample, locals: { id: 'legend_custom', other_css: 'legend_custom.css' } ================================================ FILE: docs/samples/legend_position.html.haml ================================================ = partial :sample, locals: { id: 'legend_position' } ================================================ FILE: docs/samples/options_color.html.haml ================================================ = partial :sample, locals: { id: 'options_color' } ================================================ FILE: docs/samples/options_gridline.html.haml ================================================ = partial :sample, locals: { id: 'options_gridline' } ================================================ FILE: docs/samples/options_legend.html.haml ================================================ = partial :sample, locals: { id: 'options_legend' } ================================================ FILE: docs/samples/options_padding.html.haml ================================================ = partial :sample, locals: { id: 'options_padding' } ================================================ FILE: docs/samples/options_size.html.haml ================================================ = partial :sample, locals: { id: 'options_size' } ================================================ FILE: docs/samples/options_subchart.html.haml ================================================ = partial :sample, locals: { id: 'options_subchart' } ================================================ FILE: docs/samples/pie_label_format.html.haml ================================================ = partial :sample, locals: { id: 'pie_label_format' } ================================================ FILE: docs/samples/point_show.html.haml ================================================ = partial :sample, locals: { id: 'point_show' } ================================================ FILE: docs/samples/region.html.haml ================================================ = partial :sample, locals: { id: 'region', other_css: 'region.css' } ================================================ FILE: docs/samples/region_timeseries.html.haml ================================================ = partial :sample, locals: { id: 'region_timeseries' } ================================================ FILE: docs/samples/simple_multiple.html.haml ================================================ = partial :sample, locals: { id: 'simple_multiple' } ================================================ FILE: docs/samples/simple_regions.html.haml ================================================ = partial :sample, locals: { id: 'simple_regions' } ================================================ FILE: docs/samples/simple_xy.html.haml ================================================ = partial :sample, locals: { id: 'simple_xy' } ================================================ FILE: docs/samples/simple_xy_multiple.html.haml ================================================ = partial :sample, locals: { id: 'simple_xy_multiple' } ================================================ FILE: docs/samples/style_grid.html.haml ================================================ = partial :sample, locals: { id: 'style_grid', other_css: 'style_grid.css' } ================================================ FILE: docs/samples/style_region.html.haml ================================================ = partial :sample, locals: { id: 'style_region', other_css: 'style_region.css' } ================================================ FILE: docs/samples/timeseries.html.haml ================================================ = partial :sample, locals: { id: 'timeseries' } ================================================ FILE: docs/samples/tooltip_format.html.haml ================================================ = partial :sample, locals: { id: 'tooltip_format' } ================================================ FILE: docs/samples/tooltip_grouped.html.haml ================================================ = partial :sample, locals: { id: 'tooltip_grouped' } ================================================ FILE: docs/samples/tooltip_horizontal.html.haml ================================================ = partial :sample, locals: { id: 'tooltip_horizontal' } ================================================ FILE: docs/samples/tooltip_show.html.haml ================================================ = partial :sample, locals: { id: 'tooltip_show' } ================================================ FILE: docs/samples/transform_area.html.haml ================================================ = partial :sample, locals: { id: 'transform_area' } ================================================ FILE: docs/samples/transform_areaspline.html.haml ================================================ = partial :sample, locals: { id: 'transform_areaspline' } ================================================ FILE: docs/samples/transform_bar.html.haml ================================================ = partial :sample, locals: { id: 'transform_bar' } ================================================ FILE: docs/samples/transform_donut.html.haml ================================================ = partial :sample, locals: { id: 'transform_donut' } ================================================ FILE: docs/samples/transform_line.html.haml ================================================ = partial :sample, locals: { id: 'transform_line' } ================================================ FILE: docs/samples/transform_pie.html.haml ================================================ = partial :sample, locals: { id: 'transform_pie' } ================================================ FILE: docs/samples/transform_scatter.html.haml ================================================ = partial :sample, locals: { id: 'transform_scatter' } ================================================ FILE: docs/samples/transform_spline.html.haml ================================================ = partial :sample, locals: { id: 'transform_spline' } ================================================ FILE: docs/samples/transition_duration.html.haml ================================================ = partial :sample, locals: { id: 'transition_duration' } ================================================ FILE: extensions/chart-bubble/bubble.js ================================================ ;(function() { var extra = {} c3.chart.internal.fn.additionalConfig = { data_pairs: [] } c3.chart.internal.fn.beforeInit = function(config) { var that = this // update internals only when chart type is "bubble" if (config.data.type !== 'bubble') { return } // Set extra to ba able to be used in other part this.extra = extra extra.getKey = function(x, y) { return x + '::' + y } this.config.data_type = 'scatter' this.config.axis_x_padding = 0 this.config.axis_y_padding = 0 this.config.axis_x_tick_centered = true this.config.axis_x_tick_format = function(d) { return extra.names[d] } this.config.axis_y_tick_format = function(d) { return extra.names[d] } if (!config.color || !config.color.pattern) { this.config.color_pattern = ['#1f77b4'] } this.config.point_r = function(d) { var names = extra.names, values = extra.values, base_length = extra.base_length, x = names[d.x], y = d.id, key = extra.getKey(x, y), value = !values[key] ? 0 : values[key], max, max_r, max_area, a, area, r if (!base_length) { base_length = extra.base_length = d3.min([ that.svg .select('.c3-axis.c3-axis-y path') .node() .getTotalLength(), that.svg .select('.c3-axis.c3-axis-x path') .node() .getTotalLength() ]) } max = d3.max( Object.keys(values).map(function(key) { return values[key] }) ) max_r = base_length / (names.length * 2) max_area = max_r * max_r * Math.PI a = max_area / max area = value * a r = Math.sqrt(area / Math.PI) return r } this.config.point_sensitivity = 25 this.config.point_focus_expand_enabled = false this.config.legend_show = false if (!config.tooltip || !config.tooltip.contents) { this.config.tooltip_contents = function( d, defaultTitleFormat, defaultValueFormat, color ) { var x = extra.names[d[0].x], y = d[0].name, v = extra.values[extra.getKey(x, y)], text text = "" text += "' text += "' text += '
" + x + ' / ' + y + '
" + (!v ? 0 : v) + '
' return text } } // construct bubble chart data and setup config based on the values var xs = this.config.data_pairs.map(function(pair) { return pair.x }), ys = this.config.data_pairs.map(function(pair) { return pair.y }) extra.names = d3 .set(xs.concat(ys)) .values() .sort() this.config.axis_y_tick_values = extra.names.map(function(name, i) { return i }) var data_xs = {} extra.names.forEach(function(name) { data_xs[name] = name + '_x' }) var data_columns_xs = Object.keys(data_xs).map(function(key) { return [data_xs[key]].concat( extra.names.map(function(name, i) { return i }) ) }) var data_columns_values = extra.names.map(function(name, i) { return [name].concat( extra.names.map(function(name) { return i }) ) }) this.config.data_xs = data_xs this.config.data_columns = data_columns_xs.concat(data_columns_values) var values = {} this.config.data_pairs.forEach(function(pair) { if (!pair.x || !pair.y) { throw 'x and y are required in data.' } values[extra.getKey(pair.x, pair.y)] = pair.value }) extra.values = values this.config.axis_x_min = this.config.axis_y_min = -0.5 this.config.axis_x_max = this.config.axis_y_max = extra.names.length - 0.5 } })(window) ================================================ FILE: extensions/chart-bubble/index.html ================================================
================================================ FILE: extensions/exporter/config.json ================================================ { "js": [ "../../bower_components/d3/d3.min.js", "../../c3.min.js" ], "css": [ "../../c3.css" ], "template": "
" } ================================================ FILE: extensions/exporter/phantom-exporter.js ================================================ /** * PNG\JPEG exporter for C3.js, version 0.2 * (c) 2014 Yuval Bar-On * * usage: path/to/phantomjs output options [WxH] * */ // useful python-styled string formatting, "hello {0}! Javascript is {1}".format("world", "awesome"); if (!String.prototype.format) { String.prototype.format = function() { var args = arguments return this.replace(/{(\d+)}/g, function(match, number) { return typeof args[number] != 'undefined' ? args[number] : match }) } } // defaults var page = require('webpage').create(), fs = require('fs'), system = require('system'), config = JSON.parse(fs.read('config.json')), output, size if (system.args.length < 3) { console.log('Usage: phantasm.js filename html [WxH]') phantom.exit(1) } else { out = system.args[1] opts = JSON.parse(system.args[2]) if (system.args[3]) { var dimensions = system.args[3].split('x'), width = dimensions[0], height = dimensions[1] function checkNum(check) { check = parseInt(check) if (!isNaN(check)) return check return false } width = checkNum(width) height = checkNum(height) if (width && height) { page.viewportSize = { height: height, width: width } } // fit chart size to img size, if undefined if (!opts.size) { opts.size = { height: height, width: width } } } else { // check if size is defined in chart, // else apply defaults page.viewportSize = { height: opts.size && opts.size.height ? opts.size.height : 320, width: opts.size && opts.size.width ? opts.size.width : 710 } } } page.onResourceRequested = function(requestData, request) { console.log('::loading resource ', requestData['url']) } // helpful debug functions page.onConsoleMessage = function(msg) { console.log(msg) } page.onError = function(msg, trace) { var msgStack = ['ERROR: ' + msg] if (trace && trace.length) { msgStack.push('TRACE:') trace.forEach(function(t) { msgStack.push( ' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : '') ) }) } console.error(msgStack.join('\n')) } // render page function injectVerify(script) { var req = page.injectJs(script) if (!req) { console.log('\nError!\n' + script + ' not found!\n') phantom.exit(1) } } page.onLoadFinished = function() { console.log('::rendering') for (var j in config.js) { injectVerify(config.js[j]) } page.evaluate(function(chartoptions) { // phantomjs doesn't know how to handle .bind, so we override Function.prototype.bind = Function.prototype.bind || function(thisp) { var fn = this return function() { return fn.apply(thisp, arguments) } } // generate chart c3.generate(chartoptions) }, opts) // setting transition to 0 has proven not to work thus far, but 300ms isn't much // so this is acceptable for now setTimeout(function() { page.render(out) phantom.exit() }, 300) } // apply css inline because that usually renders better var css = '' for (var i in config.css) { css += fs.read(config.css[i]) } page.content = config.template.format(css) ================================================ FILE: extensions/js/c3ext.js ================================================ var c3ext = {} c3ext.generate = function(options) { if (options.zoom2 != null) { zoom2_reducers = options.zoom2.reducers || {} zoom2_enabled = options.zoom2.enabled _zoom2_factor = options.zoom2.factor || 1 _zoom2_maxItems = options.zoom2.maxItems } if (!zoom2_enabled) { return c3.generate(options) } var originalData = Q.copy(options.data) var zoom2_reducers var zoom2_enabled var _zoom2_maxItems if (_zoom2_maxItems == null) { var el = d3.select(options.bindto).node() if (el != null) { var availWidth = el.clientWidth var pointSize = 20 _zoom2_maxItems = Math.ceil(availWidth / pointSize) } if (_zoom2_maxItems == null || _zoom2_maxItems < 10) { _zoom2_maxItems = 10 } } function onZoomChanged(e) { refresh() } var zoom2 = c3ext.ZoomBehavior({ changed: onZoomChanged, bindto: options.bindto }) zoom2.enhance = function() { _zoom2_maxItems *= 2 var totalItems = zoom2.getZoom().totalItems if (_zoom2_maxItems > totalItems) _zoom2_maxItems = totalItems refresh() } zoom2.dehance = function() { _zoom2_maxItems = Math.ceil(_zoom2_maxItems / 2) + 1 refresh() } zoom2.maxItems = function() { return _zoom2_maxItems } function zoomAndReduceData(list, zoomRange, func, maxItems) { //var maxItems = 10;//Math.ceil(10 * zoomFactor); var list2 = list.slice(zoomRange[0], zoomRange[1]) var chunkSize = 1 var list3 = list2 if (list3.length > maxItems) { var chunkSize = Math.ceil(list2.length / maxItems) list3 = list3.splitIntoChunksOf(chunkSize).map(func) } //console.log("x" + getCurrentZoomLevel() + ", maxItems=" + maxItems + " chunkSize=" + chunkSize + " totalBefore=" + list2.length + ", totalAfter=" + list3.length); return list3 } function first(t) { return t[0] } var getDataForZoom = function(data) { if (data.columns == null || data.columns.length == 0) return var zoomInfo = zoom2.getZoom() if (zoomInfo.totalItems != data.columns[0].length - 1) { zoom2.setOptions({ totalItems: data.columns[0].length - 1 }) zoomInfo = zoom2.getZoom() } data.columns = originalData.columns.map(function(column) { var name = column[0] var reducer = zoom2_reducers[name] || first //by default take the first var values = column.slice(1) var newValues = zoomAndReduceData( values, zoomInfo.currentZoom, reducer, _zoom2_maxItems ) return [name].concat(newValues) }) return data } getDataForZoom(options.data) var chart = c3.generate(options) var _chart_load_org = chart.load.bind(chart) chart.zoom2 = zoom2 chart.load = function(data) { if (data.unload) { unload(data.unload) delete data.unload } Q.copy(data, originalData) refresh() } chart.unload = function(names) { unload(names) refresh() } function unload(names) { originalData.columns.removeAll(function(t) { names.contains(t) }) } function refresh() { var data = Q.copy(originalData) getDataForZoom(data) _chart_load_org(data) } return chart } c3ext.ZoomBehavior = function(options) { var zoom = { __type: 'ZoomBehavior' } var _zoom2_factor var _left var totalItems var currentZoom var bindto = options.bindto var _zoomChanged = options.changed || function() {} var element var mousewheelTimer var deltaY = 0 var leftRatio = 0 zoom.setOptions = function(options) { if (options == null) options = {} _zoom2_factor = options.factor || 1 _left = 0 totalItems = options.totalItems || 0 currentZoom = [0, totalItems] _zoomChanged = options.changed || _zoomChanged } zoom.setOptions(options) function verifyZoom(newZoom) { //newZoom.sort(); if (newZoom[1] > totalItems) { var diff = newZoom[1] - totalItems newZoom[0] -= diff newZoom[1] -= diff } if (newZoom[0] < 0) { var diff = newZoom[0] * -1 newZoom[0] += diff newZoom[1] += diff } if (newZoom[1] > totalItems) newZoom[1] = totalItems if (newZoom[0] < 0) newZoom[0] = 0 } function zoomAndPan(zoomFactor, left) { var itemsToShow = Math.ceil(totalItems / zoomFactor) var newZoom = [left, left + itemsToShow] verifyZoom(newZoom) currentZoom = newZoom onZoomChanged() } function onZoomChanged() { if (_zoomChanged != null) _zoomChanged(zoom.getZoom()) } function applyZoomAndPan() { zoomAndPan(_zoom2_factor, _left) } function getItemsToShow() { var itemsToShow = Math.ceil(totalItems / _zoom2_factor) return itemsToShow } zoom.getZoom = function() { return { totalItems: totalItems, currentZoom: currentZoom.slice() } } zoom.factor = function(factor, skipDraw) { if (arguments.length == 0) return _zoom2_factor _zoom2_factor = factor if (_zoom2_factor < 1) _zoom2_factor = 1 if (skipDraw) return applyZoomAndPan() } zoom.left = function(left, skipDraw) { if (arguments.length == 0) return _left _left = left if (_left < 0) _left = 0 var pageSize = getItemsToShow() //_left += pageSize; if (_left + pageSize > totalItems) _left = totalItems - pageSize console.log({ left: _left, pageSize: pageSize }) if (skipDraw) return applyZoomAndPan() } zoom.zoomAndPanByRatio = function(zoomRatio, panRatio) { var pageSize = getItemsToShow() var leftOffset = Math.round(pageSize * panRatio) var mouseLeft = _left + leftOffset zoom.factor(zoom.factor() * zoomRatio, true) var finalLeft = mouseLeft if (zoomRatio != 1) { var pageSize2 = getItemsToShow() var leftOffset2 = Math.round(pageSize2 * panRatio) finalLeft = mouseLeft - leftOffset2 } zoom.left(finalLeft, true) applyZoomAndPan() } zoom.zoomIn = function() { zoom.zoomAndPanByRatio(2, 0) } zoom.zoomOut = function() { zoom.zoomAndPanByRatio(0.5, 0) } zoom.panLeft = function() { zoom.zoomAndPanByRatio(1, -1) } zoom.panRight = function() { zoom.zoomAndPanByRatio(1, 1) } zoom.reset = function() { _left = 0 _zoom2_factor = 1 applyZoomAndPan() } function doZoom() { if (deltaY != 0) { var maxDelta = 10 var multiply = (maxDelta + deltaY) / maxDelta //var factor = chart.zoom2.factor()*multiply; //factor= Math.ceil(factor*100) / 100; console.log({ deltaY: deltaY, multiply: multiply }) zoom.zoomAndPanByRatio(multiply, leftRatio) //0.5);//leftRatio); deltaY = 0 } } function element_mousewheel(e) { deltaY += e.deltaY leftRatio = (e.offsetX - 70) / (e.currentTarget.offsetWidth - 70) //console.log({ "e.offsetX": e.offsetX, "e.currentTarget.offsetWidth": e.currentTarget.offsetWidth, leftRatio: leftRatio }); mousewheelTimer.set(150) e.preventDefault() } if (bindto != null) { element = $(options.bindto) if (element.mousewheel) { mousewheelTimer = new Timer(doZoom) element.mousewheel(element_mousewheel) } } return zoom } if (typeof Q == 'undefined') { var Q = function() {} Q.copy = function(src, target, options, depth) { ///Copies an object into a target object, recursively cloning any object or array in the way, overwrite=true will overwrite a primitive field value even if exists /// /// ///{ overwrite:false } ///The copied object if (depth == null) depth = 0 if (depth == 100) { console.warn('Q.copy is in depth of 100 - possible circular reference') } options = options || { overwrite: false } if (src == target || src == null) return target if (typeof src != 'object') { if (options.overwrite || target == null) return src return target } if (typeof src.clone == 'function') { if (options.overwrite || target == null) return src.clone() return target } if (target == null) { if (src instanceof Array) target = [] else target = {} } if (src instanceof Array) { for (var i = 0; i < src.length; i++) { var item = src[i] var item2 = target[i] item2 = Q.copy(item, item2, options, depth + 1) target[i] = item2 } target.splice(src.length, target.length - src.length) return target } for (var p in src) { var value = src[p] var value2 = target[p] value2 = Q.copy(value, value2, options, depth + 1) target[p] = value2 } return target } } if (typeof Timer == 'undefined') { var Timer = function(action, ms) { this.action = action if (ms != null) this.set(ms) } Timer.prototype.set = function(ms) { if (ms == null) ms = this._ms else this._ms = ms this.clear() if (ms == null) return this.timeout = window.setTimeout(this.onTick.bind(this), ms) } Timer.prototype.onTick = function() { this.clear() this.action() } Timer.prototype.clear = function(ms) { if (this.timeout == null) return window.clearTimeout(this.timeout) this.timeout = null } } if (typeof Array.prototype.splitIntoChunksOf == 'undefined') { Array.prototype.splitIntoChunksOf = function(countInEachChunk) { var chunks = Math.ceil(this.length / countInEachChunk) var list = [] for (var i = 0; i < this.length; i += countInEachChunk) { list.push(this.slice(i, i + countInEachChunk)) } return list } } ================================================ FILE: htdocs/css/index.css ================================================ .row { margin-left: 8px; } .row a { display: block; text-align: left; font-size: 1.2em; line-height: 1.8; } .row h3 { color: #777; } ================================================ FILE: htdocs/css/style.css ================================================ body { text-align: center; } #chart { width: 720px; margin: 24px auto; } ================================================ FILE: htdocs/data/c3_stanford_data.json ================================================ [ { "HPE": 2.5, "HPL": 24.5, "epochs": 1 }, { "HPE": 2.5, "HPL": 24, "epochs": 1 }, { "HPE": 2.5, "HPL": 27.5, "epochs": 1 }, { "HPE": 3, "HPL": 26, "epochs": 1 }, { "HPE": 3.5, "HPL": 39, "epochs": 1 }, { "HPE": 3.5, "HPL": 39.5, "epochs": 1 }, { "HPE": 4, "HPL": 41, "epochs": 1 }, { "HPE": 4, "HPL": 40, "epochs": 1 }, { "HPE": 4, "HPL": 42.5, "epochs": 1 }, { "HPE": 4, "HPL": 40.5, "epochs": 1 }, { "HPE": 4, "HPL": 41.5, "epochs": 1 }, { "HPE": 4, "HPL": 30, "epochs": 1 }, { "HPE": 4, "HPL": 56, "epochs": 1 }, { "HPE": 4, "HPL": 47, "epochs": 1 }, { "HPE": 3.5, "HPL": 54, "epochs": 1 }, { "HPE": 3.5, "HPL": 48.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 54.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 53, "epochs": 1 }, { "HPE": 3, "HPL": 51.5, "epochs": 1 }, { "HPE": 3, "HPL": 50, "epochs": 1 }, { "HPE": 3.5, "HPL": 52, "epochs": 1 }, { "HPE": 4.5, "HPL": 55, "epochs": 1 }, { "HPE": 4.5, "HPL": 56.5, "epochs": 1 }, { "HPE": 4.5, "HPL": 54, "epochs": 1 }, { "HPE": 4.5, "HPL": 55.5, "epochs": 1 }, { "HPE": 4.5, "HPL": 56, "epochs": 1 }, { "HPE": 4.5, "HPL": 48.5, "epochs": 1 }, { "HPE": 5, "HPL": 56, "epochs": 1 }, { "HPE": 5, "HPL": 56.5, "epochs": 1 }, { "HPE": 5, "HPL": 53.5, "epochs": 1 }, { "HPE": 5, "HPL": 51.5, "epochs": 1 }, { "HPE": 5, "HPL": 52, "epochs": 1 }, { "HPE": 5, "HPL": 31.5, "epochs": 1 }, { "HPE": 5.5, "HPL": 37, "epochs": 1 }, { "HPE": 5.5, "HPL": 38, "epochs": 1 }, { "HPE": 5.5, "HPL": 38.5, "epochs": 1 }, { "HPE": 5, "HPL": 36.5, "epochs": 1 }, { "HPE": 5, "HPL": 38.5, "epochs": 1 }, { "HPE": 5, "HPL": 22, "epochs": 1 }, { "HPE": 5, "HPL": 21, "epochs": 1 }, { "HPE": 5, "HPL": 22.5, "epochs": 1 }, { "HPE": 4.5, "HPL": 19, "epochs": 1 }, { "HPE": 3.5, "HPL": 13.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 16.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 15.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 14.5, "epochs": 1 }, { "HPE": 4, "HPL": 11.5, "epochs": 1 }, { "HPE": 4, "HPL": 11, "epochs": 1 }, { "HPE": 4, "HPL": 12, "epochs": 1 }, { "HPE": 4, "HPL": 14.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 19, "epochs": 1 }, { "HPE": 3.5, "HPL": 19.5, "epochs": 1 }, { "HPE": 2.5, "HPL": 56.5, "epochs": 1 }, { "HPE": 2.5, "HPL": 26.5, "epochs": 1 }, { "HPE": 3.5, "HPL": 22, "epochs": 2 }, { "HPE": 3, "HPL": 12.5, "epochs": 2 }, { "HPE": 2, "HPL": 11, "epochs": 2 }, { "HPE": 4, "HPL": 13.5, "epochs": 2 }, { "HPE": 3.5, "HPL": 11, "epochs": 2 }, { "HPE": 4, "HPL": 43, "epochs": 2 }, { "HPE": 4, "HPL": 55.5, "epochs": 2 }, { "HPE": 4, "HPL": 53.5, "epochs": 2 }, { "HPE": 3.5, "HPL": 48, "epochs": 2 }, { "HPE": 4.5, "HPL": 49.5, "epochs": 2 }, { "HPE": 4.5, "HPL": 54.5, "epochs": 2 }, { "HPE": 4.5, "HPL": 51.5, "epochs": 2 }, { "HPE": 5, "HPL": 52.5, "epochs": 2 }, { "HPE": 5, "HPL": 49.5, "epochs": 2 }, { "HPE": 5, "HPL": 47, "epochs": 2 }, { "HPE": 5, "HPL": 45.5, "epochs": 2 }, { "HPE": 5, "HPL": 46, "epochs": 2 }, { "HPE": 5.5, "HPL": 34.5, "epochs": 2 }, { "HPE": 5.5, "HPL": 37.5, "epochs": 2 }, { "HPE": 5, "HPL": 20, "epochs": 2 }, { "HPE": 4.5, "HPL": 19.5, "epochs": 2 }, { "HPE": 4.5, "HPL": 24, "epochs": 2 }, { "HPE": 3.5, "HPL": 12.5, "epochs": 2 }, { "HPE": 3.5, "HPL": 14, "epochs": 2 }, { "HPE": 4, "HPL": 10.5, "epochs": 2 }, { "HPE": 3.5, "HPL": 17, "epochs": 2 }, { "HPE": 3, "HPL": 56, "epochs": 2 }, { "HPE": 2.5, "HPL": 55, "epochs": 2 }, { "HPE": 2.5, "HPL": 14.5, "epochs": 2 }, { "HPE": 3.5, "HPL": 22.5, "epochs": 3 }, { "HPE": 4.5, "HPL": 29, "epochs": 3 }, { "HPE": 4.5, "HPL": 27.5, "epochs": 3 }, { "HPE": 2.5, "HPL": 28, "epochs": 3 }, { "HPE": 3, "HPL": 10, "epochs": 3 }, { "HPE": 3, "HPL": 26.5, "epochs": 3 }, { "HPE": 4, "HPL": 29, "epochs": 3 }, { "HPE": 4, "HPL": 39.5, "epochs": 3 }, { "HPE": 4, "HPL": 48.5, "epochs": 3 }, { "HPE": 4, "HPL": 50.5, "epochs": 3 }, { "HPE": 4.5, "HPL": 52.5, "epochs": 3 }, { "HPE": 5, "HPL": 50.5, "epochs": 3 }, { "HPE": 5, "HPL": 53, "epochs": 3 }, { "HPE": 5, "HPL": 37, "epochs": 3 }, { "HPE": 5.5, "HPL": 31.5, "epochs": 3 }, { "HPE": 5.5, "HPL": 33, "epochs": 3 }, { "HPE": 5.5, "HPL": 32, "epochs": 3 }, { "HPE": 5.5, "HPL": 36, "epochs": 3 }, { "HPE": 5, "HPL": 36, "epochs": 3 }, { "HPE": 5, "HPL": 34.5, "epochs": 3 }, { "HPE": 5, "HPL": 20.5, "epochs": 3 }, { "HPE": 4, "HPL": 19.5, "epochs": 3 }, { "HPE": 3.5, "HPL": 13, "epochs": 3 }, { "HPE": 3.5, "HPL": 18.5, "epochs": 3 }, { "HPE": 3.5, "HPL": 20, "epochs": 3 }, { "HPE": 4.5, "HPL": 28, "epochs": 4 }, { "HPE": 4.5, "HPL": 24.5, "epochs": 4 }, { "HPE": 2.5, "HPL": 29, "epochs": 4 }, { "HPE": 2.5, "HPL": 28.5, "epochs": 4 }, { "HPE": 3, "HPL": 29, "epochs": 4 }, { "HPE": 3.5, "HPL": 30, "epochs": 4 }, { "HPE": 3.5, "HPL": 11.5, "epochs": 4 }, { "HPE": 4, "HPL": 49.5, "epochs": 4 }, { "HPE": 3.5, "HPL": 49, "epochs": 4 }, { "HPE": 3.5, "HPL": 52.5, "epochs": 4 }, { "HPE": 4.5, "HPL": 53, "epochs": 4 }, { "HPE": 4.5, "HPL": 50, "epochs": 4 }, { "HPE": 5, "HPL": 47.5, "epochs": 4 }, { "HPE": 5, "HPL": 38, "epochs": 4 }, { "HPE": 5.5, "HPL": 34, "epochs": 4 }, { "HPE": 5, "HPL": 35, "epochs": 4 }, { "HPE": 4.5, "HPL": 23.5, "epochs": 4 }, { "HPE": 3.5, "HPL": 20.5, "epochs": 4 }, { "HPE": 2.5, "HPL": 31.5, "epochs": 4 }, { "HPE": 3.5, "HPL": 21, "epochs": 5 }, { "HPE": 4.5, "HPL": 28.5, "epochs": 5 }, { "HPE": 2, "HPL": 12, "epochs": 5 }, { "HPE": 3.5, "HPL": 36.5, "epochs": 5 }, { "HPE": 4, "HPL": 36.5, "epochs": 5 }, { "HPE": 3.5, "HPL": 51, "epochs": 5 }, { "HPE": 4.5, "HPL": 51, "epochs": 5 }, { "HPE": 4.5, "HPL": 50.5, "epochs": 5 }, { "HPE": 5, "HPL": 50, "epochs": 5 }, { "HPE": 4.5, "HPL": 20, "epochs": 5 }, { "HPE": 3.5, "HPL": 12, "epochs": 5 }, { "HPE": 3.5, "HPL": 58.5, "epochs": 5 }, { "HPE": 3.5, "HPL": 23, "epochs": 6 }, { "HPE": 4, "HPL": 29.5, "epochs": 6 }, { "HPE": 3, "HPL": 29.5, "epochs": 6 }, { "HPE": 3, "HPL": 28.5, "epochs": 6 }, { "HPE": 3.5, "HPL": 17.5, "epochs": 6 }, { "HPE": 3.5, "HPL": 38.5, "epochs": 6 }, { "HPE": 4, "HPL": 38.5, "epochs": 6 }, { "HPE": 4, "HPL": 49, "epochs": 6 }, { "HPE": 4, "HPL": 52.5, "epochs": 6 }, { "HPE": 5, "HPL": 34, "epochs": 6 }, { "HPE": 3.5, "HPL": 57.5, "epochs": 6 }, { "HPE": 2, "HPL": 56, "epochs": 6 }, { "HPE": 2, "HPL": 55, "epochs": 6 }, { "HPE": 4, "HPL": 20.5, "epochs": 7 }, { "HPE": 3, "HPL": 27, "epochs": 7 }, { "HPE": 2, "HPL": 11.5, "epochs": 7 }, { "HPE": 3, "HPL": 30, "epochs": 7 }, { "HPE": 3.5, "HPL": 10, "epochs": 7 }, { "HPE": 3.5, "HPL": 51.5, "epochs": 7 }, { "HPE": 3.5, "HPL": 50.5, "epochs": 7 }, { "HPE": 5, "HPL": 49, "epochs": 7 }, { "HPE": 5, "HPL": 51, "epochs": 7 }, { "HPE": 5, "HPL": 48, "epochs": 7 }, { "HPE": 5.5, "HPL": 33.5, "epochs": 7 }, { "HPE": 5.5, "HPL": 32.5, "epochs": 7 }, { "HPE": 4.5, "HPL": 23, "epochs": 7 }, { "HPE": 3.5, "HPL": 18, "epochs": 7 }, { "HPE": 3, "HPL": 28, "epochs": 8 }, { "HPE": 2.5, "HPL": 27, "epochs": 8 }, { "HPE": 3, "HPL": 25.5, "epochs": 8 }, { "HPE": 3.5, "HPL": 10.5, "epochs": 8 }, { "HPE": 4, "HPL": 52, "epochs": 8 }, { "HPE": 3, "HPL": 57.5, "epochs": 8 }, { "HPE": 4, "HPL": 28.5, "epochs": 9 }, { "HPE": 3, "HPL": 27.5, "epochs": 9 }, { "HPE": 2.5, "HPL": 29.5, "epochs": 9 }, { "HPE": 3.5, "HPL": 50, "epochs": 9 }, { "HPE": 4, "HPL": 51.5, "epochs": 9 }, { "HPE": 5, "HPL": 35.5, "epochs": 9 }, { "HPE": 4.5, "HPL": 21.5, "epochs": 9 }, { "HPE": 4, "HPL": 37.5, "epochs": 10 }, { "HPE": 4, "HPL": 39, "epochs": 10 }, { "HPE": 3.5, "HPL": 49.5, "epochs": 10 }, { "HPE": 4, "HPL": 50, "epochs": 10 }, { "HPE": 4, "HPL": 51, "epochs": 10 }, { "HPE": 4.5, "HPL": 22.5, "epochs": 10 }, { "HPE": 4, "HPL": 20, "epochs": 11 }, { "HPE": 4.5, "HPL": 25.5, "epochs": 11 }, { "HPE": 3.5, "HPL": 58, "epochs": 11 }, { "HPE": 5, "HPL": 48.5, "epochs": 12 }, { "HPE": 5, "HPL": 32, "epochs": 12 }, { "HPE": 3.5, "HPL": 23.5, "epochs": 13 }, { "HPE": 4, "HPL": 23, "epochs": 13 }, { "HPE": 4.5, "HPL": 25, "epochs": 13 }, { "HPE": 2.5, "HPL": 30, "epochs": 13 }, { "HPE": 3, "HPL": 24.5, "epochs": 13 }, { "HPE": 3.5, "HPL": 29.5, "epochs": 13 }, { "HPE": 4.5, "HPL": 21, "epochs": 13 }, { "HPE": 4, "HPL": 38, "epochs": 14 }, { "HPE": 5, "HPL": 32.5, "epochs": 14 }, { "HPE": 4, "HPL": 22, "epochs": 15 }, { "HPE": 3, "HPL": 12, "epochs": 15 }, { "HPE": 4, "HPL": 37, "epochs": 15 }, { "HPE": 4.5, "HPL": 22, "epochs": 16 }, { "HPE": 2, "HPL": 55.5, "epochs": 16 }, { "HPE": 3.5, "HPL": 38, "epochs": 17 }, { "HPE": 4, "HPL": 23.5, "epochs": 18 }, { "HPE": 4, "HPL": 21, "epochs": 18 }, { "HPE": 3.5, "HPL": 29, "epochs": 18 }, { "HPE": 2.5, "HPL": 55.5, "epochs": 18 }, { "HPE": 3, "HPL": 11.5, "epochs": 19 }, { "HPE": 2.5, "HPL": 12.5, "epochs": 19 }, { "HPE": 4, "HPL": 21.5, "epochs": 19 }, { "HPE": 3, "HPL": 56.5, "epochs": 19 }, { "HPE": 2.5, "HPL": 14, "epochs": 19 }, { "HPE": 5, "HPL": 33, "epochs": 20 }, { "HPE": 5, "HPL": 33.5, "epochs": 20 }, { "HPE": 4.5, "HPL": 20.5, "epochs": 20 }, { "HPE": 4.5, "HPL": 27, "epochs": 22 }, { "HPE": 4, "HPL": 22.5, "epochs": 23 }, { "HPE": 3, "HPL": 10.5, "epochs": 23 }, { "HPE": 2.5, "HPL": 13, "epochs": 23 }, { "HPE": 3.5, "HPL": 24, "epochs": 24 }, { "HPE": 4, "HPL": 28, "epochs": 24 }, { "HPE": 3.5, "HPL": 28.5, "epochs": 24 }, { "HPE": 3, "HPL": 57, "epochs": 24 }, { "HPE": 2.5, "HPL": 10, "epochs": 25 }, { "HPE": 3.5, "HPL": 37, "epochs": 28 }, { "HPE": 3.5, "HPL": 37.5, "epochs": 29 }, { "HPE": 2.5, "HPL": 56, "epochs": 29 }, { "HPE": 2.5, "HPL": 11, "epochs": 36 }, { "HPE": 2.5, "HPL": 10.5, "epochs": 38 }, { "HPE": 3.5, "HPL": 28, "epochs": 39 }, { "HPE": 2.5, "HPL": 13.5, "epochs": 43 }, { "HPE": 4.5, "HPL": 26, "epochs": 44 }, { "HPE": 3, "HPL": 11, "epochs": 47 }, { "HPE": 4, "HPL": 27.5, "epochs": 50 }, { "HPE": 4.5, "HPL": 26.5, "epochs": 54 }, { "HPE": 2.5, "HPL": 12, "epochs": 54 }, { "HPE": 2.5, "HPL": 26, "epochs": 59 }, { "HPE": 4, "HPL": 24.5, "epochs": 62 }, { "HPE": 4, "HPL": 24, "epochs": 62 }, { "HPE": 3, "HPL": 25, "epochs": 70 }, { "HPE": 2.5, "HPL": 25, "epochs": 70 }, { "HPE": 3.5, "HPL": 25, "epochs": 81 }, { "HPE": 2.5, "HPL": 11.5, "epochs": 84 }, { "HPE": 3.5, "HPL": 25.5, "epochs": 85 }, { "HPE": 3.5, "HPL": 26.5, "epochs": 86 }, { "HPE": 3.5, "HPL": 26, "epochs": 88 }, { "HPE": 2.5, "HPL": 25.5, "epochs": 89 }, { "HPE": 3.5, "HPL": 27.5, "epochs": 93 }, { "HPE": 3.5, "HPL": 27, "epochs": 94 }, { "HPE": 4, "HPL": 25, "epochs": 95 }, { "HPE": 4, "HPL": 27, "epochs": 106 }, { "HPE": 3.5, "HPL": 24.5, "epochs": 110 }, { "HPE": 4, "HPL": 26, "epochs": 111 }, { "HPE": 4, "HPL": 26.5, "epochs": 115 }, { "HPE": 4, "HPL": 25.5, "epochs": 170 } ] ================================================ FILE: htdocs/data/c3_test.csv ================================================ data1,data2,data3 120,80,200 140,50,210 170,100,250 150,70,300 180,120,280 ================================================ FILE: htdocs/data/c3_test.json ================================================ { "data1": [120, 140, 170, 150, 180], "data2": [80, 50, 100, 70, 120], "data3": [200, 210, 250, 300, 280] } ================================================ FILE: htdocs/data/c3_test.tsv ================================================ data1 data2 data3 520 380 100 540 350 110 570 400 150 550 370 200 580 420 180 ================================================ FILE: htdocs/data/c3_test2.csv ================================================ data1,data2,data3 20,180,400 40,150,310 70,120,470 50,170,400 80,200,380 ================================================ FILE: htdocs/data/c3_test2_ts.csv ================================================ x,data1,data2,data3 2013-04-01,20,180,400 2013-04-02,40,150,310 2013-04-03,70,120,470 2013-04-04,50,170,400 2013-04-05,80,200,380 ================================================ FILE: htdocs/data/c3_test3.csv ================================================ x,download,loading www.hogehoge.com,30,19 www.aaaa.com,30,13 www.bb.com,20,24 ================================================ FILE: htdocs/data/c3_test_2.json ================================================ { "data1": [20, 40, 70, 50, 80, 30], "data2": [180, 150, 200, 170, 220, 400], "data3": [1200, 1210, 1250, 1300, 1280, 1000] } ================================================ FILE: htdocs/data/c3_test_3.json ================================================ [ { "id": 1, "name": "abc", "data1": 1200, "data2": 500 }, { "id": 2, "name": "efg", "data1": 900, "data2": 600 }, { "id": 3, "name": "pqr", "data1": 1150, "data2": 300 }, { "id": 4, "name": "xyz", "data1": 1020, "data2": 900 } ] ================================================ FILE: htdocs/data/c3_test_ts.csv ================================================ x,data1,data2,data3 2012-12-31,120,80,200 2013-01-01,140,50,210 2013-01-02,170,100,250 2013-01-03,150,70,300 2013-01-04,180,120,280 ================================================ FILE: htdocs/index.html ================================================

# Other Library

================================================ FILE: htdocs/js/require.js ================================================ /* RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. Available via the MIT or new BSD license. see: http://github.com/jrburke/requirejs for details */ var requirejs,require,define; (function(ca){function G(b){return"[object Function]"===M.call(b)}function H(b){return"[object Array]"===M.call(b)}function v(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(G(c)){if(this.events.error&&this.map.isDefine||h.onError!==da)try{f=i.execCb(b,c,e,f)}catch(d){a=d}else f=i.execCb(b,c,e,f);this.map.isDefine&&void 0===f&&((e=this.module)?f=e.exports:this.usingExports&& (f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=c;this.exports=f;if(this.map.isDefine&&!this.ignore&&(p[b]=f,h.onResourceLoad))h.onResourceLoad(i,this.map,this.depMaps);y(b);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= this.map,b=a.id,d=m(a.prefix);this.depMaps.push(d);r(d,"defined",t(this,function(f){var d,g;g=j(ba,this.map.id);var J=this.map.name,u=this.map.parentMap?this.map.parentMap.name:null,p=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(J=f.normalize(J,function(a){return c(a,u,!0)})||""),f=m(a.prefix+"!"+J,this.map.parentMap),r(f,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),g=j(k,f.id)){this.depMaps.push(f); if(this.events.error)g.on("error",t(this,function(a){this.emit("error",a)}));g.enable()}}else g?(this.map.url=i.nameToUrl(g),this.load()):(d=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),d.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(k,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),d.fromText=t(this,function(f,c){var g=a.name,J=m(g),k=O;c&&(f=c);k&&(O=!1);q(J);s(l.config,b)&&(l.config[g]=l.config[b]);try{h.exec(f)}catch(j){return w(C("fromtexteval", "fromText eval for "+b+" failed: "+j,j,[b]))}k&&(O=!0);this.depMaps.push(J);i.completeLoad(g);p([g],d)}),f.load(a.name,p,d,l))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){W[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,t(this,function(a,b){var c,f;if("string"===typeof a){a=m(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=j(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;r(a,"defined",t(this,function(a){this.defineDep(b, a);this.check()}));this.errback&&r(a,"error",t(this,this.errback))}c=a.id;f=k[c];!s(N,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,t(this,function(a){var b=j(k,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:l,contextName:b,registry:k,defined:p,urlFetched:T,defQueue:A,Module:$,makeModuleMap:m, nextTick:h.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=l.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(l[b]||(l[b]={}),V(l[b],a,!0,!0)):l[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(ba[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a);b[c]=a}),l.shim=b);a.packages&&v(a.packages,function(a){var b, a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(l.paths[b]=a.location);l.pkgs[b]=a.name+"/"+(a.main||"main").replace(ja,"").replace(R,"")});B(k,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=m(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ca,arguments));return b||a.exports&&ea(a.exports)}},makeRequire:function(a,e){function g(f,c,d){var j,l;e.enableBuildCallback&&(c&&G(c))&&(c.__requireJsBuild= !0);if("string"===typeof f){if(G(c))return w(C("requireargs","Invalid require call"),d);if(a&&s(N,f))return N[f](k[a.id]);if(h.get)return h.get(i,f,a,g);j=m(f,a,!1,!0);j=j.id;return!s(p,j)?w(C("notloaded",'Module name "'+j+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[j]}L();i.nextTick(function(){L();l=q(m(null,a));l.skipMap=e.skipMap;l.init(f,c,d,{enabled:!0});D()});return g}e=e||{};V(g,{isBrowser:z,toUrl:function(b){var e,d=b.lastIndexOf("."),g=b.split("/")[0];if(-1!== d&&(!("."===g||".."===g)||1g.attachEvent.toString().indexOf("[native code"))&&!Z?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)): (g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,L=g,D?y.insertBefore(g,D):y.appendChild(g),L=null,g;if(fa)try{importScripts(d),b.completeLoad(c)}catch(j){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,j,[c]))}};z&&!r.skipDataMain&&U(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(K=b.getAttribute("data-main"))return q=K,r.baseUrl||(E=q.split("/"),q=E.pop(),Q=E.length?E.join("/")+"/":"./",r.baseUrl= Q),q=q.replace(R,""),h.jsExtRegExp.test(q)&&(q=K),r.deps=r.deps?r.deps.concat(q):[q],!0});define=function(b,c,d){var g,h;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(la,"").replace(ma,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(O){if(!(g=L))P&&"interactive"===P.readyState||U(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),g=P;g&&(b|| (b=g.getAttribute("data-requiremodule")),h=F[g.getAttribute("data-requirecontext")])}(h?h.defQueue:S).push([b,c,d])};define.amd={jQuery:!0};h.exec=function(b){return eval(b)};h(r)}})(this); ================================================ FILE: htdocs/js/samples/plugin.js ================================================ c3.chart.internal.fn.isTimeSeries = function () { console.log('custom isTimeSeries'); return false; }; c3.chart.internal.fn.additionalConfig.test1 = undefined; c3.chart.internal.fn.additionalConfig.test2 = undefined; c3.chart.fn.hoge = function () { console.log("hoge()", this.internal.isTimeSeries()); }; c3.chart.fn.test = function () { console.log('test()', this.internal.config.test1); }; ================================================ FILE: htdocs/js/samples/requirejs.js ================================================ require.config({ baseUrl: '/js', paths: { d3: "http://d3js.org/d3.v4.min" } }); require(["d3", "c3"], function(d3, c3) { window.chart = c3.generate({ data: { columns: [ ['sample', 30, 200, 100, 400, 150, 250] ] } }); }); ================================================ FILE: htdocs/js/samples/zoom_reduction.js ================================================ var chart; function refresh() { if (suspendRefresh) return; chart.load({ columns: [ ["Value"].concat(zoom(column, currentZoom, "t=>Math.round(t.avg())".toLambda())), ["xColumn"].concat(zoom(xColumn, currentZoom, "t=>t[0]".toLambda())), ] }); } function getChart() { return chart; } function main() { var last = 0; var max = 10000; var column = Array.generate(max, function (i) { return last += Math.randomInt(-10, 10); }); var xColumn = Array.generateNumbers(0, max); var options = { bindto: "#divChart", data: { columns: [ ["Value"].concat(column), ["x"].concat(xColumn), ], type: "line", x: "x" }, zoom2: { enabled: true, } }; chart = c3ext.generate(options); window.setInterval(refreshStatus, 1000); function refreshStatus() { var zoomInfo = chart.zoom2.getZoom(); var info = { reduced: chart.zoom2.maxItems(), actual: (zoomInfo.currentZoom[1] - zoomInfo.currentZoom[0]), range: zoomInfo.currentZoom[0] + "-" + zoomInfo.currentZoom[1], total: zoomInfo.totalItems }; $("#status").text(JSON.stringify(info, null, " ")); } }; if (typeof (Array.generate) == "undefined") { Array.generate = function (length, generator) { var list = new Array(length); for (var i = 0; i < length; i++) { list[i] = generator(i); } return list; } } if (typeof (Math.randomInt) == "undefined") { Math.randomInt = function (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } } if (typeof (Array.generateNumbers) == "undefined") { Array.generateNumbers = function (from, until) { if (arguments.length == 1) { until = from; from = 0; } var length = until - from; var list = new Array(length); for (var i = 0; i < length; i++) { list[i] = i + from; } return list; } } ================================================ FILE: htdocs/samples/api_axis_label.html ================================================
================================================ FILE: htdocs/samples/api_axis_range.html ================================================
================================================ FILE: htdocs/samples/api_category.html ================================================
================================================ FILE: htdocs/samples/api_data_colors.html ================================================
================================================ FILE: htdocs/samples/api_flow.html ================================================
================================================ FILE: htdocs/samples/api_flow_timeseries.html ================================================
================================================ FILE: htdocs/samples/api_legend.html ================================================
================================================ FILE: htdocs/samples/api_tooltip_show.html ================================================
================================================ FILE: htdocs/samples/api_transform.html ================================================
================================================ FILE: htdocs/samples/api_xgrid_lines.html ================================================
================================================ FILE: htdocs/samples/api_ygrid_lines.html ================================================
================================================ FILE: htdocs/samples/api_zoom.html ================================================
================================================ FILE: htdocs/samples/area_zerobased.html ================================================
================================================ FILE: htdocs/samples/axes_log_scales.html ================================================
================================================ FILE: htdocs/samples/axes_padding.html ================================================
================================================ FILE: htdocs/samples/axes_range.html ================================================
================================================ FILE: htdocs/samples/axes_x_localtime.html ================================================
================================================ FILE: htdocs/samples/axes_x_range_timeseries.html ================================================
================================================ FILE: htdocs/samples/axes_x_selection.html ================================================
================================================ FILE: htdocs/samples/axes_x_tick_culling.html ================================================
================================================ FILE: htdocs/samples/axes_x_tick_fit.html ================================================
================================================ FILE: htdocs/samples/axes_x_tick_rotate.html ================================================
================================================ FILE: htdocs/samples/axes_x_tick_values.html ================================================
================================================ FILE: htdocs/samples/axes_y2.html ================================================
================================================ FILE: htdocs/samples/axes_y_default.html ================================================
================================================ FILE: htdocs/samples/bar_zerobased.html ================================================
================================================ FILE: htdocs/samples/bindto.html ================================================
================================================ FILE: htdocs/samples/categorized.html ================================================
================================================ FILE: htdocs/samples/chart_arc.html ================================================
================================================ FILE: htdocs/samples/chart_area.html ================================================

Zerobased

Not zerobased because of axis.y.min

Zerobased

Not zerobased because of axis.y.min

+/- vaulues

================================================ FILE: htdocs/samples/chart_area_spline.html ================================================
================================================ FILE: htdocs/samples/chart_area_spline_stacked.html ================================================
================================================ FILE: htdocs/samples/chart_area_stacked.html ================================================
================================================ FILE: htdocs/samples/chart_area_step.html ================================================
================================================ FILE: htdocs/samples/chart_area_step_stacked.html ================================================
================================================ FILE: htdocs/samples/chart_bar.html ================================================
================================================ FILE: htdocs/samples/chart_bar_max_width.html ================================================
================================================ FILE: htdocs/samples/chart_bar_space.html ================================================
================================================ FILE: htdocs/samples/chart_bar_stacked.html ================================================
================================================ FILE: htdocs/samples/chart_bar_stacked_normalized.html ================================================
================================================ FILE: htdocs/samples/chart_combination.html ================================================
================================================ FILE: htdocs/samples/chart_combination_normalized.html ================================================
================================================ FILE: htdocs/samples/chart_donut.html ================================================
================================================ FILE: htdocs/samples/chart_gauge.html ================================================
================================================ FILE: htdocs/samples/chart_multi_arc_gauge.html ================================================
================================================ FILE: htdocs/samples/chart_pie.html ================================================
================================================ FILE: htdocs/samples/chart_pie_sort.html ================================================
================================================ FILE: htdocs/samples/chart_scatter.html ================================================
================================================ FILE: htdocs/samples/chart_spline.html ================================================
================================================ FILE: htdocs/samples/chart_stanford.html ================================================
================================================ FILE: htdocs/samples/chart_stanford_custom_elements.html ================================================
================================================ FILE: htdocs/samples/chart_step.html ================================================
================================================ FILE: htdocs/samples/chart_step_category.html ================================================
================================================ FILE: htdocs/samples/custom_x_categorized.html ================================================
================================================ FILE: htdocs/samples/custom_x_scale.html ================================================
================================================ FILE: htdocs/samples/custom_xs_scale.html ================================================
================================================ FILE: htdocs/samples/data_columned.html ================================================
================================================ FILE: htdocs/samples/data_hide.html ================================================
================================================ FILE: htdocs/samples/data_json.html ================================================
================================================ FILE: htdocs/samples/data_label.html ================================================
================================================ FILE: htdocs/samples/data_label_format.html ================================================
================================================ FILE: htdocs/samples/data_load.html ================================================
================================================ FILE: htdocs/samples/data_load_timeseries.html ================================================
================================================ FILE: htdocs/samples/data_region.html ================================================
================================================ FILE: htdocs/samples/data_region_timeseries.html ================================================
================================================ FILE: htdocs/samples/data_rowed.html ================================================
================================================ FILE: htdocs/samples/data_url.html ================================================
================================================ FILE: htdocs/samples/different_category_datasets.html ================================================
================================================ FILE: htdocs/samples/domain_y.html ================================================
================================================ FILE: htdocs/samples/element.html ================================================
================================================ FILE: htdocs/samples/emptydata.html ================================================
================================================ FILE: htdocs/samples/grid_focus.html ================================================
================================================ FILE: htdocs/samples/grid_x_lines.html ================================================
================================================ FILE: htdocs/samples/grid_x_lines_timeseries.html ================================================
================================================ FILE: htdocs/samples/grids.html ================================================
================================================ FILE: htdocs/samples/grids_timeseries.html ================================================
================================================ FILE: htdocs/samples/interaction_enabled.html ================================================
================================================ FILE: htdocs/samples/legend.html ================================================
================================================ FILE: htdocs/samples/padding.html ================================================
================================================ FILE: htdocs/samples/padding_update.html ================================================
================================================ FILE: htdocs/samples/plugin.html ================================================
================================================ FILE: htdocs/samples/point_r.html ================================================
================================================ FILE: htdocs/samples/point_show.html ================================================
================================================ FILE: htdocs/samples/regions.html ================================================
================================================ FILE: htdocs/samples/regions_timeseries.html ================================================
================================================ FILE: htdocs/samples/requirejs.html ================================================
================================================ FILE: htdocs/samples/resize.html ================================================

Please drag the window around to watch the chart resize.

This example illustrates the case which the issue #2467 reports.

================================================ FILE: htdocs/samples/selection.html ================================================ grouped => true, multiple => true
grouped => true, multiple => true, tooltip.grouped = false
grouped => true, multiple => false
grouped => true, multiple => false, tooltip.grouped = false
grouped => false, multiple => true
grouped => false, multiple => true, tooltip.grouped = false
grouped => false, multiple => false
grouped => false, multiple => false, tooltip.grouped = false
================================================ FILE: htdocs/samples/simple.html ================================================
================================================ FILE: htdocs/samples/subchart.html ================================================
================================================ FILE: htdocs/samples/subchart_onbrush.html ================================================
================================================ FILE: htdocs/samples/timeseries.html ================================================
================================================ FILE: htdocs/samples/timeseries_date.html ================================================
================================================ FILE: htdocs/samples/timeseries_descendent.html ================================================
================================================ FILE: htdocs/samples/timeseries_raw.html ================================================
================================================ FILE: htdocs/samples/tooltip_grouped.html ================================================
================================================ FILE: htdocs/samples/tooltip_horizontal.html ================================================
================================================ FILE: htdocs/samples/tooltip_show.html ================================================
================================================ FILE: htdocs/samples/zoom.html ================================================
================================================ FILE: htdocs/samples/zoom_category.html ================================================
================================================ FILE: htdocs/samples/zoom_onzoom.html ================================================
================================================ FILE: htdocs/samples/zoom_reduction.html ================================================ c3ext

C3 DataSet Reduction by Zoom Level

Hackathon May 2014

By Dan-el Khen

Rendering graphs in the browser has many advantages, the downside is that takes a long time to render when having large datasets.

This feature allows you reduces the dataset according to your current zoom level. It allows the developer to implement the reduction algorithm in a simple function that accepts an array of values, and returns a reduced single value. The default reducer will take the first item, but avg/sum/first/last or any other algorithm is simple to implement.

Example

In the following example, we'll render 10K data points, each time we'll reduce those to about 100 items (depending on available size on your screen), when zooming in, the resolution of the data will be better and more accurate. This would help in showing the big picture, even when the amount of data is bigger than the numbers of pixels on the screen.

Click on the buttons or scroll with your mouse wheel inside the graph to zoom and/or pan.


        

Notes

Only 'columns' data format is supported for now.

================================================ FILE: htdocs/samples/zoom_type.html ================================================

Zoom Type Scroll with Default Zoom Behavior

Zoom Type Drag with Default Zoom Behavior

================================================ FILE: htdocs/samples/zoom_type_disable_default_behavior.html ================================================

Zoom Type Scroll with Zoom Behavior Disabled (See the console)

Zoom Type Drag with Zoom Behavior Disabled (See the console)

================================================ FILE: karma.conf.js ================================================ const path = require('path') module.exports = config => config.set({ frameworks: ['jasmine', 'karma-typescript'], files: ['htdocs/css/c3.css', 'src/**/*.ts', 'spec/**/*'], preprocessors: { 'spec/**/*.ts': ['karma-typescript'], 'src/**/*.ts': ['karma-typescript'] }, karmaTypescriptConfig: { coverageOptions: { exclude: /spec/ }, reports: { lcov: 'coverage' } }, reporters: ['spec', 'karma-typescript'], browsers: ['Chrome'], singleRun: true }) ================================================ FILE: package.json ================================================ { "name": "c3", "version": "0.7.20", "description": "D3-based reusable chart library", "main": "c3.js", "files": [ "c3.js", "c3.min.js", "c3.esm.js", "c3.css", "c3.min.css", "src" ], "scripts": { "start": "run-p serve-static watch", "serve-static": "static -p 8080 htdocs/", "lint": "jshint --reporter=node_modules/jshint-stylish src/ spec/", "type": "tsc --noEmit", "fmt": "prettier --write rollup.config.js karma.conf.js \"src/**/*.ts\" \"spec/**/*.ts\" *.json", "docs": "bundle exec middleman", "build": "run-s build:js build:css", "build:js": "run-s build:js:rollup build:js:uglify", "build:js:rollup": "rollup -c", "build:js:uglify": "uglifyjs htdocs/js/c3.js --compress --mangle --comments -o htdocs/js/c3.min.js", "build:css": "run-s build:css:sass build:css:min", "build:css:sass": "sass src/scss/main.scss > htdocs/css/c3.css", "build:css:min": "cleancss -o htdocs/css/c3.min.css htdocs/css/c3.css", "build:docs": "bundle exec middleman build", "publish-docs": "npm run build:docs && gh-pages -d build -m \"chore: update gh-pages [skip ci]\"", "watch": "nodemon -e js,scss --watch src -x npm run build:js:rollup && npm run build:css:sass", "watch:js": "nodemon -e js --watch src --ignore src/scss -x 'npm run build:js:rollup'", "watch:css": "nodemon -e scss --watch src -x 'npm run build:css:sass'", "watch:docs": "bundle exec middleman", "karma": "karma start karma.conf.js", "test": "run-s build lint karma", "dist": "run-s build copy-to-root copy-to-docs", "copy-to-docs": "cp htdocs/js/c3.* docs/js/ && cp htdocs/css/c3.* docs/css/", "copy-to-root": "cp htdocs/{css,js}/c3.* ./", "codecov": "codecov" }, "repository": { "type": "git", "url": "git://github.com/c3js/c3.git" }, "keywords": [ "d3", "chart", "graph" ], "authors": [ "Masayuki Tanaka", "Ændrew Rininsland", "Yoshiya Hinosawa" ], "license": "MIT", "gitHead": "84e03109d9a590f9c8ef687c03d751f666080c6f", "readmeFilename": "README.md", "dependencies": { "d3": "^5.8.0" }, "devDependencies": { "@types/d3": "^5.7.2", "@types/jasmine": "^3.5.10", "clean-css-cli": "^4.1.11", "codecov": "^3.0.4", "gh-pages": "^5.0.0", "jasmine-core": "^2.3.4", "jshint": "^2.9.7", "jshint-stylish": "^2.1.0", "karma": "^6.3.14", "karma-chrome-launcher": "^3.0.0", "karma-jasmine": "^1.1.0", "karma-spec-reporter": "^0.0.32", "karma-typescript": "^5.0.3", "node-static": "^0.7.9", "nodemon": "^2.0.0", "npm-run-all": "^4.1.3", "prettier": "^1.19.1", "rollup": "^2.79.2", "rollup-plugin-typescript2": "^0.27.1", "sass": "^1.10.3", "status-back": "^1.1.0", "typescript": "^3.9.5", "uglify-js": "^3.6.4", "watchify": "^3.11.1" }, "nyc": { "exclude": [ "src/polyfill.js", "spec/" ] } } ================================================ FILE: rollup.config.js ================================================ import typescript from 'rollup-plugin-typescript2' import pkg from './package.json' export default [ { input: 'src/index.ts', output: { file: 'htdocs/js/c3.js', name: 'c3', format: 'umd', banner: `/* @license C3.js v${pkg.version} | (c) C3 Team and other contributors | http://c3js.org/ */`, globals: { d3: 'd3' } }, plugins: [typescript()], external: ['d3'] } ] ================================================ FILE: spec/api.axis-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api axis', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('axis.labels', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100], ['data2', 50, 20, 10] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y: { label: 'Y Axis Label' }, y2: { show: true, label: 'Y2 Axis Label' } } } }) it('updates y axis label', function() { chart.axis.labels({ y: 'New Y Axis Label' }) var label = d3.select('.c3-axis-y-label') expect(label.text()).toBe('New Y Axis Label') expect(label.attr('dx')).toBe('-0.5em') expect(label.attr('dy')).toBe('1.2em') }) it('updates y axis label', function() { chart.axis.labels({ y2: 'New Y2 Axis Label' }) var label = d3.select('.c3-axis-y2-label') expect(label.text()).toBe('New Y2 Axis Label') expect(label.attr('dx')).toBe('-0.5em') expect(label.attr('dy')).toBe('-0.5em') }) it('updates axis max values', function() { chart.axis.max({ x: 100, y: 300, y2: 100 }) var max_values = chart.axis.max() expect(max_values.x).toBe(100) expect(max_values.y).toBe(300) expect(max_values.y2).toBe(100) }) it('updates axis min values', function() { chart.axis.min({ x: 0, y: 20, y2: 50 }) var min_values = chart.axis.min() expect(min_values.x).toBe(0) expect(min_values.y).toBe(20) expect(min_values.y2).toBe(50) }) it('updates axis range', function() { chart.axis.range({ min: 5, max: 250 }) var range = chart.axis.range() expect(range.max.y).toBe(250) expect(range.min.y).toBe(5) }) }) describe('axis.types', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100], ['data2', 30, 200, 100] ], axes: { data1: 'y', data2: 'y2' } }, axis: { y: { label: 'Y Axis Label' }, y2: { show: true, type: 'log', label: 'Y2 Axis Label' } } } }) it('retrieves y/y2 axis types', function() { expect(chart.axis.types()).toEqual({ y: 'linear', y2: 'log' }) const linearDomain = chart.internal.y.domain() const logDomain = chart.internal.y2.domain() chart.axis.types({ y: 'log', y2: 'linear' }) expect(chart.internal.y2.domain()).toEqual(linearDomain) expect(chart.internal.y.domain()).toEqual(logDomain) expect(chart.axis.types()).toEqual({ y: 'log', y2: 'linear' }) }) }) }) ================================================ FILE: spec/api.data-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api data', function() { 'use strict' var chart var args: any = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 5000, 2000, 1000, 4000, 1500, 2500] ], names: { data1: 'Data Name 1', data2: 'Data Name 2' }, colors: { data1: '#FF0000', data2: '#00FF00' }, axes: { data1: 'y', data2: 'y2' } }, axis: { y2: { show: true } } } beforeEach(function(done) { jasmine.addMatchers(customMatchers as any) chart = initChart(chart, args, done) }) describe('data()', function() { it('should return all of data if no argument given', function() { var results = chart.data(), expected = ['data1', 'data2'] results.forEach(function(result, i) { expect(result.id).toBe(expected[i]) }) }) it('should return specified data if string argument given', function() { var results = chart.data('data1') expect(results.length).toBe(1) expect(results[0].id).toBe('data1') }) it('should return specified data if array argument given', function() { var results = chart.data(['data1', 'data2']) expect(results.length).toBe(2) expect(results[0].id).toBe('data1') expect(results[1].id).toBe('data2') }) }) describe('data.shown()', function() { it('should return only shown targets', function() { var results chart.hide('data1') results = chart.data.shown() expect(results.length).toBe(1) expect(results[0].id).toBe('data2') }) }) describe('data.values()', function() { it('should return values for specified target', function() { var values = chart.data.values('data1'), expectedValues = [30, 200, 100, 400, 150, 250] expect(values.length).toBe(6) values.forEach(function(v, i) { expect(v).toBe(expectedValues[i]) }) }) it('should return null when no args', function() { var values = chart.data.values() expect(values).toBeNull() }) }) describe('data.names()', function() { it('should return data.names specified as argument', function() { var results = chart.data.names() expect(results.data1).toBe('Data Name 1') expect(results.data2).toBe('Data Name 2') }) it('should return data.names specified as api', function() { var results = chart.data.names({ data1: 'New Data Name 1', data2: 'New Data Name 2' }) expect(results.data1).toBe('New Data Name 1') expect(results.data2).toBe('New Data Name 2') }) it('should set data.names specified as api', function() { expect(d3.select('.c3-legend-item-data1 text').text()).toBe( 'New Data Name 1' ) expect(d3.select('.c3-legend-item-data2 text').text()).toBe( 'New Data Name 2' ) }) }) describe('data.colors()', function() { it('should return data.colors specified as argument', function() { var results = chart.data.colors() ;(expect(results.data1) as any).toBeHexOrRGB('#FF0000') ;(expect(results.data2) as any).toBeHexOrRGB('#00FF00') }) it('should return data.colors specified as api', function() { var results = chart.data.colors({ data1: '#00FF00', data2: '#FF0000' }) ;(expect(results.data1) as any).toBeHexOrRGB('#00FF00') ;(expect(results.data2) as any).toBeHexOrRGB('#FF0000') }) it('should set data.colors specified as api', function() { ;(expect( d3.select('.c3-line-data1').style('stroke') ) as any).toBeHexOrRGB('#00ff00') ;(expect( d3.select('.c3-line-data2').style('stroke') ) as any).toBeHexOrRGB('#ff0000') ;(expect( d3.select('.c3-legend-item-data1 .c3-legend-item-tile').style('stroke') ) as any).toBeHexOrRGB('#00ff00') ;(expect( d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('stroke') ) as any).toBeHexOrRGB('#ff0000') }) }) describe('data.axes()', function() { it('should return data.axes specified as argument', function() { var results = chart.data.axes() expect(results.data1).toBe('y') expect(results.data2).toBe('y2') expect(d3.select('.c3-axis-y g.tick text').text()).toBe('0') expect(d3.select('.c3-axis-y2 g.tick text').text()).toBe('1000') }) it('should return data.axes specified as api', function() { var results = chart.data.axes({ data1: 'y2', data2: 'y' }) expect(results.data1).toBe('y2') expect(results.data2).toBe('y') expect(d3.select('.c3-axis-y g.tick text').text()).toBe('1000') expect(d3.select('.c3-axis-y2 g.tick text').text()).toBe('0') }) }) describe('data.stackNormalized()', function() { beforeEach(function(done) { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 500, 850, 1000, 200, 350, 100] ], groups: [['data1', 'data2']], stack: { normalize: true } } } chart = initChart(chart, args, done) }) it('can toggle option', function(done) { expect(chart.data.stackNormalized()).toBe(true) expect(chart.internal.y.domain()).toEqual([0, 100]) chart.data.stackNormalized(false) setTimeout(function() { expect(chart.data.stackNormalized()).toBe(false) expect(chart.internal.y.domain()).toEqual([0, 1200]) done() }, 100) }) }) }) describe('c3 api data.x', function() { 'use strict' var chart var args = { data: { x: 'x', columns: [ ['x', 10, 30, 45, 50, 70, 100], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) it('should return values for target data1', function() { var values = chart.data.values('data1'), expectedValues = [30, 200, 100, 400, 150, 250] expect(values.length).toBe(6) values.forEach(function(v, i) { expect(v).toBe(expectedValues[i]) }) }) it('should return null when no args', function() { var values = chart.data.values() expect(values).toBeNull() }) it('should return data values for data if string argument given', function() { var results = chart.data('data1') expect(results.length).toBe(1) expect(results[0].id).toBe('data1') }) it('should return specified data if array argument given', function() { var results = chart.data(['data1', 'data2']) expect(results.length).toBe(2) expect(results[0].id).toBe('data1') expect(results[1].id).toBe('data2') }) }) describe('c3 api data.xs', function() { 'use strict' var chart var args = { data: { xs: { data1: 'x1', data2: 'x2' }, columns: [ ['x1', 10, 30, 45, 50, 70, 100], ['x2', 30, 50, 75, 100, 120], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) it('should return values for target data1', function() { var values = chart.data.values('data1'), expectedValues = [30, 200, 100, 400, 150, 250] expect(values.length).toBe(6) values.forEach(function(v, i) { expect(v).toBe(expectedValues[i]) }) }) it('should return null when no args', function() { var values = chart.data.values() expect(values).toBeNull() }) it('should return data values for data if string argument given', function() { var results = chart.data('data1') expect(results.length).toBe(1) expect(results[0].id).toBe('data1') }) it('should return specified data if array argument given', function() { var results = chart.data(['data1', 'data2']) expect(results.length).toBe(2) expect(results[0].id).toBe('data1') expect(results[1].id).toBe('data2') }) }) var customMatchers = { toBeHexOrRGB: function(util, customEqualityTesters) { 'use strict' function rgb2hex(rgb) { rgb = rgb.match( /^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i ) return rgb && rgb.length === 4 ? '#' + ('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) + ('0' + parseInt(rgb[3], 10).toString(16)).slice(-2) : '' } return { compare: function(actual, expected) { if (expected === undefined) { expected = '' } var result: any = {} actual = actual.match('rgb') ? rgb2hex(actual) : actual expected = expected.match('rgb') ? rgb2hex(expected) : expected result.pass = util.equals(actual, expected, customEqualityTesters) if (result.pass) { result.message = 'Expected ' + actual + ' not to be quite so goofy' } else { result.message = 'Expected ' + actual + ' to be goofy, but it was not very goofy' } return result } } } } ================================================ FILE: spec/api.donut-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 api donut', function() { 'use strict' var chart, args args = { data: { columns: [ ['data1', 60], ['data2', 40] ], type: 'donut' }, donut: { padAngle: 0.5 } } beforeAll(function(done) { chart = initChart(chart, args, done) }) it('can configure padAngle', function(done) { expect(chart.donut.padAngle()).toBe(0.5) const path = chart.internal.main.select('.c3-arc-data1').attr('d') chart.donut.padAngle(0.2) setTimeout(function() { expect(chart.donut.padAngle()).toBe(0.2) expect(chart.internal.main.select('.c3-arc-data1').attr('d')).not.toBe( path ) done() }, 500) }) }) ================================================ FILE: spec/api.focus-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api focus', function() { 'use strict' var chart var args: any = { data: { columns: [ ['data1', 30, 200, 100, 400], ['data2', 1000, 800, 500, 2000], ['data3', 5000, 2000, 1000, 4000] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('focus', function() { it('should focus all targets', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-focused')).toBeTruthy() }) legendItems.each(function() { var item = d3.select(this) expect(item.classed('c3-legend-item-focused')).toBeTruthy() }) done() }, 1000) }) it('should focus one target', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus('data2') setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-focused')).toBeFalsy() expect(targets.data2.classed('c3-focused')).toBeTruthy() expect(targets.data3.classed('c3-focused')).toBeFalsy() expect(legendItems.data1.classed('c3-legend-item-focused')).toBeFalsy() expect(legendItems.data2.classed('c3-legend-item-focused')).toBeTruthy() expect(legendItems.data3.classed('c3-legend-item-focused')).toBeFalsy() done() }, 1000) }) it('should focus multiple targets', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus(['data1', 'data2']) setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-focused')).toBeTruthy() expect(targets.data2.classed('c3-focused')).toBeTruthy() expect(targets.data3.classed('c3-focused')).toBeFalsy() expect(legendItems.data1.classed('c3-legend-item-focused')).toBeTruthy() expect(legendItems.data2.classed('c3-legend-item-focused')).toBeTruthy() expect(legendItems.data3.classed('c3-legend-item-focused')).toBeFalsy() done() }, 1000) }) }) describe('defocus', function() { it('should defocus all targets', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-focused')).toBeFalsy() expect(line.classed('c3-defocused')).toBeTruthy() }) legendItems.each(function() { var item = d3.select(this) expect(item.classed('c3-legend-item-focused')).toBeFalsy() expect(+item.style('opacity')).toBeCloseTo(0.3) }) done() }, 1000) }) it('should defocus one target', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus('data2') setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-defocused')).toBeFalsy() expect(targets.data2.classed('c3-defocused')).toBeTruthy() expect(targets.data3.classed('c3-defocused')).toBeFalsy() expect(legendItems.data1.classed('c3-legend-item-focused')).toBeFalsy() expect(legendItems.data2.classed('c3-legend-item-focused')).toBeFalsy() expect(legendItems.data3.classed('c3-legend-item-focused')).toBeFalsy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(1) expect(+legendItems.data2.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data3.style('opacity')).toBeCloseTo(1) done() }, 1000) }) it('should defocus multiple targets', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus(['data1', 'data2']) setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-defocused')).toBeTruthy() expect(targets.data2.classed('c3-defocused')).toBeTruthy() expect(targets.data3.classed('c3-defocused')).toBeFalsy() expect(legendItems.data1.classed('c3-legend-item-focused')).toBeFalsy() expect(legendItems.data2.classed('c3-legend-item-focused')).toBeFalsy() expect(legendItems.data3.classed('c3-legend-item-focused')).toBeFalsy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data2.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data3.style('opacity')).toBeCloseTo(1) done() }, 1000) }) it('should defocus multiple targets after focused', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { chart.defocus(['data1', 'data2']) setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-defocused')).toBeTruthy() expect(targets.data2.classed('c3-defocused')).toBeTruthy() expect(targets.data3.classed('c3-defocused')).toBeFalsy() expect( legendItems.data1.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data2.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data3.classed('c3-legend-item-focused') ).toBeTruthy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data2.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data3.style('opacity')).toBeCloseTo(1) done() }, 1000) }, 1000) }) }) describe('revert', function() { it('should revert all targets after focus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { chart.revert() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-focused')).toBeFalsy() }) legendItems.each(function() { var item = d3.select(this) expect(item.classed('c3-legend-item-focused')).toBeFalsy() expect(+item.style('opacity')).toBeCloseTo(1) }) done() }, 1000) }, 1000) }) it('should revert all targets after defocus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus() setTimeout(function() { chart.revert() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-defocused')).toBeFalsy() }) legendItems.each(function() { var item = d3.select(this) expect(item.classed('c3-legend-item-focused')).toBeFalsy() expect(+item.style('opacity')).toBeCloseTo(1) }) done() }, 1000) }, 1000) }) it('should revert one target after focus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { chart.revert('data2') setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-focused')).toBeTruthy() expect(targets.data2.classed('c3-focused')).toBeFalsy() expect(targets.data3.classed('c3-focused')).toBeTruthy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(1) expect(+legendItems.data2.style('opacity')).toBeCloseTo(1) expect(+legendItems.data3.style('opacity')).toBeCloseTo(1) expect( legendItems.data1.classed('c3-legend-item-focused') ).toBeTruthy() expect( legendItems.data2.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data3.classed('c3-legend-item-focused') ).toBeTruthy() done() }, 1000) }, 1000) }) it('should revert one target after defocus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus() setTimeout(function() { chart.revert('data2') setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-defocused')).toBeTruthy() expect(targets.data2.classed('c3-defocused')).toBeFalsy() expect(targets.data3.classed('c3-defocused')).toBeTruthy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(0.3) expect(+legendItems.data2.style('opacity')).toBeCloseTo(1) expect(+legendItems.data3.style('opacity')).toBeCloseTo(0.3) expect( legendItems.data1.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data2.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data3.classed('c3-legend-item-focused') ).toBeFalsy() done() }, 1000) }, 1000) }) it('should focus multiple targets after focus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { chart.revert(['data1', 'data2']) setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-focused')).toBeFalsy() expect(targets.data2.classed('c3-focused')).toBeFalsy() expect(targets.data3.classed('c3-focused')).toBeTruthy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(1) expect(+legendItems.data2.style('opacity')).toBeCloseTo(1) expect(+legendItems.data3.style('opacity')).toBeCloseTo(1) expect( legendItems.data1.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data2.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data3.classed('c3-legend-item-focused') ).toBeTruthy() done() }, 1000) }, 1000) }) it('should focus multiple targets after defocus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus() setTimeout(function() { chart.revert(['data1', 'data2']) setTimeout(function() { var targets = { data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), data3: main.select('.c3-chart-line.c3-target.c3-target-data3') }, legendItems = { data1: legend.select('.c3-legend-item-data1'), data2: legend.select('.c3-legend-item-data2'), data3: legend.select('.c3-legend-item-data3') } expect(targets.data1.classed('c3-defocused')).toBeFalsy() expect(targets.data2.classed('c3-defocused')).toBeFalsy() expect(targets.data3.classed('c3-defocused')).toBeTruthy() expect(+legendItems.data1.style('opacity')).toBeCloseTo(1) expect(+legendItems.data2.style('opacity')).toBeCloseTo(1) expect(+legendItems.data3.style('opacity')).toBeCloseTo(0.3) expect( legendItems.data1.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data2.classed('c3-legend-item-focused') ).toBeFalsy() expect( legendItems.data3.classed('c3-legend-item-focused') ).toBeFalsy() done() }, 1000) }, 1000) }) }) describe('when legend.show = false', function() { beforeAll(function() { args.legend = { show: false } }) it('should focus all targets without showing legend', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-focused')).toBeTruthy() }) expect(legendItems.size()).toBeCloseTo(0) done() }, 1000) }) it('should defocus all targets without showing legend', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.defocus() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-defocused')).toBeTruthy() }) expect(legendItems.size()).toBeCloseTo(0) done() }, 1000) }) it('should revert all targets after focus', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.focus() setTimeout(function() { chart.revert() setTimeout(function() { var targets = main.select('.c3-chart-line.c3-target'), legendItems = legend.select('.c3-legend-item') targets.each(function() { var line = d3.select(this) expect(line.classed('c3-focused')).toBeFalsy() }) expect(legendItems.size()).toBeCloseTo(0) done() }, 1000) }, 1000) }) }) }) ================================================ FILE: spec/api.grid-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api grid', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('ygrid.add and ygrid.remove', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] } } }) it('updates y grids', function(done) { var main = chart.internal.main, expectedGrids = [ { value: 100, text: 'Pressure Low' }, { value: 200, text: 'Pressure High' } ], grids // Call ygrids.add chart.ygrids.add(expectedGrids) setTimeout(function() { grids = main.selectAll('.c3-ygrid-line') expect(grids.size()).toBe(expectedGrids.length) grids.each(function(d, i) { var y = +d3 .select(this) .select('line') .attr('y1'), text = d3 .select(this) .select('text') .text(), expectedY = Math.round(chart.internal.y(expectedGrids[i].value)), expectedText = expectedGrids[i].text expect(y).toBe(expectedY) expect(text).toBe(expectedText) }) // Call ygrids.remove chart.ygrids.remove(expectedGrids) setTimeout(function() { grids = main.selectAll('.c3-ygrid-line') expect(grids.size()).toBe(0) }, 500) }, 500) setTimeout(function() { done() }, 1200) }) it('updates x ygrids even if zoomed', function(done) { var main = chart.internal.main, expectedGrids = [ { value: 0, text: 'Pressure Low' }, { value: 1, text: 'Pressure High' } ], grids, domain chart.zoom([0, 2]) setTimeout(function() { // Call xgrids chart.xgrids(expectedGrids) setTimeout(function() { grids = main.selectAll('.c3-xgrid-line') expect(grids.size()).toBe(expectedGrids.length) grids.each(function(d, i) { var x = +d3 .select(this) .select('line') .attr('x1'), text = d3 .select(this) .select('text') .text(), expectedX = Math.round(chart.internal.x(expectedGrids[i].value)), expectedText = expectedGrids[i].text expect(x).toBe(expectedX) expect(text).toBe(expectedText) }) // check if it was not rescaled domain = chart.internal.y.domain() expect(domain[0]).toBeLessThan(0) expect(domain[1]).toBeGreaterThan(400) // Call xgrids.remove chart.xgrids.remove(expectedGrids) setTimeout(function() { grids = main.selectAll('.c3-xgrid-line') expect(grids.size()).toBe(0) }, 500) // for xgrids.remove() }, 500) // for xgrids() }, 500) // for zoom setTimeout(function() { done() }, 1700) }) }) }) ================================================ FILE: spec/api.load-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api load', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('indexed data', function() { describe('as column', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 5000, 2000, 1000, 4000, 1500, 2500] ] } } }) it('should load additional data', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.load({ columns: [['data3', 800, 500, 900, 500, 1000, 700]] }) setTimeout(function() { var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), legendItem = legend.select('.c3-legend-item.c3-legend-item-data3') expect(target.size()).toBe(1) expect(legendItem.size()).toBe(1) done() }, 500) }) }) }) describe('category data', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', 'cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6'], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 5000, 2000, 1000, 4000, 1500, 2500] ] }, axis: { x: { type: 'category' } } } }) describe('as column', function() { it('should load additional data', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.load({ columns: [['data3', 800, 500, 900, 500, 1000, 700]] }) setTimeout(function() { var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), legendItem = legend.select('.c3-legend-item.c3-legend-item-data3'), tickTexts = main.selectAll('.c3-axis-x g.tick text'), expected = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6'] expect(target.size()).toBe(1) expect(legendItem.size()).toBe(1) tickTexts.each(function(d, i) { var text = d3 .select(this) .select('tspan') .text() expect(text).toBe(expected[i]) }) done() }, 500) }) it('should load additional data', function(done) { var main = chart.internal.main, legend = chart.internal.legend chart.load({ columns: [ ['x', 'new1', 'new2', 'new3', 'new4', 'new5', 'new6'], ['data3', 800, 500, 900, 500, 1000, 700] ] }) setTimeout(function() { var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), legendItem = legend.select('.c3-legend-item.c3-legend-item-data3'), tickTexts = main.selectAll('.c3-axis-x g.tick text'), expected = ['new1', 'new2', 'new3', 'new4', 'new5', 'new6'] expect(target.size()).toBe(1) expect(legendItem.size()).toBe(1) tickTexts.each(function(d, i) { var text = d3 .select(this) .select('tspan') .text() expect(text).toBe(expected[i]) }) done() }, 500) }) }) }) }) ================================================ FILE: spec/api.pie-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 api pie', function() { 'use strict' var chart, args args = { data: { columns: [ ['data1', 60], ['data2', 40] ], type: 'pie' }, pie: { padAngle: 0.5 } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('padAngle', function() { it('can configure padAngle', function(done) { expect(chart.pie.padAngle()).toBe(0.5) const path = chart.internal.main.select('.c3-arc-data1').attr('d') chart.pie.padAngle(0.2) setTimeout(function() { expect(chart.pie.padAngle()).toBe(0.2) expect(chart.internal.main.select('.c3-arc-data1').attr('d')).not.toBe( path ) done() }, 500) }) }) describe('load data', function() { beforeAll(() => { args = { data: { columns: ['r', 'l', 'd'], type: 'pie', colors: { r: 'red', l: 'green', d: 'blue' } } } }) it('can add data point to existing data', () => { chart.load({ columns: [ ['r', 75], ['l', 20], ['d', 5] ] }) expect(chart.internal.getTotalDataSum()).toEqual(100) expect(chart.internal.main.selectAll('.c3-arc').size()).toEqual(3) }) }) }) ================================================ FILE: spec/api.region-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api region', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('api.region', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, regions: [ { axis: 'y', start: 300, end: 400, class: 'green', label: 'Region 1' }, { axis: 'y', start: 0, end: 100, class: 'green' } ] } }) it('updates regions', function(done) { var main = chart.internal.main, expectedRegions = [ { axis: 'y', start: 250, end: 350, class: 'red', label: 'Region 1' }, { axis: 'y', start: 25, end: 75, class: 'red', label: '' } ], regions // Call regions API chart.regions(expectedRegions) setTimeout(function() { regions = main.selectAll('.c3-region') expect(regions.size()).toBe(expectedRegions.length) regions.each(function(d, i) { var region = d3.select(this), label = region.select('text'), y = +region.attr('y'), height = +region.attr('height'), expectedClass = 'red', expectedLabel = expectedRegions[i].label, unexpectedClass = 'green', expectedStart = Math.round( chart.internal.y(expectedRegions[i].start) ), expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), expectedY = expectedEnd, expectedHeight = expectedStart - expectedEnd expect(y).toBeCloseTo(expectedY, -1) expect(height).toBeCloseTo(expectedHeight, -1) expect(region.classed(expectedClass)).toBeTruthy() expect(region.classed(unexpectedClass)).toBeFalsy() expect(label.text()).toBe(expectedLabel) }) }, 500) setTimeout(function() { done() }, 1000) }) }) describe('api.region.add', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, regions: [ { axis: 'y', start: 300, end: 400, class: 'green' }, { axis: 'y', start: 0, end: 100, class: 'green' } ] } }) it('should add regions', function(done) { var main = chart.internal.main, expectedRegions = [ { axis: 'y', start: 300, end: 400, class: 'green' }, { axis: 'y', start: 0, end: 100, class: 'green' }, { axis: 'y', start: 250, end: 350, class: 'red' }, { axis: 'y', start: 25, end: 75, class: 'red' } ], expectedClasses = ['green', 'green', 'red', 'red'], regions // Call regions API chart.regions(expectedRegions) setTimeout(function() { regions = main.selectAll('.c3-region') expect(regions.size()).toBe(expectedRegions.length) regions.each(function(d, i) { var region = d3.select(this), y = +region.attr('y'), height = +region.attr('height'), expectedClass = expectedClasses[i], expectedStart = Math.round( chart.internal.y(expectedRegions[i].start) ), expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), expectedY = expectedEnd, expectedHeight = expectedStart - expectedEnd expect(y).toBeCloseTo(expectedY, -1) expect(height).toBeCloseTo(expectedHeight, -1) expect(region.classed(expectedClass)).toBeTruthy() }) }, 500) setTimeout(function() { done() }, 1000) }) }) describe('api.region.remove', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, regions: [ { axis: 'y', start: 300, end: 400, class: 'green' }, { axis: 'y', start: 0, end: 100, class: 'green' }, { axis: 'y', start: 250, end: 350, class: 'red' } ] } }) it('should remove regions', function(done) { var main = chart.internal.main, expectedRegions = [ { axis: 'y', start: 250, end: 350, class: 'red' } ], expectedClasses = ['red'], regions // Call regions API chart.regions(expectedRegions) setTimeout(function() { regions = main.selectAll('.c3-region') expect(regions.size()).toBe(expectedRegions.length) regions.each(function(d, i) { var region = d3.select(this), y = +region.attr('y'), height = +region.attr('height'), expectedClass = expectedClasses[i], expectedStart = Math.round( chart.internal.y(expectedRegions[i].start) ), expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), expectedY = expectedEnd, expectedHeight = expectedStart - expectedEnd expect(y).toBeCloseTo(expectedY, -1) expect(height).toBeCloseTo(expectedHeight, -1) expect(region.classed(expectedClass)).toBeTruthy() }) }, 500) setTimeout(function() { done() }, 1000) }) }) }) ================================================ FILE: spec/api.x-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 api.x', function() { 'use strict' var chart var args = { data: { x: 'x', columns: [ ['x', 10, 30, 45, 50, 70, 100], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) it('should return initial ticks for axis x', function() { var expectedValues = [10, 30, 45, 50, 70, 100] d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d, i) { var text = d3 .select(this) .select('text') .text() expect(+text).toBe(expectedValues[i]) }) }) it('should return new ticks for axis x after calling chart.x', function() { var expectedValues = [16, 26, 55, 60, 75, 90] chart.x(expectedValues) d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d, i) { var text = d3 .select(this) .select('text') .text() expect(+text).toBe(expectedValues[i]) }) }) }) describe('c3 api.xs', function() { 'use strict' var chart var args = { data: { xs: { data1: 'x1', data2: 'x2' }, columns: [ ['x1', 10, 30, 50, 70, 90, 110], ['x2', 20, 40, 60, 80, 100], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) it('should return initial ticks for axis x', function() { var expectedValues = [ '10', '20', '30', '40', '50', '60', '70', '80', '90', '100', '110' ] d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d, i) { var text = d3 .select(this) .select('text') .text() expect(text).toBe(expectedValues[i]) }) }) it('should return new ticks for axis x after calling chart.xs', function() { var expectedValues = [ '15', '25', '35', '45', '55', '65', '75', '85', '95', '105', '115' ] chart.xs({ data1: [15, 35, 55, 75, 95, 115], data2: [25, 45, 65, 85, 105] }) d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d, i) { var text = d3 .select(this) .select('text') .text() expect(text).toBe(expectedValues[i]) }) }) }) ================================================ FILE: spec/api.zoom-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 api zoom', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('zoom', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] }, zoom: { enabled: true } } }) it('should return the correct extent', function() { var zoomDomain = chart.zoom(), expectedDomain = chart.internal.x.domain() expect(+zoomDomain[0]).toBe(+expectedDomain[0]) expect(+zoomDomain[1]).toBe(+expectedDomain[1]) }) it('should be zoomed properly', function() { var target = [3, 5], domain chart.zoom(target) domain = chart.internal.x.domain() expect(domain[0]).toBe(target[0]) expect(domain[1]).toBe(target[1]) }) it('should be zoomed properly again', function() { var target = [1, 4], domain chart.zoom(target) domain = chart.internal.x.domain() expect(domain[0]).toBe(target[0]) expect(domain[1]).toBe(target[1]) }) it('should set the max zoom properly', function() { chart.zoom.max(100) expect(chart.zoom.max()).toBe(100) }) it('should set the min zoom properly', function() { chart.zoom.min(-1) expect(chart.zoom.min()).toBe(-1) }) describe('with timeseries data', function() { beforeAll(function() { args = { data: { x: 'date', columns: [ ['date', '2014-01-01', '2014-01-02', '2014-08-01', '2014-10-19'], ['data1', 30, 200, 100, 400] ] }, axis: { x: { type: 'timeseries' } }, zoom: { enabled: true } } }) it('should be zoomed properly', function() { var target = [new Date(2014, 7, 1), new Date(2014, 8, 1)], domain chart.zoom(target) domain = chart.internal.x.domain() expect(+domain[0]).toBe(+target[0]) expect(+domain[1]).toBe(+target[1]) }) it('should be zoomed properly', function() { var target = ['2014-08-01', '2014-09-01'], domain chart.zoom(target) domain = chart.internal.x.domain() expect(+domain[0]).toBe(+chart.internal.parseDate(target[0])) expect(+domain[1]).toBe(+chart.internal.parseDate(target[1])) }) }) }) describe('unzoom', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, zoom: { enabled: true } } }) it('should be unzoomed properly', function() { var target = [1, 4], orginal = chart.internal.x.domain(), domain chart.zoom(target) domain = chart.internal.x.domain() expect(domain[0]).toBe(target[0]) expect(domain[1]).toBe(target[1]) chart.unzoom() domain = chart.internal.x.domain() expect(domain[0]).toBe(orginal[0]) expect(domain[1]).toBe(orginal[1]) }) }) }) ================================================ FILE: spec/arc-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart arc', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('unloads correctly', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30], ['data2', 150], ['data3', 120] ], type: 'pie' }, transition: { duration: 500 } } }) it('unloads without error', function(done) { chart.load({ columns: [['data2', 30, 20, 50, 40, 60, 50]] }) setTimeout(function() { chart.destroy() }, 200) setTimeout(function() { expect(chart.internal.config).toBeNull() done() }, 501) }) }) describe('show pie chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30], ['data2', 150], ['data3', 120] ], type: 'pie' } } }) it('should have correct classes', function() { var chartArc = d3.select('.c3-chart-arcs'), arcs = { data1: chartArc .select('.c3-chart-arc.c3-target.c3-target-data1') .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), data2: chartArc .select('.c3-chart-arc.c3-target.c3-target-data2') .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), data3: chartArc .select('.c3-chart-arc.c3-target.c3-target-data3') .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') } expect(arcs.data1.size()).toBe(1) expect(arcs.data2.size()).toBe(1) expect(arcs.data3.size()).toBe(1) }) it('should have correct d', function() { expect(d3.select('.c3-arc-data1').attr('d')).toMatch( /M-124\..+,-171\..+A211\..+,211\..+,0,0,1,-3\..+,-211\..+L0,0Z/ ) expect(d3.select('.c3-arc-data2').attr('d')).toMatch( /M1\..+,-211\..+211\..+,211\..+,0,0,1,1\..+,211\..+L0,0Z/ ) expect(d3.select('.c3-arc-data3').attr('d')).toMatch( /M1\..+,211\..+211\..+,211\..+,0,0,1,-124\..+,-171\..+L0,0Z/ ) }) describe('with data id that can be converted to a color', function() { beforeAll(function() { args.data.columns = [ ['black', 30], ['data2', 150], ['data3', 120] ] }) it('should have correct d even if data id can be converted to a color', function(done) { setTimeout(function() { expect(d3.select('.c3-arc-black').attr('d')).toMatch( /M-124\..+,-171\..+A211\..+,211\..+,0,0,1,-3\..+,-211\..+L0,0Z/ ) done() }, 500) }) describe('with empty pie chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', null], ['data2', null], ['data3', null] ], type: 'pie' } } }) it('should have correct d attribute', function() { var chartArc = d3.select('.c3-chart-arcs'), arcs = { data1: chartArc .select('.c3-chart-arc.c3-target.c3-target-data1') .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), data2: chartArc .select('.c3-chart-arc.c3-target.c3-target-data2') .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), data3: chartArc .select('.c3-chart-arc.c3-target.c3-target-data3') .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') } expect(arcs.data1.attr('d').indexOf('NaN')).toBe(-1) expect(arcs.data2.attr('d').indexOf('NaN')).toBe(-1) expect(arcs.data3.attr('d').indexOf('NaN')).toBe(-1) }) }) }) }) describe('sort pie chart', function() { var createPie = function(order) { return { data: { order: order, columns: [ ['data1', 30], ['data2', 150], ['data3', 120] ], type: 'pie' } } } var collectArcs = function() { return d3 .selectAll('.c3-arc') .data() .sort(function(a: any, b: any) { return a.startAngle - b.startAngle }) .map(function(item: any) { return item.data.id }) } it('should update data_order to desc', function() { args = createPie('desc') expect(true).toBeTruthy() }) it('it should have descending ordering', function() { expect(collectArcs()).toEqual(['data2', 'data3', 'data1']) }) it('should update data_order to asc', function() { args = createPie('asc') expect(true).toBeTruthy() }) it('it should have ascending ordering', function() { expect(collectArcs()).toEqual(['data1', 'data3', 'data2']) }) it('should update data_order to NULL', function() { args = createPie(null) expect(true).toBeTruthy() }) it('it should have no ordering', function() { expect(collectArcs()).toEqual(['data1', 'data2', 'data3']) }) it('should update data_order to Array', function() { args = createPie(['data3', 'data2', 'data1']) expect(true).toBeTruthy() }) it('it should have array specified ordering', function() { expect(collectArcs()).toEqual(['data3', 'data2', 'data1']) }) it('should update data_order to Function', function() { var names = ['data2', 'data1', 'data3'] args = createPie(function(a, b) { return names.indexOf(a.id) - names.indexOf(b.id) }) expect(true).toBeTruthy() }) it('it should have array specified ordering', function() { expect(collectArcs()).toEqual(['data2', 'data1', 'data3']) }) }) describe('config donut chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30], ['data2', 150], ['data3', 120] ], type: 'donut' }, donut: { padAngle: 0.05 } } }) it('can configure padAngle', function() { expect(chart.internal.pie.padAngle()()).toBe(0.05) }) }) describe('config pie chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30], ['data2', 150], ['data3', 120] ], type: 'pie' }, pie: { padAngle: 0.05 } } }) it('can configure padAngle', function() { expect(chart.internal.pie.padAngle()()).toBe(0.05) }) }) describe('show gauge', function() { describe('with a 180 degree gauge', function() { beforeAll(function() { args = { gauge: { width: 10, max: 10, expand: true }, data: { columns: [['data', 8]], type: 'gauge' } } }) it('should have correct d for Pi radian gauge', function() { var chartArc = d3.select('.c3-chart-arcs'), data = chartArc .select('.c3-chart-arc.c3-target.c3-target-data') .select('g.c3-shapes.c3-shapes-data.c3-arcs.c3-arcs-data') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data') expect(data.attr('d')).toMatch( /-258.4,-3\..+A258.4,258.4,0,0,1,209\..+,-151\..+L200\..+,-146\..+A248.39999999999998,248.39999999999998,0,0,0,-248.39999999999998,-3\..+Z/ ) }) }) describe('with a 2 Pi radian gauge that starts at Pi/2', function() { beforeAll(function() { args = { gauge: { width: 10, max: 10, expand: true, fullCircle: true, startingAngle: Math.PI / 2 }, data: { columns: [['data', 8]], type: 'gauge' } } }) it('should have correct d for 2 Pi radian gauge starting at Pi/2', function() { var chartArc = d3.select('.c3-chart-arcs'), data = chartArc .select('.c3-chart-arc.c3-target.c3-target-data') .select('g.c3-shapes.c3-shapes-data.c3-arcs.c3-arcs-data') .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data') expect(data.attr('d')).toMatch(/^M180/) }) describe('with labels use custom text', function() { beforeAll(function() { args = { gauge: { width: 10, max: 100, expand: true, label: { extents: function(value, isMax) { if (isMax) { return 'Max: ' + value + '%' } return 'Min: ' + value + '%' } } }, data: { columns: [['data', 8]], type: 'gauge' } } }) it('should show custom min/max guage labels', function() { var chartArc = d3.select('.c3-chart-arcs'), min = chartArc.select('.c3-chart-arcs-gauge-min'), max = chartArc.select('.c3-chart-arcs-gauge-max') expect(min.text()).toMatch('Min: 0%') expect(max.text()).toMatch('Max: 100%') }) }) }) describe('with more than one data_column ', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 100], ['padded2', 90], ['padded3', 50], ['padded4', 20] ], type: 'gauge' }, color: { pattern: ['#FF0000', '#F97600', '#F6C600', '#60B044'], threshold: { values: [30, 80, 95] } } } }) var arcColor = [ 'rgb(96, 176, 68)', 'rgb(246, 198, 0)', 'rgb(249, 118, 0)', 'rgb(255, 0, 0)' ] describe('should contain arcs ', function() { it('each data_column should have one arc', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc') .each(function(d, i) { expect( d3.select(this).classed('c3-arc-' + args.data.columns[i][0]) ).toBeTruthy() }) }) it('each arc should have the color from color_pattern if color_treshold is given ', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc') .each(function(d, i) { expect(d3.select(this).style('fill')).toBe(arcColor[i]) }) }) }) describe('should contain backgrounds ', function() { it('each data_column should have one background', function() { chart.internal.main .selectAll('.c3-chart-arcs path.c3-chart-arcs-background') .each(function(d, i) { expect( d3.select(this).classed('c3-chart-arcs-background-' + i) ).toBeTruthy() }) }) it('each background should have tbe same color', function() { chart.internal.main .selectAll('.c3-chart-arcs path.c3-chart-arcs-background') .each(function() { expect(d3.select(this).style('fill')).toBe('rgb(224, 224, 224)') }) }) }) describe('should contain labels', function() { it('each data_column should have a label', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-gauge-value') .each(function(d, i) { expect(d3.select(this).text()).toBe( chart.internal.defaultArcValueFormat( null, args.data.columns[i][1] / 100 ) ) }) }) it('each label should have the same color', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-gauge-value') .each(function() { expect(d3.select(this).style('fill')).toBe('rgb(0, 0, 0)') }) }) it('if only one data_column is visible the label should have "" for transform', function(done) { var textBeforeHide = chart.internal.main.select( '.c3-chart-arc.c3-target.c3-target-padded4 text' ) expect(textBeforeHide.attr('transform')).not.toBe('') chart.hide(['padded1', 'padded2', 'padded3']) setTimeout(function() { var textAfterHide = chart.internal.main.select( '.c3-chart-arc.c3-target.c3-target-padded4 text' ) expect(textAfterHide.attr('transform')).toBe('') done() }, 1000) }) }) describe('should contain labellines', function() { it('each data_column should have a labelline', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc-label-line') .each(function(d, i) { expect( d3.select(this).classed('c3-target-' + args.data.columns[i][0]) ).toBeTruthy() }) }) it('each labelline should have the color from color_pattern if color_treshold is given', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc-label-line') .each(function(d, i) { expect(d3.select(this).style('fill')).toBe(arcColor[i]) }) }) }) }) describe('with more than one data value ', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 40, 60], ['padded2', 100, -10], ['padded3', 0, 50], ['padded4', 20, 0] ], type: 'gauge' }, color: { pattern: ['#FF0000', '#F97600', '#F6C600', '#60B044'], threshold: { values: [30, 80, 95] } } } }) var arcColor = [ 'rgb(96, 176, 68)', 'rgb(246, 198, 0)', 'rgb(249, 118, 0)', 'rgb(255, 0, 0)' ] describe('should contain arcs ', function() { it('each data_column should have one arc', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc') .each(function(d, i) { expect( d3.select(this).classed('c3-arc-' + args.data.columns[i][0]) ).toBeTruthy() }) }) it('each arc should have the color from color_pattern if color_treshold is given ', function() { chart.internal.main .selectAll('.c3-chart-arc .c3-arc') .each(function(d, i) { expect(d3.select(this).style('fill')).toBe(arcColor[i]) }) }) }) }) }) }) ================================================ FILE: spec/axis-spec.ts ================================================ import { d3, c3, initChart } from './c3-helper' describe('c3 chart axis', function() { 'use strict' var chart var args: any = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] }, axis: { y: { tick: { values: null, count: undefined } }, y2: { tick: { values: null, count: undefined } } } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('axis.y.tick.count', function() { describe('with only 1 tick on y axis', function() { beforeAll(function() { args.axis.y.tick.count = 1 }) it('should have only 1 tick on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(1) }) }) describe('with 2 ticks on y axis', function() { beforeAll(function() { args.axis.y.tick.count = 2 }) it('should have 2 ticks on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(2) }) }) describe('with 3 ticks on y axis', function() { beforeAll(function() { args.axis.y.tick.count = 3 }) it('should have 3 ticks on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(3) }) }) }) describe('axis.y.tick.values', function() { var values = [100, 500] describe('with only 2 ticks on y axis', function() { beforeAll(function() { args.axis.y.tick.values = values }) it('should have only 2 tick on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(2) }) it('should have specified tick texts', function() { d3.select('.c3-axis-y') .selectAll('g.tick') .each(function(d, i) { var text = d3 .select(this) .select('text') .text() expect(+text).toBe(values[i]) }) }) }) }) describe('axis x timeseries with seconds', function() { beforeAll(function() { args = { data: { type: 'line', columns: [ ['epoch', 1401879600000, 1401883200000, 1401886800000], ['y', 1955, 2419, 2262] ], xs: { y: 'epoch' } }, axis: { x: { type: 'timeseries', min: new Date(1401879600000), max: new Date(1401969600000), localtime: false } } } }) it('should have 3 ticks on x axis', function() { var ticksSize = d3 .select('.c3-axis-x') .selectAll('g.tick') .size() expect(ticksSize).toBe(3) }) it('should have specified 1 hour intervals', function() { var prevValue d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d: any, i) { if (i !== 0) { var result = d - prevValue expect(result).toEqual(3600000) // expressed in milliseconds } prevValue = d }) }) describe('changing min x time and columns', function() { beforeAll(function() { args.axis.x.min = new Date(1401876000000) args.axis.x.max = new Date(1401876075000) args.data.columns = [ [ 'epoch', 1401876000000, 1401876015000, 1401876030000, 1401876045000, 1401876060000, 1401876075000 ], ['y', 1968, 1800, 1955, 2419, 2262, 1940] ] }) it('should have 6 ticks on x axis', function() { var ticksSize = d3 .select('.c3-axis-x') .selectAll('g.tick') .size() expect(ticksSize).toBe(6) // the count starts at initial value and increments by the set interval }) it('should have specified 15 seconds intervals', function() { var prevValue d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d: any, i) { if (i !== 0) { var result = d - prevValue expect(result).toEqual(15000) // expressed in milliseconds } prevValue = d }) }) describe('with axis.x.time.format %Y-%m-%d %H:%M:%S', function() { beforeAll(function() { args.axis.x.tick = { format: '%M:%S' // https://github.com/mbostock/d3/wiki/Time-Formatting#wiki-format } }) var textDates = ['00:00', '00:15', '00:30', '00:45', '01:00', '01:15'] it('should format x ticks as dates with time', function() { var ticks = d3 .select('.c3-axis-x') .selectAll('g.tick') .selectAll('tspan') .each(function(d: any) { expect(d.splitted).toEqual(textDates[d.index]) }) expect(ticks.size()).toBe(6) }) }) }) }) describe('axis x timeseries with iso dates', function() { beforeAll(function() { args = { data: { type: 'line', columns: [ ['epoch', 1527811200000, 1527897600000, 1527984000000], ['y', 1955, 2419, 2262] ], xs: { y: 'epoch' } }, axis: { x: { type: 'timeseries', min: new Date('2018-06-01'), max: new Date('2018-06-03'), localtime: false, tick: { format: '%Y-%m-%dT%H:%M:%S' // https://github.com/mbostock/d3/wiki/Time-Formatting#wiki-format } } } } }) var textDates = [ '2018-06-01T00:00:00', '2018-06-02T00:00:00', '2018-06-03T00:00:00' ] it('should format x ticks as dates', function() { var ticks = d3 .select('.c3-axis-x') .selectAll('g.tick') .selectAll('tspan') .each(function(d: any) { expect(d.splitted).toEqual(textDates[d.index]) }) expect(ticks.size()).toBe(3) }) }) describe('axis y timeseries', function() { beforeAll(function() { args = { data: { columns: [['times', 60000, 120000, 180000, 240000]] }, axis: { y: { type: 'timeseries', tick: { time: {} } } } } }) it('should have 7 ticks on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(7) // the count starts at initial value and increments by the set interval }) it('should have specified 30 second intervals', function() { var prevValue d3.select('.c3-axis-y') .selectAll('g.tick') .each(function(d: any, i) { if (i !== 0) { var result = d - prevValue expect(result).toEqual(30000) // expressed in milliseconds } prevValue = d }) }) describe('with axis.y.time', function() { beforeAll(function() { args.axis.y.tick.time = { type: d3.timeSecond, interval: 60 } }) it('should have 4 ticks on y axis', function() { var ticksSize = d3 .select('.c3-axis-y') .selectAll('g.tick') .size() expect(ticksSize).toBe(4) // the count starts at initial value and increments by the set interval }) it('should have specified 60 second intervals', function() { var prevValue d3.select('.c3-axis-y') .selectAll('g.tick') .each(function(d: any, i) { if (i !== 0) { var result = d - prevValue expect(result).toEqual(60000) // expressed in milliseconds } prevValue = d }) }) }) }) describe('axis.y.type', function() { describe('type=log', function() { beforeAll(function() { args = { data: { columns: [ ['linear', 318, 37, 0, 4, 0, 1], ['log', 318, 37, 0, 4, 0, 1] ], type: 'bar', axes: { log: 'y', linear: 'y2' }, labels: true }, axis: { y: { type: 'log' }, y2: { show: true } } } }) it('should have bars from y bigger than y2', function() { expect( (d3.select('.c3-bars-log .c3-bar-5').node() as any).getBBox().height ).toBeGreaterThan( (d3.select('.c3-bars-linear .c3-bar-5').node() as any).getBBox() .height ) }) it('should not have truncated data label', () => { const text = d3.select('.c3-texts-log .c3-text-0').node() as any expect(text).not.toBeUndefined() const bbox = text.getBBox() expect(Math.abs(bbox.y) - bbox.height).toBeGreaterThan(0) }) }) }) describe('axis.x.tick.values', function() { describe('formatted correctly when negative', function() { var xValues = [-3.3, -2.2, -1.1, 1.1, 2.2, 3.3] beforeEach(function() { args.data = { x: 'x', columns: [ ['x'].concat(xValues as any), ['data1', 30, 200, 100, 400, 150, 250] ] } }) it('should not generate whole number for negative values', function() { var tickValues = [] d3.select('.c3-axis-x') .selectAll('g.tick') .selectAll('tspan') .each(function(d: any, i) { expect(tickValues.push(parseFloat(d.splitted)) === xValues[i]) }) }) }) describe('function is provided', function() { var tickGenerator = function() { var values = [] for (var i = 0; i <= 300; i += 50) { values.push(i) } return values } beforeEach(function() { args.axis.x = { tick: { values: tickGenerator } } chart = c3.generate(args) ;(window as any).generatedTicks = tickGenerator() // This should be removed from window }) it('should use function to generate ticks', function() { d3.select('.c3-axis-x') .selectAll('g.tick') .each(function(d, i) { var tick = d3 .select(this) .select('text') .text() expect(+tick).toBe((window as any).generatedTicks[i]) }) }) }) }) describe('axis.x.tick.width', function() { describe('indexed x axis and y/y2 axis', function() { describe('not rotated', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], axes: { data2: 'y2' } }, axis: { y2: { show: true } } } }) it('should construct indexed x axis properly', function() { var ticks = chart.internal.main .select('.c3-axis-x') .selectAll('g.tick'), expectedX = '0', expectedDy = '.71em' expect(ticks.size()).toBe(6) ticks.each(function(d, i) { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.text()).toBe(i + '') expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) }) }) describe('should set axis.x.tick.format', function() { beforeAll(function() { args.axis.x = { tick: { format: function() { return 'very long tick text on x axis' } } } }) it('should split x axis tick text to multiple lines', function() { var ticks = chart.internal.main .select('.c3-axis-x') .selectAll('g.tick'), expectedTexts = ['very long tick text', 'on x axis'], expectedX = '0' expect(ticks.size()).toBe(6) ticks.each(function() { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(2) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTexts[i]) expect(tspan.attr('x')).toBe(expectedX) if (i === 0) { expect(tspan.attr('dy')).toBe('.71em') } else { expect(tspan.attr('dy')).toBeGreaterThan(8) } }) }) }) it('should construct y axis properly', function() { var ticks = chart.internal.main .select('.c3-axis-y') .selectAll('g.tick'), expectedX = '-9', expectedDy = '3' expect(ticks.size()).toBe(9) ticks.each(function(d) { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.text()).toBe(d + '') expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) }) }) it('should construct y2 axis properly', function() { var ticks = chart.internal.main .select('.c3-axis-y2') .selectAll('g.tick'), expectedX = '9', expectedDy = '3' expect(ticks.size()).toBe(9) ticks.each(function(d) { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.text()).toBe(d + '') expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) }) }) }) describe('should set big values in y', function() { beforeAll(function() { args.data.columns = [ ['data1', 3000000000000000, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }) it('should not split y axis tick text to multiple lines', function() { var ticks = chart.internal.main .select('.c3-axis-y2') .selectAll('g.tick') ticks.each(function() { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(1) }) }) }) }) describe('rotated', function() { beforeAll(function() { args.axis.rotated = true }) it('should split x axis tick text to multiple lines', function() { var ticks = chart.internal.main .select('.c3-axis-x') .selectAll('g.tick'), expectedTexts = ['very long tick text on', 'x axis'], expectedX = '-9' expect(ticks.size()).toBe(6) ticks.each(function() { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(2) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTexts[i]) expect(tspan.attr('x')).toBe(expectedX) if (i === 0) { expect(tspan.attr('dy')).toBeLessThan(0) } else { expect(tspan.attr('dy')).toBeGreaterThan(9) } }) }) }) it('should not split y axis tick text to multiple lines', function() { var ticks = chart.internal.main .select('.c3-axis-y') .selectAll('g.tick'), expectedTexts = [ '0', '500000000000000', '1000000000000000', '1500000000000000', '2000000000000000', '2500000000000000', '3000000000000000' ], expectedX = '0', expectedDy = '.71em' expect(ticks.size()).toBe(7) ticks.each(function(d, i) { var tspans = d3.select(this).selectAll('tspan') expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTexts[i]) expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) }) }) }) }) describe('category axis', function() { describe('not rotated', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ [ 'x', 'this is a very long tick text on category axis', 'cat1', 'cat2', 'cat3', 'cat4', 'cat5' ], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { x: { type: 'category' } } } }) it('should locate ticks properly', function() { var ticks = chart.internal.main .select('.c3-axis-x') .selectAll('g.tick') ticks.each(function(d, i) { var tspans = d3.select(this).selectAll('tspan'), expectedX = '0', expectedDy = '.71em' if (i > 0) { // i === 0 should be checked in next test expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) } }) }) xit('should split tick text properly', function() { var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), tspans = tick.selectAll('tspan'), expectedTickTexts = [ 'this is a very long', 'tick text on category', 'axis' ], expectedX = '0' expect(tspans.size()).toBe(3) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTickTexts[i]) expect(tspan.attr('x')).toBe(expectedX) // unable to define pricise number because it differs depends on environment.. if (i === 0) { expect(tspan.attr('dy')).toBe('.71em') } else { expect(tspan.attr('dy')).toBeGreaterThan(8) } }) }) }) describe('rotated', function() { beforeAll(function() { args.axis.rotated = true }) it('should locate ticks on rotated axis properly', function() { var ticks = chart.internal.main .select('.c3-axis-x') .selectAll('g.tick') ticks.each(function(d, i) { var tspans = d3.select(this).selectAll('tspan'), expectedX = '-9', expectedDy = '3' if (i > 0) { // i === 0 should be checked in next test expect(tspans.size()).toBe(1) tspans.each(function() { var tspan = d3.select(this) expect(tspan.attr('x')).toBe(expectedX) expect(tspan.attr('dy')).toBe(expectedDy) }) } }) }) it('should split tick text on rotated axis properly', function() { var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), tspans = tick.selectAll('tspan'), expectedTickTexts = [ 'this is a very long', 'tick text on category', 'axis' ], expectedX = '-9' expect(tspans.size()).toBe(3) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTickTexts[i]) expect(tspan.attr('x')).toBe(expectedX) // unable to define pricise number because it differs depends on environment.. if (i === 0) { expect(tspan.attr('dy')).toBeLessThan(0) } else { expect(tspan.attr('dy')).toBeGreaterThan(8) } }) }) }) describe('option used', function() { describe('as null', function() { beforeAll(function() { //'without split ticks', args.axis.x.tick = { multiline: false } }) it('should split x tick', function() { var tick = chart.internal.main .select('.c3-axis-x') .select('g.tick'), tspans = tick.selectAll('tspan') expect(tspans.size()).toBe(1) }) }) describe('as value', function() { beforeAll(function() { // 'without split ticks', args.axis.x.tick = { width: 150 } }) it('should split x tick to 2 lines properly', function() { var tick = chart.internal.main .select('.c3-axis-x') .select('g.tick'), tspans = tick.selectAll('tspan'), expectedTickTexts = [ 'this is a very long tick text on', 'category axis' ], expectedX = '-9' expect(tspans.size()).toBe(2) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTickTexts[i]) expect(tspan.attr('x')).toBe(expectedX) // unable to define pricise number because it differs depends on environment.. if (i === 0) { expect(tspan.attr('dy')).toBeLessThan(0) } else { expect(tspan.attr('dy')).toBeGreaterThan(8) } }) }) }) describe('with multilineMax', function() { beforeAll(function() { args.axis.x.tick = { multiline: true, multilineMax: 2 } }) it('should ellipsify x tick properly', function() { var tick = chart.internal.main.select('.c3-axis-x').select('g.tick') var tspans = tick.selectAll('tspan') var expectedTickText = [ 'this is a very long', 'tick text on categ...' ] expect(tspans.size()).toBe(2) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTickText[i]) }) }) }) }) }) describe('with axis.x.tick.format', function() { beforeAll(function() { // 'with axis.x.tick.format', args.axis.x.tick.format = function() { return ['this is a very long tick text', 'on category axis'] } }) it('should have multiline tick text', function() { var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), tspans = tick.selectAll('tspan'), expectedTickTexts = [ 'this is a very long tick text', 'on category axis' ] expect(tspans.size()).toBe(2) tspans.each(function(d, i) { var tspan = d3.select(this) expect(tspan.text()).toBe(expectedTickTexts[i]) }) }) }) }) describe('axis.x.tick.rotate', function() { describe('not rotated', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ [ 'x', 'category 1', 'category 2', 'category 3', 'category 4', 'category 5', 'category 6' ], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { x: { type: 'category', tick: { rotate: 60 } } } } }) it('should rotate tick texts', function() { chart.internal.main.selectAll('.c3-axis-x g.tick').each(function() { var tick = d3.select(this), text = tick.select('text'), tspan = text.select('tspan') expect(text.attr('transform')).toBe('rotate(60)') expect(text.attr('y')).toBe('1.5') expect(tspan.attr('dx')).toBe('6.928203230275509') }) }) it('should have automatically calculated x axis height', function() { var box = chart.internal.main .select('.c3-axis-x') .node() .getBoundingClientRect(), height = chart.internal.getHorizontalAxisHeight('x') expect(box.height).toBeGreaterThan(50) expect(height).toBeCloseTo(76, -1.3) // @TODO make this test better }) }) }) describe('axis.y.tick.rotate', function() { describe('not rotated', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250, 100, 600], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { rotated: true, y: { tick: { rotate: 45 } } } } }) it('should rotate tick texts', function() { chart.internal.main.selectAll('.c3-axis-y g.tick').each(function() { var tick = d3.select(this), text = tick.select('text'), tspan = text.select('tspan') expect(text.attr('transform')).toBe('rotate(45)') expect(text.attr('y')).toBe('4') expect(tspan.attr('dx')).toBeCloseTo(5.6, 0) }) }) it('should have automatically calculated y axis width', function() { var box = chart.internal.main .select('.c3-axis-y') .node() .getBoundingClientRect() expect(box.width).toBeCloseTo(590, 1) }) }) }) describe('axis.x.tick.fit', function() { describe('axis.x.tick.fit = true', function() { beforeAll(function() { // 'should set args for indexed data', args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } } }) it('should show fitted ticks on indexed data', function() { var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(6) }) describe('should set args for x-based data', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', 10, 20, 100, 110, 200, 1000], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } } }) it('should show fitted ticks on indexed data', function() { var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(6) }) it('should show fitted ticks after hide and show', function() { chart.hide() chart.show() var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(6) }) }) }) describe('axis.x.tick.fit = false', function() { describe('should set args for indexed data', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] }, axis: { x: { tick: { fit: false } } } } }) it('should show fitted ticks on indexed data', function() { var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(11) }) }) describe('should set args for x-based data', function() { beforeAll(function() { args.data = { x: 'x', columns: [ ['x', 10, 20, 100, 110, 200, 1000], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } }) it('should show fitted ticks on indexed data', function() { var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(10) }) it('should show fitted ticks after hide and show', function() { chart.hide() chart.show() var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick') expect(ticks.size()).toBe(10) }) }) }) }) describe('axis.y.inner', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { y: { inner: false } } } }) it('should not have inner y axis', function() { var paddingLeft = chart.internal.getCurrentPaddingLeft(), tickTexts = chart.internal.main.selectAll('.c3-axis-y g.tick text') expect(paddingLeft).toBeGreaterThan(19) tickTexts.each(function() { expect(+d3.select(this).attr('x')).toBeLessThan(0) }) }) describe('with inner y axis', function() { beforeAll(function() { args.axis.y.inner = true }) it('should have inner y axis', function() { var paddingLeft = chart.internal.getCurrentPaddingLeft(), tickTexts = chart.internal.main.selectAll('.c3-axis-y g.tick text') expect(paddingLeft).toBe(1) tickTexts.each(function() { expect(+d3.select(this).attr('x')).toBeGreaterThan(0) }) }) }) }) describe('axis.y2.inner', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { y2: { show: true, inner: false } } } }) it('should not have inner y axis', function() { var paddingRight = chart.internal.getCurrentPaddingRight(), tickTexts = chart.internal.main.selectAll('.c3-axis-2y g.tick text') expect(paddingRight).toBeGreaterThan(19) tickTexts.each(function() { expect(+d3.select(this).attr('x')).toBeGreaterThan(0) }) }) describe('with inner y axis', function() { beforeAll(function() { args.axis.y2.inner = true }) it('should have inner y axis', function() { var paddingRight = chart.internal.getCurrentPaddingRight(), tickTexts = chart.internal.main.selectAll('.c3-axis-2y g.tick text') expect(paddingRight).toBe(2) tickTexts.each(function() { expect(+d3.select(this).attr('x')).toBeLessThan(0) }) }) }) }) describe('axis.x.label', function() { beforeAll(function() { args = { data: { columns: [ ['somewhat long 1', 30, 200, 100, 400, 150, 250], ['somewhat long 2', 50, 20, 10, 40, 15, 25] ] }, axis: { x: { show: true, label: { text: 'Label of X axis' } } } } }) it('renders label text properly', () => { expect(d3.select('.c3-axis-x-label').text()).toEqual('Label of X axis') }) describe('outer label position', function() { beforeAll(function() { args.axis.x.label.position = 'outer-center' }) it('renders position properly', () => { const label = d3.select('.c3-axis-x-label') expect(label.attr('dy')).toEqual('30') }) describe('with rotated tick', function() { beforeAll(function() { args.axis.x.tick = { rotate: 90 } }) it('renders position properly', () => { const label = d3.select('.c3-axis-x-label') expect(label.attr('dy')).toBeGreaterThan(30) }) }) }) describe('inner label position', function() { beforeAll(function() { args.axis.x.label.position = 'inner-center' }) it('renders position properly', () => { const label = d3.select('.c3-axis-x-label') expect(label.attr('dy')).toEqual('-0.5em') }) describe('with rotated tick', function() { beforeAll(function() { args.axis.x.tick = { rotate: 90 } }) it('renders position properly', () => { const label = d3.select('.c3-axis-x-label') expect(label.attr('dy')).toEqual('-0.5em') }) }) }) }) }) ================================================ FILE: spec/c3-helper.ts ================================================ import c3 from '../src/index' ;(window as any).c3 = c3 const d3 = (window.d3 = require('d3')) const initDom = ((window as any).initDom = function() { var div = document.createElement('div') div.id = 'chart' div.style.width = '640px' div.style.height = '480px' document.body.appendChild(div) document.body.style.margin = '0px' }) const setMouseEvent = ((window as any).setMouseEvent = function( chart, name, x, y, element ) { var paddingLeft = chart.internal.main.node().transform.baseVal.getItem(0) .matrix.e, event = document.createEvent('MouseEvents') event.initMouseEvent( name, true, true, window, 0, 0, 0, x + paddingLeft, y + 5, false, false, false, false, 0, null ) if (element) { element.dispatchEvent(event) } }) const initChart = ((window as any).initChart = function(chart, args, done) { if (typeof chart === 'undefined') { initDom() } if (args) { chart = c3.generate(args) window.d3 = chart.internal.d3 window.d3 .select('.jasmine_html-reporter') .style('position', 'absolute') .style('width', '640px') .style('right', 0) // when using Karma debug in browser the `window.chart` reference the DOM element // instead of the actual chart instance here so let's keep it here ;(window as any).chartInstance = chart } window.setTimeout(function() { done() }, 10) return chart }) export { d3, c3, initDom, setMouseEvent, initChart } ================================================ FILE: spec/cache-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 cache', function() { 'use strict' var chart var args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) it('returns undefined for unknown values', function() { expect(chart.internal.getFromCache('undefined')).toBeUndefined() }) it('returns cached value', function() { chart.internal.addToCache('cached', 1) expect(chart.internal.getFromCache('cached')).toEqual(1) chart.internal.addToCache('cached', { x: 1 }) expect(chart.internal.getFromCache('cached')).toEqual({ x: 1 }) }) it('can clear cached values', function() { chart.internal.addToCache('cached', 1) expect(chart.internal.getFromCache('cached')).toEqual(1) chart.internal.resetCache() expect(chart.internal.getFromCache('cached')).toBeUndefined() }) }) ================================================ FILE: spec/class-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 chart class', function() { 'use strict' var chart var args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2 prefix', 50, 20, 10, 40, 15, 25], ['data3 мужчины', 150, 120, 110, 140, 115, 125], ['my\u007fapp', 10, 20, 40, 20, 65, 55] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('internal.generateTargetClass', function() { it('should not replace any characters', function() { var input = 'data1', expected = '-' + input, suffix = chart.internal.generateTargetClass(input) expect(suffix).toBe(expected) }) it('should replace space to "-"', function() { var input = 'data1 suffix', expected = '-data1-suffix', suffix = chart.internal.generateTargetClass(input) expect(suffix).toBe(expected) }) it('should replace space to "-" with multibyte characters', function() { var input = 'data1 suffix 日本語', expected = '-data1-suffix-日本語', suffix = chart.internal.generateTargetClass(input) expect(suffix).toBe(expected) }) it('should not replace special characters', function() { var input = 'data1 !@#$%^&*()_=+,.<>"\':;[]/|?~`{}\\', expected = '-data1-!@#$%^&*()_=+,.<>"\':;[]/|?~`{}\\', suffix = chart.internal.generateTargetClass(input) expect(suffix).toBe(expected) }) }) describe('internal.getTargetSelectorSuffix', function() { it('should escape special characters', function() { var input = 'data1 !@#$%^&*()_=+,.<>"\':;[]/|?~`{}\\', expected = '-data1-\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)_\\=\\+\\,\\.\\<\\>\\"\\\'\\:\\;\\[\\]\\/\\|\\?\\~\\`\\{\\}\\\\', suffix = chart.internal.getTargetSelectorSuffix(input) expect(suffix).toBe(expected) }) }) describe('select target in chart', function() { it('should replace space to "-" with multibyte characters', function() { var selector = '.c3-target-data3-мужчины' expect(chart.internal.main.select(selector).size()).toBe(1) }) it('should be able to select class with unicode characters', () => { const selector = `.c3-target${chart.internal.getTargetSelectorSuffix( args.data.columns[3][0] )}` expect(chart.internal.main.select(selector).size()).toBe(1) }) }) }) ================================================ FILE: spec/core-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart', function() { 'use strict' var chart var args: any = { svg: { classname: 'customclass' }, data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('init', function() { it('should be created', function() { var svg = d3.select('#chart svg') expect(svg).not.toBeNull() }) it('should bind to window focus event', done => { const addEventListener = window.addEventListener window.addEventListener = (event, handler) => { if (event === 'focus') { setTimeout(() => { expect(handler).toBe(chart.internal.windowFocusHandler) window.addEventListener = addEventListener // restore the original done() }, 10) } } chart = initChart(chart, args, () => {}) }) describe('should set 3rd party property to Function', function() { beforeAll(function() { ;(Function.prototype as any).$extIsFunction = true }) it('should be created even if 3rd party property has been set', function() { var svg = d3.select('#chart svg') expect(svg).not.toBeNull() }) it('should be created with a custom class', function() { var svg = d3.select('#chart svg') expect(svg.attr('class')).not.toBeNull() expect(svg.attr('class')).toBe('customclass') }) }) }) describe('size', function() { it('should have same width', function() { var svg = d3.select('#chart svg') expect(+svg.attr('width')).toBe(640) }) it('should have same height', function() { var svg = d3.select('#chart svg') expect(+svg.attr('height')).toBe(480) }) }) describe('call resize and resized callbacks', function() { beforeAll(function() { args.bindto = '#chart' args.axis = { rotated: true } args.resize_var = false args.resized_var = false args.onresize = function() { args.resize_var = true } args.onresized = function() { args.resized_var = true } }) it('arbitrary parameters should be false before resize', function() { expect(args.resize_var).toBe(false) expect(args.resized_var).toBe(false) }) it('arbitrary parameters should be true after resize', function() { window.dispatchEvent(new Event('resize')) expect(args.resize_var).toBe(true) expect(args.resized_var).toBe(true) }) }) describe('bindto', function() { describe('selector', function() { beforeAll(function() { d3.select('#chart').html('') args.bindto = '#chart' }) it('should be created', function() { var svg = d3.select('#chart svg') expect(svg.size()).toBe(1) }) }) describe('d3.selection object', function() { beforeAll(function() { d3.select('#chart').html('') args.bindto = d3.select('#chart') }) it('should be created', function() { var svg = d3.select('#chart svg') expect(svg.size()).toBe(1) }) }) describe('null', function() { beforeAll(function() { d3.select('#chart').html('') args.bindto = null }) it('should not be created', function() { var svg = d3.select('#chart svg') expect(svg.size()).toBe(0) }) }) describe('empty string', function() { beforeAll(function() { d3.select('#chart').html('') args.bindto = '' }) it('should not be created', function() { var svg = d3.select('#chart svg') expect(svg.size()).toBe(0) }) }) describe('bind to selector with rotated axis', function() { beforeAll(function() { args.bindto = '#chart' args.axis = { rotated: true } }) it('should be created', function() { var svg = d3.select('#chart svg') expect(svg.size()).toBe(1) }) }) }) describe('empty data', function() { beforeAll(function() { args = { data: { columns: [['data1'], ['data2']] } } }) it('should generate a chart', function() { var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick') expect(ticks.size()).toBe(0) }) describe('more empty data', function() { beforeAll(function() { args = { data: { x: 'x', columns: [['x'], ['data1'], ['data2']] }, axis: { x: { type: 'timeseries' } } } }) it('should generate a chart', function() { var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick') expect(ticks.size()).toBe(0) }) }) }) }) ================================================ FILE: spec/data-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('load without data', function() { var chart, args beforeAll(function() { args = { data: {} } }) it('throws when data is an empty object', () => { expect(() => initChart(chart, args, () => {})).toThrowError( Error, /url or json or rows or columns is required/ ) }) }) describe('c3 chart data', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('load json', function() { beforeAll(function() { args = { data: { json: { data1: [30, 20, 50], data2: [200, 130, 90] } } } }) it('should draw correctly', function() { var expectedCx = [6, 299, 593], expectedCy = [371, 391, 332] d3.selectAll('.c3-circles-data1 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[i], 0) }) }) describe('more data', function() { beforeAll(function() { args = { data: { json: [ { date: '2014-06-03', '443': '3000', '995': '500' }, { date: '2014-06-04', '443': '1000' }, { date: '2014-06-05', '443': '5000', '995': '1000' } ], keys: { x: 'date', value: ['443', '995'] } }, axis: { x: { type: 'category' } } } }) it('should draw correctly', function() { var expectedCx = { 443: [98, 294, 490], 995: [98, 294, 490] }, expectedCy = { 443: [194, 351, 36], 995: [391, 430, 351] } d3.selectAll('.c3-circles-443 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[443][i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[443][i], 0) }) d3.selectAll('.c3-circles-995 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[995][i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[995][i], 0) }) }) }) describe('with nested JSON args', function() { beforeAll(function() { args = { data: { json: [ { date: '2014-06-03', '443': '3000', '995': { '996': '500' }, '112': ['600'], '223': [{ '224': '100' }], '334': [[], [{ '335': '300' }]], '556': { '557': { '558': ['1000'] } }, '778.889': '700' }, { date: '2014-06-04', '443': '1000', '112': ['700'], '223': [{ '224': '200' }], '556': { '557': { '558': ['2000'] } }, '778.889': '300' }, { date: '2014-06-05', '995': { '996': '1000' }, '112': ['800'], '223': [{ '224': '300' }], '443': '5000', '334': [[], [{ '335': '500' }]], '556': { '557': { '558': ['3000'] } }, '778.889': '800' } ], keys: { x: 'date', value: [ '443', '995.996', '112[0]', '223[0].224', '334[1][0].335', '556.557.558[0]', '778.889' ] } }, axis: { x: { type: 'category' } } } }) it('should draw nested JSON correctly', function() { var expectedCx = [98, 294, 490], expectedCy = { 443: [181, 326, 36], 995: [362, 398, 326], 112: [354, 347, 340], 223: [391, 383, 376], 334: [376, 398, 362], 556: [326, 253, 181], '778.889': [347, 376, 340] } d3.selectAll('.c3-circles-443 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[443][i], 0) }) d3.selectAll('.c3-circles-995-996 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[995][i], 0) }) d3.selectAll('.c3-circles-112-0- .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[112][i], 0) }) d3.selectAll('.c3-circles-223-0--224 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[223][i], 0) }) d3.selectAll('.c3-circles-334-1--0--335 .c3-circle').each(function( d, i ) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[334][i], 0) }) d3.selectAll('.c3-circles-556-557-558-0- .c3-circle').each(function( d, i ) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[556][i], 0) }) d3.selectAll('.c3-circles-778-889 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy['778.889'][i], 0) }) }) }) }) describe('load rows', function() { beforeAll(function() { args = { data: { rows: [ ['data1', 'data2', 'data3'], [90, 120, 300], [40, 160, 240], [50, 200, 290], [120, 160, 230], [80, 130, 300], [90, 220, 320] ] } } }) it('should draw correctly', function() { var expectedCx = [6, 124, 241, 358, 475, 593], expectedCy = [327, 391, 378, 289, 340, 327] d3.selectAll('.c3-circles-data1 .c3-circle').each(function(d, i) { var circle = d3.select(this) expect(+circle.attr('cx')).toBeCloseTo(expectedCx[i], 0) expect(+circle.attr('cy')).toBeCloseTo(expectedCy[i], 0) }) }) }) describe('function in data.order', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ], order: function() { return 0 } } } }) it('should return false in isOrderAsc and isOrderDesc functions', function() { expect(chart.internal.isOrderAsc() || chart.internal.isOrderDesc()).toBe( false ) }) }) describe('addHiddenTargetIds if not already hidden', function() { it('should update args', function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 150, 120, 110, 140, 115, 125] ] } } expect(true).toBeTruthy() }) it('length of hiddenTargetIds should not change if same key added twice', function() { chart.internal.addHiddenTargetIds('data1') expect(chart.internal.hiddenTargetIds.length).toBe(1) chart.internal.addHiddenTargetIds('data1') expect(chart.internal.hiddenTargetIds.length).toBe(1) chart.hide('data1') expect(chart.internal.hiddenTargetIds.length).toBe(1) chart.internal.addHiddenTargetIds('data2') expect(chart.internal.hiddenTargetIds.length).toBe(2) chart.show() chart.hide(['data1', 'data2']) expect(chart.internal.hiddenTargetIds.length).toBe(2) chart.show() chart.hide() expect(chart.internal.hiddenTargetIds.length).toBe(2) }) }) describe('addHiddenLegendIds if not already hidden', function() { it('should update args', function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 150, 120, 110, 140, 115, 125] ] } } expect(true).toBeTruthy() }) it('length of hiddenLegendIds should not change if same key added twice', function() { chart.internal.addHiddenLegendIds('data1') expect(chart.internal.hiddenLegendIds.length).toBe(1) chart.internal.addHiddenLegendIds('data1') expect(chart.internal.hiddenLegendIds.length).toBe(1) chart.hide('data1', { withLegend: true }) expect(chart.internal.hiddenLegendIds.length).toBe(1) chart.hide('data2', { withLegend: true }) expect(chart.internal.hiddenLegendIds.length).toBe(2) chart.show(['data1', 'data2'], { withLegend: true }) chart.hide(['data1', 'data2'], { withLegend: true }) expect(chart.internal.hiddenLegendIds.length).toBe(2) }) }) describe('data.xs', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ] } } }) describe('normal x', function() { it('should have correct number of xs for each', function() { expect(Object.keys(chart.internal.data.xs).length).toBe(3) expect(chart.internal.data.xs.data1.length).toBe(6) expect(chart.internal.data.xs.data2.length).toBe(6) expect(chart.internal.data.xs.data3.length).toBe(6) }) it('should have integer index as x', function() { for (var i = 0; i < chart.internal.data.xs.data3.length; i++) { expect(chart.internal.data.xs.data1[i]).toBe(i) expect(chart.internal.data.xs.data2[i]).toBe(i) expect(chart.internal.data.xs.data3[i]).toBe(i) } }) }) describe('timeseries x', function() { describe('without xFormat', function() { beforeAll(function() { args = { data: { x: 'date', columns: [ ['date', '2013-01-01', '2013-01-02', '2013-01-03'], ['data1', 30, 200, 100], ['data2', 130, 300, 200] ] }, axis: { x: { type: 'timeseries' } } } }) it('should have correct number of xs', function() { expect(Object.keys(chart.internal.data.xs).length).toBe(2) expect(chart.internal.data.xs.data1.length).toBe(3) expect(chart.internal.data.xs.data2.length).toBe(3) }) it('should have Date object as x', function() { var xs = chart.internal.data.xs expect(+xs.data1[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0)) expect(+xs.data1[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0)) expect(+xs.data1[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0)) expect(+xs.data2[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0)) expect(+xs.data2[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0)) expect(+xs.data2[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0)) }) }) describe('with xFormat', function() { describe('timeseries x with xFormat', function() { beforeAll(function() { args = { data: { x: 'date', xFormat: '%Y%m%d', columns: [ ['date', '20130101', '20130102', '20130103'], ['data1', 30, 200, 100], ['data2', 130, 300, 200] ] }, axis: { x: { type: 'timeseries' } } } }) it('should have correct number of xs', function() { expect(Object.keys(chart.internal.data.xs).length).toBe(2) expect(chart.internal.data.xs.data1.length).toBe(3) expect(chart.internal.data.xs.data2.length).toBe(3) }) it('should have Date object as x', function() { var xs = chart.internal.data.xs expect(+xs.data1[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0)) expect(+xs.data1[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0)) expect(+xs.data1[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0)) expect(+xs.data2[0]).toBe(+new Date(2013, 0, 1, 0, 0, 0)) expect(+xs.data2[1]).toBe(+new Date(2013, 0, 2, 0, 0, 0)) expect(+xs.data2[2]).toBe(+new Date(2013, 0, 3, 0, 0, 0)) }) }) }) }) describe('milliseconds timeseries x', function() { describe('as date string', function() { beforeAll(function() { args = { data: { x: 'date', xFormat: '%Y-%m-%d %H:%M:%S.%L', columns: [ ['date', '2014-05-20 17:25:00.123', '2014-05-20 17:30:00.345'], ['data1', 30, 200], ['data2', 130, 300] ] }, axis: { x: { type: 'timeseries', tick: { format: '%Y-%m-%d %H:%M:%S.%L', multiline: false } } } } }) it('should have correct number of xs', function() { expect(Object.keys(chart.internal.data.xs).length).toBe(2) expect(chart.internal.data.xs.data1.length).toBe(2) expect(chart.internal.data.xs.data2.length).toBe(2) }) it('should have Date object as x', function() { var xs = chart.internal.data.xs expect(+xs.data1[0]).toBe(+new Date(2014, 4, 20, 17, 25, 0, 123)) expect(+xs.data1[1]).toBe(+new Date(2014, 4, 20, 17, 30, 0, 345)) expect(+xs.data2[0]).toBe(+new Date(2014, 4, 20, 17, 25, 0, 123)) expect(+xs.data2[1]).toBe(+new Date(2014, 4, 20, 17, 30, 0, 345)) }) it('should have milliseconds tick format', function() { var expected = ['2014-05-20 17:25:00.123', '2014-05-20 17:30:00.345'] chart.internal.main .selectAll('.c3-axis-x g.tick text') .each(function(d, i) { expect(d3.select(this).text()).toBe(expected[i]) }) }) }) describe('as unixtime number', function() { beforeAll(function() { args = { data: { x: 'date', columns: [ ['date', 1417622461123, 1417622522345], ['data1', 30, 200], ['data2', 130, 300] ] }, axis: { x: { type: 'timeseries', tick: { format: '%Y-%m-%d %H:%M:%S.%L' } } } } }) it('should have correct number of xs', function() { expect(Object.keys(chart.internal.data.xs).length).toBe(2) expect(chart.internal.data.xs.data1.length).toBe(2) expect(chart.internal.data.xs.data2.length).toBe(2) }) it('should have Date object as x', function() { var xs = chart.internal.data.xs expect(+xs.data1[0]).toBe(1417622461123) expect(+xs.data1[1]).toBe(1417622522345) expect(+xs.data2[0]).toBe(1417622461123) expect(+xs.data2[1]).toBe(1417622522345) }) }) }) }) describe('data.label', function() { describe('on line chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 1030, 2200, 2100], ['data2', 1150, 2010, 1200], ['data3', -1150, -2010, -1200], ['data4', -1030, -2200, -2100] ], type: 'line', labels: true } } }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [128, 38, 46], data2: [119, 53, 115], data3: [311, 377, 315], data4: [302, 392, 384] } var expectedTextX = { data1: [6, 294, 583], data2: [6, 294, 583], data3: [6, 294, 583], data4: [6, 294, 583] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) describe('with stacked', function() { beforeAll(function() { args.data.groups = [ ['data1', 'data2'], ['data3', 'data4'] ] }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [120, 38, 75], data2: [161, 127, 159], data3: [269, 303, 271], data4: [310, 392, 355] } var expectedTextX = { data1: [6, 294, 583], data2: [6, 294, 583], data3: [6, 294, 583], data4: [6, 294, 583] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) }) }) describe('on area chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 1030, 2200, 2100], ['data2', 1150, 2010, 1200], ['data3', -1150, -2010, -1200], ['data4', -1030, -2200, -2100] ], type: 'area', labels: true } } }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [128, 38, 46], data2: [119, 53, 115], data3: [311, 377, 315], data4: [302, 392, 384] } var expectedTextX = { data1: [6, 294, 583], data2: [6, 294, 583], data3: [6, 294, 583], data4: [6, 294, 583] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) describe('with stacked', function() { beforeAll(function() { args.data.groups = [ ['data1', 'data2'], ['data3', 'data4'] ] }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [120, 38, 75], data2: [161, 127, 159], data3: [269, 303, 271], data4: [310, 392, 355] } var expectedTextX = { data1: [6, 294, 583], data2: [6, 294, 583], data3: [6, 294, 583], data4: [6, 294, 583] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) }) }) describe('on bar chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 1030, 2200, 2100], ['data2', 1150, 2010, 1200], ['data3', -1150, -2010, -1200], ['data4', -1030, -2200, -2100] ], type: 'bar', labels: true } } }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [128, 38, 46], data2: [119, 53, 115], data3: [311, 377, 315], data4: [302, 392, 384] } var expectedTextX = { data1: [53, 249, 445], data2: [83, 279, 475], data3: [112, 308, 504], data4: [142, 338, 534] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) describe('with stacked', function() { beforeAll(function() { args.data.groups = [ ['data1', 'data2'], ['data3', 'data4'] ] }) it('should locate data labels in correct position', function() { var expectedTextY = { data1: [120, 38, 75], data2: [161, 127, 159], data3: [269, 303, 271], data4: [310, 392, 355] } var expectedTextX = { data1: [68.6, 264, 460], data2: [68.6, 264, 460], data3: [127, 323, 519], data4: [127, 323, 519] } Object.keys(expectedTextY).forEach(function(key) { d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function( d, i ) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedTextY[key][i], -2) expect(+text.attr('x')).toBeCloseTo(expectedTextX[key][i], -2) }) }) }) }) }) describe('for all targets', function() { describe('with data label for all data', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 100, 200, 100, 400, 150, 250], ['data2', 10, 20, 10, 40, 15, 25], ['data3', 1000, 2000, 1000, 4000, 1500, 2500] ], labels: true } } }) it('should have data labels on all data', function() { d3.selectAll('.c3-texts-data1 text').each(function(d, i) { expect(d3.select(this).text()).toBe( args.data.columns[0][i + 1] + '' ) }) d3.selectAll('.c3-texts-data2 text').each(function(d, i) { expect(d3.select(this).text()).toBe( args.data.columns[1][i + 1] + '' ) }) d3.selectAll('.c3-texts-data3 text').each(function(d, i) { expect(d3.select(this).text()).toBe( args.data.columns[2][i + 1] + '' ) }) }) }) }) describe('for each target', function() { describe('as true', function() { describe('with data label for only data1', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 100, 200, 100, 400, 150, 250], ['data2', 10, 20, 10, 40, 15, 25], ['data3', 1000, 2000, 1000, 4000, 1500, 2500] ], labels: { format: { data1: true } } } } }) it('should have data labels on all data', function() { d3.selectAll('.c3-texts-data1 text').each(function(d, i) { expect(d3.select(this).text()).toBe( args.data.columns[0][i + 1] + '' ) }) d3.selectAll('.c3-texts-data2 text').each(function() { expect(d3.select(this).text()).toBe('') }) d3.selectAll('.c3-texts-data3 text').each(function() { expect(d3.select(this).text()).toBe('') }) }) }) }) describe('as function', function() { describe('with data label for only data1', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 100, 200, 100, 400, 150, 250], ['data2', 10, 20, 10, 40, 15, 25], ['data3', 1000, 2000, 1000, 4000, 1500, 2500] ], labels: { format: { data1: d3.format('$') } } } } }) it('should have data labels on all data', function() { d3.selectAll('.c3-texts-data1 text').each(function(d, i) { expect(d3.select(this).text()).toBe( '$' + args.data.columns[0][i + 1] ) }) d3.selectAll('.c3-texts-data2 text').each(function() { expect(d3.select(this).text()).toBe('') }) d3.selectAll('.c3-texts-data3 text').each(function() { expect(d3.select(this).text()).toBe('') }) }) }) }) }) describe('with small values', function() { describe('with data label', function() { beforeAll(function() { args = { data: { columns: [['data1', 0.03, 0.2, 0.1, 0.4, 0.15, 0.25]], labels: true } } }) it('should have proper y domain', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-0.02) expect(domain[1]).toBeCloseTo(0.45) }) }) }) describe('with positive values and null', function() { describe('on not rotated axis', function() { beforeAll(function() { args = { data: { columns: [['data1', 190, 200, 190, null]], type: 'bar', labels: { format: function(v) { if (v === null) { return 'Not Applicable' } return d3.format('$')(v) } } } } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(0, -1) expect(domain[1]).toBeCloseTo(227, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [67, 49, 67, 423], expectedXs = [74, 221, 368, 515] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(189, -1) expect(domain[1]).toBeCloseTo(201, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [375, 40, 375, 422], expectedXs = [6, 198, 391, 583] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) describe('on rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args.data.type = 'bar' args.axis = { rotated: true } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(0, -1) expect(domain[1]).toBeCloseTo(231, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [57, 163, 269, 375], expectedXs = [490, 516, 490, 4] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(188, -1) expect(domain[1]).toBeCloseTo(202, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [9, 147, 286, 424], expectedXs = [76, 526, 76, 4] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) }) describe('with negative values and null', function() { describe('on not rotated axis', function() { describe('type bar', function() { beforeAll(function() { args = { data: { columns: [['data1', -190, 0, -190, null]], type: 'bar', labels: { format: function(v) { if (v === null) { return 'Not Applicable' } return d3.format('$')(v) } } } } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-215, -1) expect(domain[1]).toBeCloseTo(0, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [368, 12, 368, 12], expectedXs = [74, 221, 368, 515] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-215, -1) expect(domain[1]).toBeCloseTo(25, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [395, 60, 395, 12], expectedXs = [6, 198, 391, 583] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) describe('on rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args.data.type = 'bar' args.axis = { rotated: true } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-220, -1) expect(domain[1]).toBeCloseTo(0, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [57, 163, 269, 375], expectedXs = [103, 594, 103, 526] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-220, -1) expect(domain[1]).toBeCloseTo(24, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [9, 147, 286, 424], expectedXs = [67, 537, 67, 526] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) }) describe('with positive and negative values and null', function() { describe('on non rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args = { data: { columns: [['data1', -190, 200, 190, null]], type: 'bar', labels: { format: function(v) { if (v === null) { return 'Not Applicable' } return d3.format('$')(v) } } } } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-243, -1) expect(domain[1]).toBeCloseTo(253, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [392, 43, 52, 215], expectedXs = [74, 221, 368, 515] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-243, -1) expect(domain[1]).toBeCloseTo(253, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [392, 40, 49, 212], expectedXs = [6, 198, 391, 583] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) describe('on rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args.data.type = 'bar' args.axis = { rotated: true } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-253, -1) expect(domain[1]).toBeCloseTo(260, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [57, 163, 269, 375], expectedXs = [69, 525, 513, 295] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-253, -1) expect(domain[1]).toBeCloseTo(260, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [9, 147, 286, 424], expectedXs = [67, 527, 515, 297] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) }) describe('with positive grouped values', function() { describe('on non rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 500], ['data2', 50, 20, 10, 40], ['data3', 250, 220, 210, 240] ], groups: [['data1', 'data2', 'data3']], labels: true, type: 'bar' } } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(0, -1) expect(domain[1]).toBeCloseTo(885, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [385, 317, 370, 164], expectedXs = [74, 221, 368, 515] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-94, -1) expect(domain[1]).toBeCloseTo(884, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [344, 284, 331, 144], expectedXs = [6, 198, 391, 583] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) describe('on rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args.data.type = 'bar' args.axis = { rotated: true } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(0, -1) expect(domain[1]).toBeCloseTo(888, -1.2) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [57, 163, 269, 375], expectedXs = [57, 150, 77, 363] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-87, -1) expect(domain[1]).toBeCloseTo(887, -1.2) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [9, 147, 286, 424], expectedXs = [107, 192, 125, 386] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) }) describe('with negative grouped values', function() { describe('on non rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args = { data: { columns: [ ['data1', -30, -200, -100, -500], ['data2', -50, -20, -10, -40], ['data3', -250, -220, -210, -240] ], groups: [['data1', 'data2', 'data3']], labels: true, type: 'bar' } } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-885, -1) expect(domain[1]).toBeCloseTo(0, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [51, 118, 65, 272], expectedXs = [74, 221, 368, 515] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-884, -1) expect(domain[1]).toBeCloseTo(94, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [88, 149, 101, 288], expectedXs = [6, 198, 391, 583] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) describe('on rotated axis', function() { describe('data type bar', function() { beforeAll(function() { args.data.type = 'bar' args.axis = { rotated: true } }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-899, -1) expect(domain[1]).toBeCloseTo(0, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [57, 163, 269, 375], expectedXs = [533, 440, 513, 230] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) describe('data type line', function() { beforeAll(function() { args.data.type = 'line' }) it('should have y domain with proper padding', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-893, -1) expect(domain[1]).toBeCloseTo(93, -1) }) it('should locate labels above each data point', function() { var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), expectedYs = [9, 147, 286, 424], expectedXs = [480, 397, 462, 205] texts.each(function(d, i) { var text = d3.select(this) expect(+text.attr('y')).toBeCloseTo(expectedYs[i], -2) expect(+text.attr('x')).toBeCloseTo(expectedXs[i], -2) }) }) }) }) }) }) describe('data.stack', function() { beforeAll(() => { args = { data: { columns: [ ['data1', 230, 50, 300], ['data2', 198, 87, 580] ], type: 'bar', groups: [['data1', 'data2']], stack: { normalize: true } } } }) const getChartHeight = () => +chart.internal.main.select(`.c3-event-rect`).attr('height') - 1 it('check for the normalized y axis tick in percentage', () => { const tick = chart.internal.main.selectAll(`.c3-axis-y .tick tspan`) // check for the y axis to be in percentage tick.each(function(v, i) { expect(this.textContent).toEqual(`${i * 10}%`) }) }) it("check for the normalized bar's height", () => { const chartHeight = getChartHeight() const bars = chart.internal.main.selectAll('.c3-bar').nodes() bars.splice(0, 3).forEach((v, i) => { expect(v.getBBox().height + bars[i].getBBox().height).toEqual( chartHeight ) }) }) it('check when hiding data', done => { const chartHeight = getChartHeight() // when chart.hide('data1') setTimeout(() => { chart.internal.main.selectAll(`.c3-target-data2 path`).each(function() { expect(this.getBBox().height).toBe(chartHeight) }) done() }, 500) }) describe('timeseries chart', () => { beforeAll(function() { args = { data: { x: 'date', columns: [ ['date', '2012-12-24', '2012-12-25', '2012-12-26'], ['data1', 30, 200, 400], ['data2', 50, 60, 50] ], groups: [['data1', 'data2']], type: 'bar', stack: { normalize: true } }, axis: { x: { type: 'timeseries' } } } }) it("check for the normalized bar's height", () => { const chartHeight = getChartHeight() const bars = chart.internal.main.selectAll('.c3-bar').nodes() expect(document.hidden).toBeFalsy() bars.splice(0, 3).forEach((v, i) => { expect(v.getBBox().height + bars[i].getBBox().height).toEqual( chartHeight ) }) }) }) describe('area chart', () => { beforeAll(() => { args = { data: { columns: [ ['data1', 200, 387, 123], ['data2', 200, 387, 123] ], type: 'area', groups: [['data1', 'data2']], stack: { normalize: true } } } }) it("check for the normalized area's height", () => { const chartHeight = getChartHeight() let areaHeight = 0 chart.internal.main.selectAll('.c3-area').each(function() { areaHeight += this.getBBox().height }) expect(document.hidden).toBeFalsy() expect(areaHeight).toEqual(chartHeight) }) }) }) }) ================================================ FILE: spec/data.convert-spec.ts ================================================ import { c3, initChart } from './c3-helper' const $$ = c3.chart.internal.fn $$.d3 = require('d3') describe('data.convert', () => { describe('$$.convertColumnsToData', () => { it('converts column data to normalized data', () => { const data = $$.convertColumnsToData([ ['cat1', 'a', 'b', 'c', 'd'], ['data1', 30, 200, 100, 400], ['cat2', 'b', 'a', 'c', 'd', 'e', 'f'], ['data2', 400, 60, 200, 800, 10, 10] ]) expect(data).toEqual({ keys: ['cat1', 'data1', 'cat2', 'data2'], rows: [ { cat1: 'a', data1: 30, cat2: 'b', data2: 400 }, { cat1: 'b', data1: 200, cat2: 'a', data2: 60 }, { cat1: 'c', data1: 100, cat2: 'c', data2: 200 }, { cat1: 'd', data1: 400, cat2: 'd', data2: 800 }, { cat2: 'e', data2: 10 }, { cat2: 'f', data2: 10 } ] }) }) it('throws when the column data contains undefined', () => { expect(() => $$.convertColumnsToData([ ['cat1', 'a', 'b', 'c', 'd'], ['data1', undefined] ]) ).toThrowError(Error, /Source data is missing a component/) }) }) describe('$$.convertRowsToData', () => { it('converts the row data to normalized data', () => { const data = $$.convertRowsToData([ ['data1', 'data2', 'data3'], [90, 120, 300], [40, 160, 240], [50, 200, 290], [120, 160, 230], [80, 130, 300], [90, 220, 320] ]) expect(data).toEqual({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: 90, data2: 120, data3: 300 }, { data1: 40, data2: 160, data3: 240 }, { data1: 50, data2: 200, data3: 290 }, { data1: 120, data2: 160, data3: 230 }, { data1: 80, data2: 130, data3: 300 }, { data1: 90, data2: 220, data3: 320 } ] }) }) it('throws when the row data contains undefined', () => { expect(() => $$.convertRowsToData([ ['data1', 'data2', 'data3'], [40, 160, 240], [90, 120, undefined] ]) ).toThrowError(Error, /Source data is missing a component/) }) }) describe('$$.convertXsvToData', () => { it('converts the csv data to normalized data', () => { const data = [ { data1: '90', data2: '120', data3: '300' }, { data1: '40', data2: '160', data3: '240' }, { data1: '50', data2: '200', data3: '290' }, { data1: '120', data2: '160', data3: '230' }, { data1: '80', data2: '130', data3: '300' }, { data1: '90', data2: '220', data3: '320' } ] ;(data as any).columns = ['data1', 'data2', 'data3'] expect($$.convertXsvToData(data)).toEqual({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: '90', data2: '120', data3: '300' }, { data1: '40', data2: '160', data3: '240' }, { data1: '50', data2: '200', data3: '290' }, { data1: '120', data2: '160', data3: '230' }, { data1: '80', data2: '130', data3: '300' }, { data1: '90', data2: '220', data3: '320' } ] }) }) it('converts one lined CSV data', () => { const data = [] ;(data as any).columns = ['data1', 'data2', 'data3'] expect($$.convertXsvToData(data)).toEqual({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: null, data2: null, data3: null } ] }) }) }) describe('$$.convertDataToTargets', () => { beforeEach(() => { $$.cache = {} $$.data = { xs: [] } $$.config = { data_idConverter: v => v } }) it('converts the legacy data format into targets', () => { const targets = $$.convertDataToTargets([ { data1: 90, data2: 120, data3: 300 }, { data1: 40, data2: 160, data3: 240 } ]) expect(targets).toEqual([ { id: 'data1', id_org: 'data1', values: [ { x: 0, value: 90, id: 'data1', index: 0 }, { x: 1, value: 40, id: 'data1', index: 1 } ] }, { id: 'data2', id_org: 'data2', values: [ { x: 0, value: 120, id: 'data2', index: 0 }, { x: 1, value: 160, id: 'data2', index: 1 } ] }, { id: 'data3', id_org: 'data3', values: [ { x: 0, value: 300, id: 'data3', index: 0 }, { x: 1, value: 240, id: 'data3', index: 1 } ] } ]) }) it('converts the data into targets', () => { const targets = $$.convertDataToTargets({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: 90, data2: 120, data3: 300 }, { data1: 40, data2: 160, data3: 240 } ] }) expect(targets).toEqual([ { id: 'data1', id_org: 'data1', values: [ { x: 0, value: 90, id: 'data1', index: 0 }, { x: 1, value: 40, id: 'data1', index: 1 } ] }, { id: 'data2', id_org: 'data2', values: [ { x: 0, value: 120, id: 'data2', index: 0 }, { x: 1, value: 160, id: 'data2', index: 1 } ] }, { id: 'data3', id_org: 'data3', values: [ { x: 0, value: 300, id: 'data3', index: 0 }, { x: 1, value: 240, id: 'data3', index: 1 } ] } ]) }) }) describe('$$.convertJsonToData', () => { it('converts JSON as object (no keys provided)', () => { const data = $$.convertJsonToData({ data1: [90, 40, 50, 120, 80, 90], data2: [120, 160, 200, 160, 130, 220], data3: [300, 240, 290, 230, 300, 320] }) expect(data).toEqual({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: 90, data2: 120, data3: 300 }, { data1: 40, data2: 160, data3: 240 }, { data1: 50, data2: 200, data3: 290 }, { data1: 120, data2: 160, data3: 230 }, { data1: 80, data2: 130, data3: 300 }, { data1: 90, data2: 220, data3: 320 } ] }) }) it('converts JSON as rows (keys provided)', () => { const data = $$.convertJsonToData( [ { data1: 90, data2: 120, data3: 300, unused: 42 }, { data1: 40, data2: 160, data3: 240, unused: 42 }, { data1: 50, data2: 200, data3: 290, unused: 42 }, { data1: 120, data2: 160, data3: 230, unused: 42 }, { data1: 80, data2: 130, data3: 300, unused: 42 }, { data1: 90, data2: 220, data3: 320, unused: 42 } ], { value: ['data1', 'data2', 'data3'] } ) expect(data).toEqual({ keys: ['data1', 'data2', 'data3'], rows: [ { data1: 90, data2: 120, data3: 300 }, { data1: 40, data2: 160, data3: 240 }, { data1: 50, data2: 200, data3: 290 }, { data1: 120, data2: 160, data3: 230 }, { data1: 80, data2: 130, data3: 300 }, { data1: 90, data2: 220, data3: 320 } ] }) }) }) }) ================================================ FILE: spec/domain-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 chart domain', function() { 'use strict' var chart var args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, axis: { y: {}, y2: {} } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('axis.y.min', function() { describe('should change axis.y.min to -100', function() { beforeAll(function() { ;(args.axis.y as any).min = -100 }) it('should be set properly when smaller than max of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBe(-150) expect(domain[1]).toBe(450) }) }) describe('should change axis.y.min to 500', function() { beforeAll(function() { ;(args.axis.y as any).min = 500 }) it('should be set properly when bigger than max of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBe(499) expect(domain[1]).toBe(511) }) }) afterAll(function() { ;(args.axis.y as any).min = undefined }) }) describe('axis.y.max', function() { describe('should change axis.y.max to 1000', function() { beforeAll(function() { ;(args.axis.y as any).max = 1000 }) it('should be set properly when bigger than min of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBe(-89) expect(domain[1]).toBe(1099) }) }) describe('should change axis.y.max to 0', function() { beforeAll(function() { ;(args.axis.y as any).max = 0 }) it('should be set properly when smaller than min of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBe(-11) expect(domain[1]).toBe(1) }) }) }) describe('axis.y.padding', function() { describe('should change axis.y.max to 1000', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 10, 20, 10, 40, 15, 25], ['data2', 50, 40, 30, 45, 25, 45] ] }, axis: { y: { padding: 200 }, y2: {} } } }) it('should be set properly when bigger than min of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-9, -1) expect(domain[1]).toBeCloseTo(69, -1) }) }) describe('should change axis.y.max to 1000 with top/bottom padding', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 10, 20, 10, 40, 15, 25], ['data2', 50, 40, 30, 45, 25, 45] ] }, axis: { y: { padding: { top: 200, bottom: 200 } }, y2: {} } } }) it('should be set properly when bigger than min of data', function() { var domain = chart.internal.y.domain() expect(domain[0]).toBeCloseTo(-9, -1) expect(domain[1]).toBeCloseTo(69, -1) }) }) }) }) ================================================ FILE: spec/drag-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('drag behavior', function() { 'use strict' var chart, shapes, $$, totalshapes var args = { data: { x: 'x', columns: [ ['x', 10, 30, 45, 50, 70, 100], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ], selection: { enabled: true, grouped: true, multiple: true, draggable: true } } } beforeAll(function(done) { chart = initChart(chart, args, done) $$ = chart.internal shapes = $$.main .selectAll('.' + $$.CLASS.shapes) .selectAll('.' + $$.CLASS.shape) totalshapes = shapes.size() }) it('should contain 15 shapes', function() { expect(totalshapes).toBe(15) }) it('should have no selected shapes', function() { var selected = shapes .filter(function() { return d3.select(this).classed($$.CLASS.SELECTED) }) .size() expect(selected).toBe(0) }) describe('Trigger drag events', function() { beforeAll(function() { var s = chart.internal.eventRect, coords1 = [ (s.attr('width') - s.attr('x')) / 3, (s.attr('height') - s.attr('y')) / 3 ], coords2 = [ (2 * (s.attr('width') - s.attr('x'))) / 3, (2 * (s.attr('height') - s.attr('y'))) / 3 ] $$.dragstart(coords1) $$.drag(coords2) $$.dragend() }) it('should select 6 shapes', function() { var selected = shapes .filter(function() { return d3.select(this).classed($$.CLASS.SELECTED) }) .size() expect(selected).toBe(6) }) it('should select 9 unselected shapes', function() { var unselected = shapes .filter(function() { return !d3.select(this).classed($$.CLASS.SELECTED) }) .size() expect(unselected).toBe(9) }) describe('Selected api', function() { it('should return 6 selected shapes', function() { var selected = chart.selected() expect(selected.length).toBe(6) }) it('should return 3 selected shapes with targetId = data1', function() { var selected = chart.selected('data1') expect(selected.length).toBe(3) }) }) }) }) ================================================ FILE: spec/grid-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart grid', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('y grid show', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, axis: { y: { tick: {} } }, grid: { y: { show: false } } } }) it('should not show y grids', function() { expect(chart.internal.main.select('.c3-ygrids').size()).toBe(0) }) describe('with y grids', function() { beforeAll(function() { args.grid.y.show = true }) it('should show y grids', function() { var ygrids = chart.internal.main.select('.c3-ygrids') expect(ygrids.size()).toBe(1) expect(ygrids.selectAll('.c3-ygrid').size()).toBe(9) }) }) describe('with only 3 y grids', function() { beforeAll(function() { args.grid.y.ticks = 3 }) it('should show only 3 y grids', function() { var ygrids = chart.internal.main.select('.c3-ygrids') expect(ygrids.size()).toBe(1) expect(ygrids.selectAll('.c3-ygrid').size()).toBe(3) }) }) describe('with y grids depending on y axis ticks', function() { beforeAll(function() { args.axis.y.tick.count = 5 }) it('should show grids depending on y axis ticks', function() { var ygrids = chart.internal.main.select('.c3-ygrids'), expectedYs = [] ygrids.selectAll('.c3-ygrid').each(function(d, i) { expectedYs[i] = Math.ceil(+d3.select(this).attr('y1')) }) expect(ygrids.size()).toBe(1) expect(ygrids.selectAll('.c3-ygrid').size()).toBe(5) chart.internal.main .select('.c3-axis-y') .selectAll('.tick') .each(function(d, i) { var t = d3.select(this).attr('transform') expect(t).toBe('translate(0,' + expectedYs[i] + ')') }) }) }) }) describe('y grid lines', function() { describe('position', function() { beforeAll(function() { args = { data: { columns: [['data1', 10, 200, 100, 400, 150, 250]] }, grid: { y: { lines: [ { value: 30, text: 'Label 30', position: 'start' }, { value: 145, text: 'Label 145', position: 'middle' }, { value: 225, text: 'Label 225' } ] } } } }) it('should show 3 grid lines', function() { expect( chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line').size() ).toBe(3) }) it('should locate grid lines properly', function() { var lines = chart.internal.main.selectAll( '.c3-ygrid-lines .c3-ygrid-line' ), expectedY1s = [373, 268, 196] lines.each(function(d, i) { var y1 = d3 .select(this) .select('line') .attr('y1') expect(y1).toBeCloseTo(expectedY1s[i], -2) }) }) it('should locate grid texts properly', function() { var lines = chart.internal.main.selectAll( '.c3-ygrid-lines .c3-ygrid-line' ), expectedPositions = ['start', 'middle', 'end'], expectedDxs = [4, 0, -4] lines.each(function(d, i) { var text = d3.select(this).select('text'), textAnchor = text.attr('text-anchor'), dx = text.attr('dx') expect(textAnchor).toBe(expectedPositions[i]) expect(+dx).toBe(expectedDxs[i]) }) }) describe('three gridlines', function() { beforeAll(function() { args = { data: { columns: [['data1', 10, 200, 100, 400, 150, 250]] }, axis: { rotated: true }, grid: { y: { lines: [ { value: 30, text: 'Label 30', position: 'start' }, { value: 145, text: 'Label 145', position: 'middle' }, { value: 225, text: 'Label 225' } ] } } } }) it('should show 3 grid lines', function() { expect( chart.internal.main .selectAll('.c3-ygrid-lines .c3-ygrid-line') .size() ).toBe(3) }) it('should locate grid lines properly', function() { var lines = chart.internal.main.selectAll( '.c3-ygrid-lines .c3-ygrid-line' ), expectedX1s = [75, 220, 321] lines.each(function(d, i) { var x1 = d3 .select(this) .select('line') .attr('x1') expect(x1).toBeCloseTo(expectedX1s[i], -2) }) }) it('should locate grid texts properly', function() { var lines = chart.internal.main.selectAll( '.c3-ygrid-lines .c3-ygrid-line' ), expectedPositions = ['start', 'middle', 'end'], expectedDxs = [4, 0, -4] lines.each(function(d, i) { var text = d3.select(this).select('text'), textAnchor = text.attr('text-anchor'), dx = text.attr('dx') expect(textAnchor).toBe(expectedPositions[i]) expect(+dx).toBe(expectedDxs[i]) }) }) }) }) }) describe('x grid lines', function() { describe('position', function() { beforeAll(function() { // 'should have correct height', args = { data: { columns: [['data1', 30, 200, 100, 400]] }, grid: { x: { lines: [ { value: 1, text: 'Label 1', position: 'start' }, { value: 2, text: 'Label 2', position: 'middle' }, { value: 3, text: 'Label 3' } ] } } } }) it('should show 3 grid lines', function() { expect( chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line').size() ).toBe(3) }) it('should locate grid lines properly', function() { var lines = chart.internal.main.selectAll( '.c3-xgrid-lines .c3-xgrid-line' ), expectedX1s = [202, 397, 593] lines.each(function(d, i) { var x1 = d3 .select(this) .select('line') .attr('x1') expect(x1).toBeCloseTo(expectedX1s[i], -2) }) }) it('should locate grid texts properly', function() { var lines = chart.internal.main.selectAll( '.c3-xgrid-lines .c3-xgrid-line' ), expectedPositions = ['start', 'middle', 'end'], expectedDxs = [4, 0, -4] lines.each(function(d, i) { var text = d3.select(this).select('text'), textAnchor = text.attr('text-anchor'), dx = text.attr('dx') expect(textAnchor).toBe(expectedPositions[i]) expect(+dx).toBe(expectedDxs[i]) }) }) describe('three grid lines', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400]] }, axis: { rotated: true }, grid: { x: { lines: [ { value: 1, text: 'Label 1', position: 'start' }, { value: 2, text: 'Label 2', position: 'middle' }, { value: 3, text: 'Label 3' } ] } } } }) it('should show 3 grid lines', function() { expect( chart.internal.main .selectAll('.c3-xgrid-lines .c3-xgrid-line') .size() ).toBe(3) }) it('should locate grid lines properly', function() { var lines = chart.internal.main.selectAll( '.c3-xgrid-lines .c3-xgrid-line' ), expectedY1s = [144, 283, 421] lines.each(function(d, i) { var y1 = d3 .select(this) .select('line') .attr('y1') expect(y1).toBeCloseTo(expectedY1s[i], -2) }) }) it('should locate grid texts properly', function() { var lines = chart.internal.main.selectAll( '.c3-xgrid-lines .c3-xgrid-line' ), expectedPositions = ['start', 'middle', 'end'], expectedDxs = [4, 0, -4] lines.each(function(d, i) { var text = d3.select(this).select('text'), textAnchor = text.attr('text-anchor'), dx = text.attr('dx') expect(textAnchor).toBe(expectedPositions[i]) expect(+dx).toBe(expectedDxs[i]) }) }) }) }) describe('with padding.top', function() { describe('should have correct height', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400]] }, grid: { x: { lines: [{ value: 3, text: 'Label 3' }] } }, padding: { top: 50 } } }) it('should show x grid lines', function() { var lines = chart.internal.main.select( '.c3-xgrid-lines .c3-xgrid-line' ), expectedX1 = 593, expectedText = ['Label 3'] lines.each(function(id, i) { var line = d3.select(this), l = line.select('line'), t = line.select('text') expect(+l.attr('x1')).toBeCloseTo(expectedX1, -2) expect(t.text()).toBe(expectedText[i]) }) }) }) }) describe('on category axis', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', 'a', 'b', 'c', 'd'], ['data1', 30, 200, 100, 400] ] }, axis: { x: { type: 'category' } }, grid: { x: { lines: [ { value: 3, text: 'Label 3' }, { value: 'a', text: 'Label a' } ] } } } }) it('should show x grid lines', function() { var lines = chart.internal.main.selectAll( '.c3-xgrid-lines .c3-xgrid-line' ), expectedX1 = [515, 74], expectedText = ['Label 3', 'Label a'] lines.each(function(id, i) { var line = d3.select(this), l = line.select('line'), t = line.select('text') expect(+l.attr('x1')).toBeCloseTo(expectedX1[i], -2) expect(t.text()).toBe(expectedText[i]) }) }) }) }) }) ================================================ FILE: spec/interaction-spec.ts ================================================ import { d3, setMouseEvent, initChart } from './c3-helper' describe('c3 chart interaction', function() { 'use strict' var chart, args const moveMouseOut = () => setMouseEvent(chart, 'mouseout', 0, 0, d3.select('.c3-event-rect').node()) const moveMouse = (x = 0, y = 0) => setMouseEvent(chart, 'mousemove', x, y, d3.select('.c3-event-rect').node()) const clickMouse = (x = 0, y = 0) => setMouseEvent(chart, 'click', x, y, d3.select('.c3-event-rect').node()) beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('generate event rects', function() { describe('custom x', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', 0, 1000, 3000, 10000], ['data', 10, 10, 10, 10] ], type: 'bar' } } }) it('should have only 1 event rect properly', function() { var eventRects = d3.selectAll('.c3-event-rect') expect(eventRects.size()).toBe(1) eventRects.each(function() { var box = (d3.select(this).node() as any).getBoundingClientRect() expect(box.left).toBeCloseTo(40.5, -2) expect(box.width).toBeCloseTo(598, -2) }) }) describe('mouseover', function() { let mouseoutCounter = 0 let mouseoverCounter = 0 beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'bar', onmouseout: function() { mouseoutCounter += 1 }, onmouseover: function() { mouseoverCounter += 1 } }, axis: { rotated: false } } }) beforeEach(function() { mouseoverCounter = 0 mouseoutCounter = 0 }) it('should be undefined when not within bar', function() { moveMouseOut() expect(mouseoutCounter).toEqual(0) expect(mouseoverCounter).toEqual(0) expect(chart.internal.mouseover).toBeUndefined() }) it('should be data value when within bar', function() { moveMouse(31, 280) expect(mouseoutCounter).toEqual(0) expect(mouseoverCounter).toEqual(1) expect(chart.internal.mouseover).toEqual({ x: 0, value: 30, index: 0, id: 'data1', name: 'data1' }) }) it('should be undefined after leaving chart', function() { moveMouse(31, 280) moveMouseOut() expect(mouseoutCounter).toEqual(1) expect(mouseoverCounter).toEqual(1) expect(chart.internal.mouseover).toBeUndefined() }) it('should retrigger mouseover event when returning to same value', function() { moveMouse(31, 280) moveMouseOut() moveMouse(31, 280) expect(mouseoutCounter).toEqual(1) expect(mouseoverCounter).toEqual(2) expect(chart.internal.mouseover).toEqual({ x: 0, value: 30, index: 0, id: 'data1', name: 'data1' }) }) }) describe('should generate bar chart with only one data', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', 0], ['data', 10] ], type: 'bar' } } }) it('should have 1 event rects properly', function() { var eventRects = d3.selectAll('.c3-event-rect') expect(eventRects.size()).toBe(1) eventRects.each(function() { var box = (d3.select(this).node() as any).getBoundingClientRect() expect(box.left).toBeCloseTo(40.5, -2) expect(box.width).toBeCloseTo(598, -2) }) }) }) }) describe('timeseries', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', '20140101', '20140201', '20140210', '20140301'], ['data', 10, 10, 10, 10] ] } } }) it('should have only 1 event rect properly', function() { var eventRects = d3.selectAll('.c3-event-rect') expect(eventRects.size()).toBe(1) eventRects.each(function() { var box = (d3.select(this).node() as any).getBoundingClientRect() expect(box.left).toBeCloseTo(40.5, -2) expect(box.width).toBeCloseTo(598, -2) }) }) describe('should generate line chart with only 1 data timeseries', function() { beforeAll(function() { args = { data: { x: 'x', columns: [ ['x', '20140101'], ['data', 10] ] } } }) it('should have 1 event rects properly', function() { var eventRects = d3.selectAll('.c3-event-rect') expect(eventRects.size()).toBe(1) eventRects.each(function() { var box = (d3.select(this).node() as any).getBoundingClientRect() expect(box.left).toBeCloseTo(40.5, -2) expect(box.width).toBeCloseTo(598, -2) }) }) }) }) }) describe('bar chart', function() { describe('tooltip_grouped=true', function() { beforeAll(() => { args = { data: { columns: [ ['data1', 30, 200, 200, 400, 150, -250], ['data2', 130, -100, 100, 200, 150, 50], ['data3', 230, -200, 200, 0, 250, 250] ], type: 'bar', groups: [['data1', 'data2']], hide: ['data1'] }, tooltip: { grouped: true }, axis: { x: { type: 'category' }, rotated: true }, interaction: { enabled: true } } }) it('generate a single rect', () => { const eventRectList = d3.selectAll('.c3-event-rect') expect(eventRectList.size()).toBe(1) expect(eventRectList.attr('x')).toEqual('0') expect(eventRectList.attr('y')).toEqual('0') expect(eventRectList.attr('height')).toEqual('' + chart.internal.height) expect(eventRectList.attr('width')).toEqual('' + chart.internal.width) }) it('shows tooltip with visible data of currently hovered category', () => { moveMouse(20, 20) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') const tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(3) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe('data3') expect(tooltipData[2].querySelector('.name').textContent).toBe('data2') }) it('shows cursor:pointer only if hovering bar', () => { const eventRect = d3.select('.c3-event-rect') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') moveMouse(360, 48) expect(eventRect.style('cursor')).toEqual('pointer') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') }) it('expands all bars of currently hovered category', () => { moveMouse(20, 20) const barList = d3.selectAll('.c3-bar') expect(barList.size()).toBeGreaterThan(0) barList.each(function() { if ( (this as any).classList.contains('c3-bar-0') && !(this as any).parentElement.classList.contains('c3-bars-data1') ) { expect((this as any).classList.contains('_expanded_')).toBeTruthy() } else { expect((this as any).classList.contains('_expanded_')).toBeFalsy() } }) moveMouse(20, 170) barList.each(function() { if ( (this as any).classList.contains('c3-bar-2') && !(this as any).parentElement.classList.contains('c3-bars-data1') ) { expect((this as any).classList.contains('_expanded_')).toBeTruthy() } else { expect((this as any).classList.contains('_expanded_')).toBeFalsy() } }) }) }) describe('tooltip_grouped=false', function() { beforeAll(() => { args = { data: { columns: [ ['data1', 30, 200, 200, 400, 150, -250], ['data2', 130, -100, 100, 200, 150, 50], ['data3', 230, -200, 200, 0, 250, 250] ], type: 'bar', groups: [['data1', 'data2']] }, tooltip: { grouped: false }, axis: { x: { type: 'category' } }, interaction: { enabled: true } } }) it('generate a single rect', () => { const eventRectList = d3.selectAll('.c3-event-rect') expect(eventRectList.size()).toBe(1) expect(eventRectList.attr('x')).toEqual('0') expect(eventRectList.attr('y')).toEqual('0') expect(eventRectList.attr('height')).toEqual('' + chart.internal.height) expect(eventRectList.attr('width')).toEqual('' + chart.internal.width) }) it('shows tooltip with only hovered data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('none') moveMouse(35, 268) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') const tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data2 expect(tooltipData[1].querySelector('.name').textContent).toBe('data2') expect(tooltipData[1].querySelector('.value').textContent).toBe('130') }) it('expands only hovered bar', () => { moveMouse(20, 20) const barList = d3.selectAll('.c3-bar') expect(barList.size()).toBeGreaterThan(0) // nothing expanded barList.each(function() { expect((this as any).classList.contains('_expanded_')).toBeFalsy() }) moveMouse(38, 258) barList.each(function() { if ( (this as any).classList.contains('c3-bar-0') && (this as any).parentElement.classList.contains('c3-bars-data2') ) { expect((this as any).classList.contains('_expanded_')).toBeTruthy() } else { expect((this as any).classList.contains('_expanded_')).toBeFalsy() } }) }) }) }) describe('line chart', function() { describe('tooltip_grouped=false', function() { let clickedData = [] beforeAll(() => { args = { data: { columns: [ ['data1', 30, 200, 200, 400, 150, -250], ['data2', 130, -100, 100, 200, 150, 50], ['data3', 230, -200, 200, 0, 250, 250] ], type: 'line', groups: [['data1', 'data2']], onclick: function(d) { clickedData.push(d) } }, tooltip: { grouped: false }, axis: { x: { type: 'category' } }, interaction: { enabled: true }, point: { r: 2, sensitivity: 10, focus: { expand: { enabled: true, r: 8 } } } } }) beforeEach(function() { clickedData = [] }) it('shows tooltip with only hovered data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('none') moveMouse(48, 184) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') const tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data3 expect(tooltipData[1].querySelector('.name').textContent).toBe('data3') expect(tooltipData[1].querySelector('.value').textContent).toBe('230') }) it('expands only hovered point', () => { moveMouse(1, 1) const circleList = d3.selectAll('.c3-circle') expect(circleList.size()).toBeGreaterThan(0) // nothing expanded circleList.each(function() { expect((this as any).classList.contains('_expanded_')).toBeFalsy() }) moveMouse(45, 233) circleList.each(function() { expect((this as any).classList.contains('_expanded_')).toEqual( (this as any).classList.contains('c3-circle-0') && (this as any).parentElement.classList.contains('c3-circles-data2') ) }) }) it('shows cursor:pointer only if hovering point', () => { const eventRect = d3.select('.c3-event-rect') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') moveMouse(49, 219) expect(eventRect.style('cursor')).toEqual('pointer') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') }) it('clicks only on hovered point', () => { clickMouse(144, 201) expect(clickedData).toEqual([ { x: 1, index: 1, value: 200, id: 'data1', name: 'data1' } ]) }) describe('with selection enabled', () => { beforeAll(() => { args.data.selection = { enabled: true, isselectable: function(d) { return d.id !== 'data3' } } }) it('can toggle selection', () => { expect(d3.selectAll('.c3-circle _selected_').size()).toEqual(0) clickMouse(144, 201) // index 1 @ data1 expect(d3.selectAll('.c3-circle._selected_').size()).toEqual(1) expect( d3.select('.c3-circles-data1 .c3-circle-1._selected_').size() ).toEqual(1) // data3 is not selectable clickMouse(391, 283) // index 3 @ data3 expect(d3.selectAll('.c3-circle._selected_').size()).toEqual(1) expect( d3.select('.c3-circles-data3 .c3-circle-3._selected_').size() ).toEqual(0) expect( d3.select('.c3-circles-data1 .c3-circle-1._selected_').size() ).toEqual(1) clickMouse(343, 204) // index 3 @ data2 expect(d3.selectAll('.c3-circle._selected_').size()).toEqual(2) expect( d3.select('.c3-circles-data2 .c3-circle-3._selected_').size() ).toEqual(1) expect( d3.select('.c3-circles-data1 .c3-circle-1._selected_').size() ).toEqual(1) clickMouse(144, 201) // index 1 @ data1 expect(d3.selectAll('.c3-circle._selected_').size()).toEqual(1) expect( d3.select('.c3-circles-data2 .c3-circle-3._selected_').size() ).toEqual(1) }) }) describe('with tooltip_horizontal=true', () => { beforeAll(() => { args.tooltip.horizontal = true }) it('can clicks on points', () => { // out of point sensitivity clickMouse(146, 46) clickMouse(343, 263) // click 3 data point clickMouse(147, 370) clickMouse(340, 203) clickMouse(537, 386) expect(clickedData).toEqual([ { x: 1, value: -200, id: 'data3', index: 1, name: 'data3' }, { x: 3, value: 200, id: 'data2', index: 3, name: 'data2' }, { x: 5, value: -250, id: 'data1', index: 5, name: 'data1' } ]) }) it('shows tooltip with only closest data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style .display ).toEqual('none') moveMouse(146, 46) expect( (document.querySelector('.c3-tooltip-container') as any).style .display ).toEqual('block') let tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data1 expect(tooltipData[1].querySelector('.name').textContent).toBe( 'data1' ) expect(tooltipData[1].querySelector('.value').textContent).toBe('200') moveMouse(343, 263) expect( (document.querySelector('.c3-tooltip-container') as any).style .display ).toEqual('block') tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data3 expect(tooltipData[1].querySelector('.name').textContent).toBe( 'data3' ) expect(tooltipData[1].querySelector('.value').textContent).toBe('0') }) }) }) describe('tooltip_grouped=true', function() { let clickedData = [] beforeAll(() => { args = { data: { columns: [ ['data1', 30, 200, 200, 400, 150, -250], ['data2', 130, -100, 100, 200, 150, 50], ['data3', 230, -200, 200, 0, 250, 250] ], type: 'line', groups: [['data1', 'data2']], onclick: function(d) { clickedData.push(d) } }, tooltip: { grouped: true }, axis: { x: { type: 'category' } }, interaction: { enabled: true }, point: { r: 2, sensitivity: 10, focus: { expand: { enabled: true, r: 8 } } } } }) beforeEach(function() { clickedData = [] }) describe('with tooltip_horizontal=true', () => { beforeAll(() => { args.tooltip.horizontal = true }) it('can clicks on points', () => { // out of point sensitivity clickMouse(146, 46) clickMouse(343, 263) // click 3 data point clickMouse(147, 370) clickMouse(340, 203) clickMouse(537, 386) expect(clickedData).toEqual([ { x: 1, value: -200, id: 'data3', index: 1, name: 'data3' }, { x: 3, value: 200, id: 'data2', index: 3, name: 'data2' }, { x: 5, value: -250, id: 'data1', index: 5, name: 'data1' } ]) }) it('shows tooltip with all data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style .display ).toEqual('block') let tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(4) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe( 'data1' ) expect(tooltipData[1].querySelector('.value').textContent).toBe('30') expect(tooltipData[2].querySelector('.name').textContent).toBe( 'data3' ) expect(tooltipData[2].querySelector('.value').textContent).toBe('230') expect(tooltipData[3].querySelector('.name').textContent).toBe( 'data2' ) expect(tooltipData[3].querySelector('.value').textContent).toBe('130') moveMouse(146, 46) expect( (document.querySelector('.c3-tooltip-container') as any).style .display ).toEqual('block') tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(4) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe( 'data1' ) expect(tooltipData[1].querySelector('.value').textContent).toBe('200') expect(tooltipData[2].querySelector('.name').textContent).toBe( 'data3' ) expect(tooltipData[2].querySelector('.value').textContent).toBe( '-200' ) expect(tooltipData[3].querySelector('.name').textContent).toBe( 'data2' ) expect(tooltipData[3].querySelector('.value').textContent).toBe( '-100' ) }) }) }) }) describe('line chart (multiple xs)', function() { let clickedData = [] beforeAll(() => { args = { data: { xs: { data1: 'x1', data2: 'x2' }, columns: [ ['x1', 10, 30, 45, 50, 70, 100], ['x2', 30, 50, 75, 100, 120], ['data1', 30, 200, 100, 400, 150, 250], ['data2', 20, 180, 240, 100, 190] ], type: 'line', onclick: function(d) { clickedData.push(d) } }, tooltip: { grouped: true, horizontal: true }, interaction: { enabled: true } } }) beforeEach(function() { clickedData = [] }) it('shows tooltip with all data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') let tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data[1] expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[1].querySelector('.value').textContent).toBe('30') moveMouse(107, 95) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') tooltipData = [...(document.querySelectorAll('.c3-tooltip tr') as any)] expect(tooltipData.length).toBe(3) // header + data[12] expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[1].querySelector('.value').textContent).toBe('200') expect(tooltipData[2].querySelector('.name').textContent).toBe('data2') expect(tooltipData[2].querySelector('.value').textContent).toBe('20') moveMouse(430, 140) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') tooltipData = [...(document.querySelectorAll('.c3-tooltip tr') as any)] expect(tooltipData.length).toBe(3) // header + data[12] expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[1].querySelector('.value').textContent).toBe('250') expect(tooltipData[2].querySelector('.name').textContent).toBe('data2') expect(tooltipData[2].querySelector('.value').textContent).toBe('100') }) }) describe('scatter chart', function() { describe('tooltip_grouped=true', function() { beforeAll(() => { args = { data: { columns: [ ['data1', 30, null, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'scatter' }, tooltip: { grouped: true }, interaction: { enabled: true } } }) it('shows tooltip with visible data of currently hovered category', () => { moveMouse(20, 20) let tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(4) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe('data2') expect(tooltipData[1].querySelector('.value').textContent).toBe('50') expect(tooltipData[2].querySelector('.name').textContent).toBe('data1') expect(tooltipData[2].querySelector('.value').textContent).toBe('30') expect(tooltipData[3].querySelector('.name').textContent).toBe('data3') expect(tooltipData[3].querySelector('.value').textContent).toBe('-150') moveMouse(350, 354) tooltipData = [...(document.querySelectorAll('.c3-tooltip tr') as any)] expect(tooltipData.length).toBe(4) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[1].querySelector('.value').textContent).toBe('400') expect(tooltipData[2].querySelector('.name').textContent).toBe('data3') expect(tooltipData[2].querySelector('.value').textContent).toBe('140') expect(tooltipData[3].querySelector('.name').textContent).toBe('data2') expect(tooltipData[3].querySelector('.value').textContent).toBe('40') }) it('shows x grid', () => { moveMouse(20, 20) expect(d3.select('.c3-xgrid-focus').style('visibility')).toBe('visible') }) }) }) describe('area chart (timeseries)', function() { describe('tooltip_grouped=true', function() { beforeAll(() => { args = { data: { x: 'x', columns: [ [ 'x', '2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06' ], ['data1', 30, 200, 200, 400, 150, 250], ['data2', 130, 100, 100, 200, 150, 50], ['data3', 230, 200, 200, 0, 250, 250] ], type: 'area', groups: [['data1', 'data2', 'data3']] }, tooltip: { grouped: true }, axis: { x: { type: 'timeseries' } }, interaction: { enabled: true } } }) it('shows tooltip with visible data of currently hovered category', () => { moveMouse(20, 20) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') const tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(4) // header + data[123] expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[2].querySelector('.name').textContent).toBe('data3') expect(tooltipData[3].querySelector('.name').textContent).toBe('data2') }) it('shows cursor:pointer only if hovering area', () => { const eventRect = d3.select('.c3-event-rect') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') moveMouse(360, 48) expect(eventRect.style('cursor')).toEqual('pointer') moveMouse(1, 1) expect(eventRect.style('cursor')).toEqual('auto') }) }) describe('tooltip_grouped=false', function() { beforeAll(() => { args = { data: { x: 'x', columns: [ [ 'x', '2018-01-01', '2018-01-02', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06' ], ['data1', 30, 200, 200, 400, 150, 250], ['data2', 130, 100, 100, 200, 150, 50], ['data3', 230, 200, 200, 0, 250, 250] ], type: 'area', groups: [['data1', 'data2', 'data3']] }, tooltip: { grouped: false }, axis: { x: { type: 'timeseries' }, rotated: false }, interaction: { enabled: true } } }) it('shows tooltip with only hovered data', () => { moveMouse(1, 1) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('none') moveMouse(5, 174) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('block') const tooltipData = [ ...(document.querySelectorAll('.c3-tooltip tr') as any) ] expect(tooltipData.length).toBe(2) // header + data1 expect(tooltipData[1].querySelector('.name').textContent).toBe('data1') expect(tooltipData[1].querySelector('.value').textContent).toBe('30') }) }) }) describe('disabled', function() { beforeAll(() => { args = { data: { columns: [ ['data1', 30, 200, 200, 400, 150, -250], ['data2', 130, -100, 100, 200, 150, 50], ['data3', 230, -200, 200, 0, 250, 250] ], type: 'bar', groups: [['data1', 'data2']] }, axis: { x: { type: 'category' } }, interaction: { enabled: false } } }) it('generate a single rect', () => { const eventRectList = d3.selectAll('.c3-event-rect') expect(eventRectList.size()).toBe(1) expect(eventRectList.attr('x')).toEqual('0') expect(eventRectList.attr('y')).toEqual('0') expect(eventRectList.attr('height')).toEqual('' + chart.internal.height) expect(eventRectList.attr('width')).toEqual('' + chart.internal.width) }) it('does not show tooltip when hovering data', () => { moveMouse(40, 260) expect( (document.querySelector('.c3-tooltip-container') as any).style.display ).toEqual('none') }) it('does not show cursor:pointer when hovering data', () => { moveMouse(40, 260) expect(d3.select('.c3-event-rect').style('cursor')).toEqual('auto') }) }) }) ================================================ FILE: spec/legend-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart legend', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('legend when multiple charts rendered', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30], ['data2', 50], ['data3', 100] ] } } }) describe('long data names', function() { beforeAll(function() { args = { data: { columns: [ ['long data name 1', 30], ['long data name 2', 50], ['long data name 3', 50] ] } } }) it('should have properly computed legend width', function() { var expectedLeft = [148, 226, 384], expectedWidth = [118, 118, 108] d3.selectAll('.c3-legend-item').each(function(d, i) { var rect = (d3.select(this).node() as any).getBoundingClientRect() expect(rect.left).toBeCloseTo(expectedLeft[i], -2) expect(rect.width).toBeCloseTo(expectedWidth[i], -2) }) }) }) }) describe('legend position', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] } } }) it('should be located on the center of chart', function() { var box = chart.internal.legend.node().getBoundingClientRect() expect(box.left + box.right).toBe(638) }) }) describe('legend as inset', function() { describe('should change the legend to "inset" successfully', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25] ] }, legend: { position: 'inset', inset: { step: null } } } }) it('should be positioned properly', function() { var box = (d3 .select('.c3-legend-background') .node() as any).getBoundingClientRect() expect(box.top).toBe(5.5) expect(box.left).toBeGreaterThan(30) }) it('should have automatically calculated height', function() { var box = (d3 .select('.c3-legend-background') .node() as any).getBoundingClientRect() expect(box.height).toBe(48) }) }) describe('should change the legend step to 1 successfully', function() { beforeAll(function() { args.legend.inset.step = 1 }) it('should have automatically calculated height', function() { var box = (d3 .select('.c3-legend-background') .node() as any).getBoundingClientRect() expect(box.height).toBe(28) }) }) describe('should change the legend step to 2 successfully', function() { beforeAll(function() { args.legend.inset.step = 2 }) it('should have automatically calculated height', function() { var box = (d3 .select('.c3-legend-background') .node() as any).getBoundingClientRect() expect(box.height).toBe(48) }) }) describe('with only one series', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, legend: { position: 'inset' } } }) it('should locate legend properly', function() { var box = (d3 .select('.c3-legend-background') .node() as any).getBoundingClientRect() expect(box.height).toBe(28) expect(box.width).toBeGreaterThan(64) }) }) }) describe('legend.hide', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 200, 100, 250, 150] ] }, legend: { hide: true } } }) it('should not show legends', function() { d3.selectAll('.c3-legend-item').each(function() { expect(d3.select(this).style('visibility')).toBe('hidden') }) }) describe('hidden legend', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 200, 100, 250, 150] ] }, legend: { hide: 'data2' } } }) it('should not show legends', function() { expect(d3.select('.c3-legend-item-data1').style('visibility')).toBe( 'visible' ) expect(d3.select('.c3-legend-item-data2').style('visibility')).toBe( 'hidden' ) }) }) }) describe('legend.show', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 200, 100, 250, 150] ] }, legend: { show: false } } }) it('should not initially have rendered any legend items', function() { expect(d3.selectAll('.c3-legend-item').empty()).toBe(true) }) it('allows us to show the legend on showLegend call', function() { chart.legend.show() d3.selectAll('.c3-legend-item').each(function() { expect(d3.select(this).style('visibility')).toBe('visible') // This selects all the children, but we expect it to be empty expect((d3.select(this).selectAll('*') as any).length).not.toEqual(0) }) }) }) describe('with legend.show is true', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 200, 100, 250, 150] ] }, legend: { show: true } } }) it('should initially have rendered some legend items', function() { expect(d3.selectAll('.c3-legend-item').empty()).toBe(false) }) it('should remove rendered every legend items', function() { chart.legend.hide() d3.selectAll('.c3-legend-item').each(function() { expect(d3.select(this).style('visibility')).toBe('hidden') // This selects all the children, but we expect it to be empty expect((d3.select(this).selectAll('*') as any).length).toEqual( undefined ) }) }) }) describe('custom legend size', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 130, 100, 200, 100, 250, 150] ] }, legend: { item: { tile: { width: 15, height: 2 } } } } }) it('renders the legend item with the correct width and height', function() { d3.selectAll('.c3-legend-item-tile').each(function() { expect(d3.select(this).style('stroke-width')).toBe( args.legend.item.tile.height + 'px' ) var tileWidth = Number(d3.select(this).attr('x2')) - Number(d3.select(this).attr('x1')) expect(tileWidth).toBe(args.legend.item.tile.width) }) }) }) describe('custom legend padding', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 30, 200, 100, 400, 150, 250], ['padded2', 130, 100, 200, 100, 250, 150] ] }, legend: { padding: 10 } } }) it('renders the correct amount of padding on the legend element', function() { d3.selectAll( '.c3-legend-item-padded1 .c3-legend-item-tile, .c3-legend-item-padded2 .c3-legend-item-tile' ).each(function(el, index) { var itemWidth = (d3.select(this).node() as any).parentNode.getBBox() .width, textBoxWidth = (d3 .select((d3.select(this).node() as any).parentNode) .select('text') .node() as any).getBBox().width, tileWidth = 17, // default value is 10, plus 7 more for padding @TODO verify this, seems PhantomJS@^2 adds another 1px to each side expectedWidth = textBoxWidth + tileWidth + (index ? 0 : 10) + args.legend.padding expect(itemWidth).toBe(expectedWidth) }) }) }) describe('legend item tile coloring with color_treshold', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 100], ['padded2', 90], ['padded3', 50], ['padded4', 20] ] }, type: 'gauge', color: { pattern: ['#FF0000', '#F97600', '#F6C600', '#60B044'], threshold: { values: [30, 80, 95] } } } }) // espacially for gauges with multiple arcs to have the same coloring between legend tiles, tooltip tiles and arc it('selects the color from color_pattern if color_treshold is given', function() { var tileColor = [] d3.selectAll('.c3-legend-item-tile').each(function() { tileColor.push(d3.select(this).style('stroke')) }) expect(tileColor[0]).toBe('rgb(96, 176, 68)') expect(tileColor[1]).toBe('rgb(246, 198, 0)') expect(tileColor[2]).toBe('rgb(249, 118, 0)') expect(tileColor[3]).toBe('rgb(255, 0, 0)') }) }) describe('legend item tile coloring with color_treshold (more than one data value)', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 40, 60], ['padded2', 100, -10], ['padded3', 0, 50], ['padded4', 20, 0] ] }, type: 'gauge', color: { pattern: ['#FF0000', '#F97600', '#F6C600', '#60B044'], threshold: { values: [30, 80, 95] } } } }) // espacially for gauges with multiple arcs to have the same coloring between legend tiles, tooltip tiles and arc it('selects the color from color_pattern if color_treshold is given', function() { var tileColor = [] d3.selectAll('.c3-legend-item-tile').each(function() { tileColor.push(d3.select(this).style('stroke')) }) expect(tileColor[0]).toBe('rgb(96, 176, 68)') expect(tileColor[1]).toBe('rgb(246, 198, 0)') expect(tileColor[2]).toBe('rgb(249, 118, 0)') expect(tileColor[3]).toBe('rgb(255, 0, 0)') }) }) describe('legend item tile coloring without color_treshold', function() { beforeAll(function() { args = { data: { columns: [ ['padded1', 100], ['padded2', 90], ['padded3', 50], ['padded4', 20] ], colors: { padded1: '#60b044', padded4: '#8b008b' } }, type: 'gauge' } }) it('selects the color from data_colors, data_color or default', function() { var tileColor = [] d3.selectAll('.c3-legend-item-tile').each(function() { tileColor.push(d3.select(this).style('stroke')) }) expect(tileColor[0]).toBe('rgb(96, 176, 68)') expect(tileColor[1]).toBe('rgb(31, 119, 180)') expect(tileColor[2]).toBe('rgb(255, 127, 14)') expect(tileColor[3]).toBe('rgb(139, 0, 139)') }) }) }) ================================================ FILE: spec/shape.bar-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart shape bar', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('Path boxes', function() { beforeAll(function() { args = { data: { columns: [['data1', 30]], type: 'bar' }, bar: { width: { max: 40 } } } }) it('bars should have expected Path Box', function() { var expected = { x: 279, y: 40, width: 40, height: 387 } var shapes = chart.internal.main .selectAll('.' + chart.internal.CLASS.shapes) .selectAll('.' + chart.internal.CLASS.shape) shapes.each(function() { var pathBox = chart.internal.getPathBox(this) expect(pathBox.x).toBeCloseTo(expected.x, -1) expect(pathBox.y).toBeCloseTo(expected.y, -1) expect(pathBox.width).toBeCloseTo(expected.width, -1) expect(pathBox.height).toBeCloseTo(expected.height, -1) }) }) }) describe('with groups', function() { describe('with indexed data', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, -100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], groups: [['data1', 'data2']], type: 'bar' } } }) it('should be stacked', function() { var expectedBottom = [275, 293, 365, 281, 395, 290] chart.internal.main .selectAll('.c3-bars-data1 .c3-bar') .each(function(d, i) { var rect = d3 .select(this) .node() .getBoundingClientRect() expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1) }) }) }) describe('with timeseries data', function() { beforeAll(function() { args = { data: { x: 'date', columns: [ [ 'date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29' ], ['data1', 30, 200, -100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], groups: [['data1', 'data2']], type: 'bar' }, axis: { x: { type: 'timeseries' } } } }) it('should be stacked', function() { var expectedBottom = [275, 293, 365, 281, 395, 290] chart.internal.main .selectAll('.c3-bars-data1 .c3-bar') .each(function(d, i) { var rect = d3 .select(this) .node() .getBoundingClientRect() expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1) }) }) }) describe('with category data', function() { beforeAll(function() { args = { data: { x: 'date', columns: [ [ 'date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29' ], ['data1', 30, 200, -100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], groups: [['data1', 'data2']], type: 'bar' }, axis: { x: { type: 'category' } } } }) it('should be stacked', function() { var expectedBottom = [275, 293, 365, 281, 395, 290] chart.internal.main .selectAll('.c3-bars-data1 .c3-bar') .each(function(d, i) { var rect = d3 .select(this) .node() .getBoundingClientRect() expect(rect.bottom).toBeCloseTo(expectedBottom[i], -1) }) }) }) }) describe('internal.isWithinBar', function() { describe('with normal axis', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'bar' }, axis: { rotated: false } } }) it('should not be within bar', function() { var bar = d3.select('.c3-target-data1 .c3-bar-0').node() expect(chart.internal.isWithinBar([0, 0], bar)).toBeFalsy() }) it('should be within bar', function() { var bar = d3.select('.c3-target-data1 .c3-bar-0').node() expect(chart.internal.isWithinBar([31, 280], bar)).toBeTruthy() }) it('should not be within bar of negative value', function() { var bar = d3.select('.c3-target-data3 .c3-bar-0').node() expect(chart.internal.isWithinBar([68, 280], bar)).toBeFalsy() }) it('should be within bar of negative value', function() { var bar = d3.select('.c3-target-data3 .c3-bar-0').node() expect(chart.internal.isWithinBar([68, 350], bar)).toBeTruthy() }) }) describe('with rotated axis', function() { beforeAll(function() { args.axis.rotated = true }) it('should not be within bar', function() { var bar = d3.select('.c3-target-data1 .c3-bar-0').node() expect(chart.internal.isWithinBar([0, 0], bar)).toBeFalsy() }) it('should be within bar', function() { var bar = d3.select('.c3-target-data1 .c3-bar-0').node() expect(chart.internal.isWithinBar([190, 20], bar)).toBeTruthy() }) it('should be within bar of negative value', function() { var bar = d3.select('.c3-target-data3 .c3-bar-0').node() expect(chart.internal.isWithinBar([68, 50], bar)).toBeTruthy() }) }) }) describe('bar spacing', function() { var createArgs = function(spacing) { return { size: { width: 500 }, data: { columns: [ ['data1', 30, 200, 100], ['data2', 50, 20, 10], ['data3', 150, 120, 110], ['data4', 12, 24, 20] ], type: 'bar', groups: [['data1', 'data4']] }, bar: { space: spacing } } } var getBBox = function(selector) { return d3 .select(selector) .node() .getBBox() } var getBarContainerWidth = function() { return parseInt(getBBox('.c3-chart-bars').width) } var getBarContainerOffset = function() { return parseInt(getBBox('.c3-chart-bars').x) } var getBarBBox = function(name, idx) { return getBBox('.c3-target-' + name + ' .c3-bar-' + (idx || 0)) } var getBarWidth = function(name, idx) { return parseInt(getBarBBox(name, idx).width) } var getBarOffset = function(name1, name2, idx) { var bbox1 = getBarBBox(name1, idx) var bbox2 = getBarBBox(name2, idx) return Math.floor(bbox2.x - (bbox1.x + bbox1.width)) } it('should set bar spacing to 0', function() { args = createArgs(0) expect(true).toBeTruthy() }) it('should display the bars without any spacing', function() { // all bars should have the same width expect(getBarWidth('data1', 0)).toEqual(30) expect(getBarWidth('data2', 0)).toEqual(30) expect(getBarWidth('data3', 0)).toEqual(30) expect(getBarWidth('data1', 1)).toEqual(30) expect(getBarWidth('data2', 1)).toEqual(30) expect(getBarWidth('data3', 1)).toEqual(30) expect(getBarWidth('data1', 2)).toEqual(30) expect(getBarWidth('data2', 2)).toEqual(30) expect(getBarWidth('data3', 2)).toEqual(30) // all offsets should be the same expect(getBarOffset('data1', 'data2', 0)).toEqual(0) expect(getBarOffset('data2', 'data3', 0)).toEqual(0) expect(getBarOffset('data1', 'data2', 1)).toEqual(0) expect(getBarOffset('data2', 'data3', 1)).toEqual(0) expect(getBarOffset('data1', 'data2', 2)).toEqual(0) expect(getBarOffset('data2', 'data3', 2)).toEqual(0) // default width/offset of the container for this chart expect(getBarContainerWidth()).toEqual(396) expect(getBarContainerOffset()).toEqual(31) }) it('should set bar spacing to 0.25', function() { args = createArgs(0.25) expect(true).toBeTruthy() }) it('should display the bars with a spacing ratio of 0.25', function() { // with bar_space of 0.25, the space between bars is // expected to be 25% of the original bar's width // which is ~7 // expect all bars to be the same width expect(getBarWidth('data1', 0)).toEqual(22) expect(getBarWidth('data2', 0)).toEqual(22) expect(getBarWidth('data3', 0)).toEqual(22) expect(getBarWidth('data1', 1)).toEqual(22) expect(getBarWidth('data2', 1)).toEqual(22) expect(getBarWidth('data3', 1)).toEqual(22) expect(getBarWidth('data1', 2)).toEqual(22) expect(getBarWidth('data2', 2)).toEqual(22) expect(getBarWidth('data3', 2)).toEqual(22) // all offsets should be the same expect(getBarOffset('data1', 'data2', 0)).toEqual(7) expect(getBarOffset('data2', 'data3', 0)).toEqual(7) expect(getBarOffset('data1', 'data2', 1)).toEqual(7) expect(getBarOffset('data2', 'data3', 1)).toEqual(7) expect(getBarOffset('data1', 'data2', 2)).toEqual(7) expect(getBarOffset('data2', 'data3', 2)).toEqual(7) // expect the container to shrink a little because of // the offsets from the first/last chart // we add/subtract 1 because of approximation due to rounded values expect(getBarContainerWidth()).toEqual(396 - 7 - 1) expect(getBarContainerOffset()).toEqual(31 + (Math.floor(7 / 2) + 1)) }) it('should set bar spacing to 0.5', function() { args = createArgs(0.5) expect(true).toBeTruthy() }) it('should display the bars with a spacing ratio of 0.5', function() { // with bar_space of 0.5, the space between bars is // expected to be 50% of the original bar's width // which is ~15 // expect all bars to be the same width expect(getBarWidth('data1', 0)).toEqual(15) expect(getBarWidth('data2', 0)).toEqual(15) expect(getBarWidth('data3', 0)).toEqual(15) expect(getBarWidth('data1', 1)).toEqual(15) expect(getBarWidth('data2', 1)).toEqual(15) expect(getBarWidth('data3', 1)).toEqual(15) expect(getBarWidth('data1', 2)).toEqual(15) expect(getBarWidth('data2', 2)).toEqual(15) expect(getBarWidth('data3', 2)).toEqual(15) // all offsets should be the same expect(getBarOffset('data1', 'data2', 0)).toEqual(15) expect(getBarOffset('data2', 'data3', 0)).toEqual(15) expect(getBarOffset('data1', 'data2', 1)).toEqual(15) expect(getBarOffset('data2', 'data3', 1)).toEqual(15) expect(getBarOffset('data1', 'data2', 2)).toEqual(15) expect(getBarOffset('data2', 'data3', 2)).toEqual(15) // expect the container to shrink a little because of // the offsets from the first/last chart expect(getBarContainerWidth()).toEqual(396 - 15) expect(getBarContainerOffset()).toEqual(31 + Math.floor(15 / 2)) }) }) }) ================================================ FILE: spec/shape.line-spec.ts ================================================ import { parseSvgPath } from './svg-helper' import { d3, initChart } from './c3-helper' describe('c3 chart shape line', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('shape-rendering for line chart', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'line' }, line: { step: { type: 'step' } } } }) it('Should render the lines correctly', function(done) { setTimeout(function() { var target = chart.internal.main.select( '.c3-chart-line.c3-target-data1' ) var commands = parseSvgPath(target.select('.c3-line-data1').attr('d')) expect(commands.length).toBe(6) done() }, 500) }) it("should not have shape-rendering when it's line chart", function() { d3.selectAll('.c3-line').each(function() { var style = d3.select(this).style('shape-rendering') expect(style).toBe('auto') }) }) describe('should change to step chart', function() { beforeAll(function() { args.data.type = 'step' }) it("should have shape-rendering = crispedges when it's step chart", function() { d3.selectAll('.c3-line').each(function() { var style = d3 .select(this) .style('shape-rendering') .toLowerCase() expect(style).toBe('crispedges') }) }) }) describe('should change to step chart with step-after', function() { beforeAll(function() { args.line.step.type = 'step-after' }) it("should have shape-rendering = crispedges when it's step chart", function() { d3.selectAll('.c3-line').each(function() { var style = d3 .select(this) .style('shape-rendering') .toLowerCase() expect(style).toBe('crispedges') }) }) }) describe('should change to step chart with step-before', function() { beforeAll(function() { args.line.step.type = 'step-before' }) it("should have shape-rendering = crispedges when it's step chart", function() { d3.selectAll('.c3-line').each(function() { var style = d3 .select(this) .style('shape-rendering') .toLowerCase() expect(style).toBe('crispedges') }) }) }) describe('should change to spline chart', function() { beforeAll(function() { args.data.type = 'spline' }) it('should use cardinal interpolation by default', function() { expect(chart.internal.config.spline_interpolation_type).toBe('cardinal') }) }) }) describe('point.show option', function() { describe('should change args to include null data', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, null, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'line' } } }) it('should not show the circle for null', function(done) { setTimeout(function() { var target = chart.internal.main.select( '.c3-chart-line.c3-target-data1' ) expect(+target.select('.c3-circle-0').style('opacity')).toBe(1) expect(+target.select('.c3-circle-1').style('opacity')).toBe(0) expect(+target.select('.c3-circle-2').style('opacity')).toBe(1) done() }, 500) }) it('should not draw a line segment for null data', function(done) { setTimeout(function() { var target = chart.internal.main.select( '.c3-chart-line.c3-target-data1' ) var commands = parseSvgPath(target.select('.c3-line-data1').attr('d')) var segments = 0 for (var i = 0; i < commands.length; i++) { commands[i].command === 'L' ? segments++ : null } expect(segments).toBe(3) done() }, 500) }) // it('should change args to include null data on scatter plot', function () { // args = { // data: { // columns: [ // ['data1', 30, null, 100, 400, -150, 250], // ['data2', 50, 20, 10, 40, 15, 25], // ['data3', -150, 120, 110, 140, 115, 125] // ], // type: 'scatter' // } // }; // expect(true).toBeTruthy(); // }); // it('should not show the circle for null', function (done) { // setTimeout(function () { // var target = chart.internal.main.select('.c3-chart-line.c3-target-data1'); // expect(+target.select('.c3-circle-0').style('opacity')).toBe(0.5); // expect(+target.select('.c3-circle-1').style('opacity')).toBe(0); // expect(+target.select('.c3-circle-2').style('opacity')).toBe(0.5); // done(); // }, 500); // }); }) describe('should allow passing a function', function() { beforeAll(function() { args = { data: { columns: [['data1', 30, 50, 100]], type: 'line' }, point: { show: function(d) { return d.value > 50 } } } }) it('should show point if function returns true', function() { var target = chart.internal.main.select( '.c3-chart-line.c3-target-data1' ) expect(+target.select('.c3-circle-0').style('opacity')).toBe(0) expect(+target.select('.c3-circle-1').style('opacity')).toBe(0) expect(+target.select('.c3-circle-2').style('opacity')).toBe(1) }) }) }) describe('spline.interpolation option', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', -150, 120, 110, 140, 115, 125] ], type: 'spline' }, spline: { interpolation: { type: 'monotone' } } } }) it('updates interpolation function', function() { expect(chart.internal.getInterpolate(chart.data()[0])).toBe( d3.curveMonotoneX ) }) describe('should not use a non-valid interpolation', function() { beforeAll(function() { args.spline.interpolation.type = 'foo' }) it('should use cardinal interpolation when given option is not valid', function() { expect(chart.internal.getInterpolate(chart.data()[0])).toBe( d3.curveCardinal ) }) }) }) }) ================================================ FILE: spec/stanford-spec.ts ================================================ import { initChart } from './c3-helper' import { getRegionArea, compareEpochs, pointInRegion } from '../src/stanford' describe('c3 stanford tests', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('count epochs in region', function() { beforeAll(function() { args = { data: { x: 'x', y: 'y', epochs: 'epochs', columns: [ ['x', 25, 35], ['y', 25, 33], ['epochs', 30, 35] ], type: 'stanford' } } }) it('should return 0 if the region has no epochs', function() { var region = [ { x: 0, y: 0 }, { x: 20, y: 0 }, { x: 20, y: 20 }, { x: 0, y: 20 } ] var result = chart.internal.countEpochsInRegion(region) expect(result.percentage).toBe(0) expect(result.value).toBe(0) }) it('should return 100% if the region has all the epochs', function() { var region = [ { x: 0, y: 0 }, { x: 60, y: 0 }, { x: 60, y: 60 }, { x: 0, y: 60 } ] var result = chart.internal.countEpochsInRegion(region) expect(Number(result.percentage)).toBe(100) expect(result.value).toBe(65) }) }) describe('get centroid of region', function() { beforeAll(function() { args = { data: { x: 'x', y: 'y', epochs: 'epochs', columns: [ ['x', 25, 35], ['y', 25, 33], ['epochs', 30, 35] ], type: 'stanford' } } }) var region = [ // a 20 x 20 square { x: 0, y: 0 }, { x: 20, y: 0 }, { x: 20, y: 20 }, { x: 0, y: 20 } ] it('should return the centroid of a polygon', function() { var result = chart.internal.getCentroid(region) expect(result.x).toBe(10) expect(result.y).toBe(10) }) }) describe('get region area', function() { var square = [ // a 20 x 20 square { x: 0, y: 0 }, { x: 20, y: 0 }, { x: 20, y: 20 }, { x: 0, y: 20 } ] var squareArea = 400 var triangle = [ // A = b * h / 2 { x: 0, y: 0 }, { x: 20, y: 20 }, { x: 0, y: 20 } ] var triangleArea = 200 it('should return the correct area for a square', function() { expect(Math.abs(getRegionArea(square))).toBe(squareArea) }) it('should return the correct area for a triangle', function() { expect(Math.abs(getRegionArea(triangle))).toBe(triangleArea) }) }) describe('compare epochs', function() { var dataBigger = { epochs: 2 } var dataLower = { epochs: 1 } it('should return -1 if epochs are lower', function() { expect(compareEpochs(dataLower, dataBigger)).toBe(-1) }) it('should return 1 if epochs are bigger', function() { expect(compareEpochs(dataBigger, dataLower)).toBe(1) }) it('should return 0 if epochs are equal', function() { expect(compareEpochs(dataLower, dataLower)).toBe(0) }) }) describe('check if point is in region', function() { var region = [ { x: 0, y: 0 }, { x: 20, y: 0 }, { x: 20, y: 20 }, { x: 20, y: 20 } ] var pointInside = { x: 0, value: 0 } var pointOutInside = { x: 21, value: 0 } it('should return true if point is inside region', function() { expect(pointInRegion(pointInside, region)).toBeTruthy() }) it('should return false if point is outside region', function() { expect(pointInRegion(pointOutInside, region)).toBeFalsy() }) }) }) ================================================ FILE: spec/subchart-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 subchart', function() { 'use strict' let chart let args = { data: { x: 'date', columns: [ [ 'date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29' ], ['data1', 30, 200, -100, 400, -150, 250], ['data2', 50, 20, 10, 40, 15, 25] ], groups: [['data1', 'data2']], type: 'bar' }, axis: { x: { type: 'category' } }, subchart: { show: true } } beforeEach(done => { chart = initChart(chart, args, done) }) const getChartHeight = () => +chart.internal.svg.select('.c3-event-rect').attr('height') - 1 describe('api', () => { it('can toggle subchart visibility', () => { const chartHeightWithSubchart = getChartHeight() expect(chart.subchart.isShown()).toBeTruthy() expect(chart.internal.svg.selectAll('.c3-axis-x').size()).toEqual(2) expect(chart.internal.svg.selectAll('.c3-brush').size()).toEqual(1) chart.subchart.hide() expect(chart.subchart.isShown()).toBeFalsy() expect(chart.internal.svg.selectAll('.c3-axis-x').size()).toEqual(1) expect(chart.internal.svg.selectAll('.c3-brush').size()).toEqual(0) expect(getChartHeight()).toBeGreaterThan(chartHeightWithSubchart) chart.subchart.show() expect(chart.subchart.isShown()).toBeTruthy() expect(chart.internal.svg.selectAll('.c3-axis-x').size()).toEqual(2) expect(chart.internal.svg.selectAll('.c3-brush').size()).toEqual(1) expect(getChartHeight()).toEqual(chartHeightWithSubchart) }) }) }) ================================================ FILE: spec/svg-helper.ts ================================================ /** * Parse the d property of an SVG path into an array of drawing commands. * @param {String} d SvgPath d attribute.] * @return {Array} an array of drawing commands. */ export function parseSvgPath(d) { //jshint ignore:line 'use strict' var commands = [] var commandTokens = ['M', 'L', 'I', 'H', 'V', 'C', 'S', 'Q', 'T', 'A'] var command var in_x = false var in_y = false var x = '' var y = '' for (var i = 0; i <= d.length; i++) { if (commandTokens.indexOf(d[i]) !== -1) { if (in_x || in_y) { commands.push({ command: command, x: x, y: y }) x = '' y = '' } command = d[i] in_x = true in_y = false } else { if (d[i] === ',') { if (in_y) { commands.push({ command: command, x: x, y: y }) x = '' y = '' } in_x = !in_x in_y = !in_y } else if (in_x) { x += d[i] } else if (in_y) { y += d[i] } } } if (d[i] !== ',' && in_y) { commands.push({ command: command, x: x, y: y }) } return commands } ================================================ FILE: spec/title-spec.ts ================================================ import { d3, initChart } from './c3-helper' describe('c3 chart title', function() { 'use strict' var chart, config describe('when given a title too long', function() { beforeEach(function(done) { config = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, title: { text: 'this is a very long title'.repeat(10), position: 'top-center' } } chart = initChart(chart, config, done) }) it('should not use negative x offset', function() { const titleEl = d3.select('.c3-title') expect(+titleEl.attr('x')).toBe(0) }) }) describe('when given a title config option', function() { describe('with no padding and no position', function() { beforeEach(function(done) { config = { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, title: { text: 'new title' } } chart = initChart(chart, config, done) }) it('renders the title at the default config position', function() { var titleEl = d3.select('.c3-title') expect(+titleEl.attr('x')).toBeCloseTo(294, -2) expect(+titleEl.attr('y')).toEqual( (titleEl.node() as any).getBBox().height ) }) it('renders the title text', function() { var titleEl = d3.select('.c3-title') expect((titleEl.node() as any).textContent).toEqual('new title') }) }) describe('with padding', function() { var config, getConfig = function(titlePosition) { return { data: { columns: [['data1', 30, 200, 100, 400, 150, 250]] }, title: { text: 'positioned title', padding: { top: 20, right: 30, bottom: 40, left: 50 }, position: titlePosition } } } describe('and position center', function() { beforeEach(function(done) { config = getConfig('top-center') chart = initChart(chart, config, done) }) it('renders the title at the default config position', function() { var titleEl = d3.select('.c3-title') expect(+titleEl.attr('x')).toBeCloseTo(275, -2) expect(+titleEl.attr('y')).toBeCloseTo(34, -1) }) it('adds the correct amount of padding to fit the title', function() { expect(chart.internal.getCurrentPaddingTop()).toEqual( config.title.padding.top + (d3.select('.c3-title').node() as any).getBBox().height + config.title.padding.bottom ) }) }) describe('and position left', function() { beforeEach(function(done) { config = getConfig('top-left') chart = initChart(chart, config, done) }) it('renders the title at the default config position', function() { var titleEl = d3.select('.c3-title') expect(+titleEl.attr('x')).toBeCloseTo(50, -1) expect(+titleEl.attr('y')).toBeCloseTo(34, -1) }) }) describe('and position right', function() { beforeEach(function(done) { config = getConfig('top-right') chart = initChart(chart, config, done) }) it('renders the title at the default config position', function() { var titleEl = d3.select('.c3-title') expect(+titleEl.attr('x')).toBeCloseTo(520, -2) expect(+titleEl.attr('y')).toBeCloseTo(34, -1) }) }) }) }) }) ================================================ FILE: spec/tooltip-spec.ts ================================================ import { d3, setMouseEvent, initChart } from './c3-helper' describe('c3 chart tooltip', function() { 'use strict' var chart var tooltipConfiguration = {} var dataOrder: any = 'desc' var dataGroups var args = function() { return { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], // 1130 ['data2', 50, 20, 10, 40, 15, 25], // 160 ['data3', 150, 120, 110, 140, 115, 125] // 760 ], order: dataOrder, groups: dataGroups }, tooltip: tooltipConfiguration } } beforeEach(function(done) { chart = initChart(chart, args(), done) dataOrder = 'desc' dataGroups = undefined }) describe('tooltip position', function() { beforeAll(function() { tooltipConfiguration = {} }) describe('without left margin', function() { it('should show tooltip on proper position', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(1), y = chart.internal.y(200) setMouseEvent(chart, 'mousemove', x, y, eventRect) var tooltipContainer = d3.select('.c3-tooltip-container'), top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')) expect(top).toBeGreaterThan(0) expect(left).toBeGreaterThan(0) }) }) describe('with left margin', function() { beforeAll(function() { d3.select('#chart').style('margin-left', '300px') }) it('should show tooltip on proper position', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(1) + 300, // add margin-left y = chart.internal.y(200) setMouseEvent(chart, 'mousemove', x, y, eventRect) var tooltipContainer = d3.select('.c3-tooltip-container'), top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')) expect(top).toBeGreaterThan(0) expect(left).toBeGreaterThan(0) }) afterAll(function() { d3.select('#chart').style('margin-left', null) }) }) }) describe('tooltip positionFunction', function() { var topExpected = 37, leftExpected = 79 beforeAll(function() { tooltipConfiguration = { position: function(data, width, height, element) { expect(data.length).toBe(args().data.columns.length) expect(data[0]).toEqual( jasmine.objectContaining({ index: 2, value: 100, id: 'data1' }) ) expect(width).toBeGreaterThan(0) expect(height).toBeGreaterThan(0) expect(element).toBe(d3.select('.c3-event-rect').node()) return { top: topExpected, left: leftExpected } } } }) it('should be set to the coordinate where the function returned', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var tooltipContainer = d3.select('.c3-tooltip-container'), top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')) expect(top).toBeGreaterThan(0) expect(left).toBeGreaterThan(0) }) }) describe('tooltip getTooltipContent', function() { beforeAll(function() { tooltipConfiguration = { data_order: 'desc' } }) it('should sort values desc', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data3') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data2') }) }) describe('tooltip with data_order as desc with grouped data', function() { beforeAll(function() { dataOrder = 'desc' dataGroups = [['data1', 'data2', 'data3']] }) it('should display each data in descending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data1') // 1130 expect(classes[2]).toBe('c3-tooltip-name--data3') // 760 expect(classes[3]).toBe('c3-tooltip-name--data2') // 160 }) }) describe('tooltip with data_order as asc with grouped data', function() { beforeAll(function() { dataOrder = 'asc' dataGroups = [['data1', 'data2', 'data3']] }) it('should display each data in ascending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') // 160 expect(classes[2]).toBe('c3-tooltip-name--data3') // 760 expect(classes[3]).toBe('c3-tooltip-name--data1') // 1130 }) }) describe('tooltip with data_order as NULL with grouped data', function() { beforeAll(function() { dataOrder = null dataGroups = [['data1', 'data2', 'data3']] }) it('should display each data in given order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data1') expect(classes[2]).toBe('c3-tooltip-name--data2') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with data_order as Function with grouped data', function() { beforeAll(function() { var order = ['data2', 'data1', 'data3'] dataOrder = function(data1, data2) { return order.indexOf(data1.id) - order.indexOf(data2.id) } dataGroups = [['data1', 'data2', 'data3']] }) it('should display each data in order given by function', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with data_order as Array with grouped data', function() { beforeAll(function() { dataOrder = ['data2', 'data1', 'data3'] dataGroups = [['data1', 'data2', 'data3']] }) it('should display each data in order given by array', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with data_order as desc with un-grouped data', function() { beforeAll(function() { dataOrder = 'desc' }) it('should display each tooltip value descending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data3') // 110 expect(classes[2]).toBe('c3-tooltip-name--data1') // 100 expect(classes[3]).toBe('c3-tooltip-name--data2') // 10 }) }) describe('tooltip with data_order as asc with un-grouped data', function() { beforeAll(function() { dataOrder = 'asc' }) it('should display each tooltip value in ascending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') // 10 expect(classes[2]).toBe('c3-tooltip-name--data1') // 100 expect(classes[3]).toBe('c3-tooltip-name--data3') // 110 }) }) describe('tooltip with data_order as NULL with un-grouped data', function() { beforeAll(function() { dataOrder = null }) it('should display each tooltip value in given data order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data1') expect(classes[2]).toBe('c3-tooltip-name--data2') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with data_order as Function with un-grouped data', function() { beforeAll(function() { var order = ['data2', 'data1', 'data3'] dataOrder = function(data1, data2) { return order.indexOf(data1.id) - order.indexOf(data2.id) } }) it('should display each tooltip value in data order given by function', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with data_order as Array with un-grouped data', function() { beforeAll(function() { dataOrder = ['data2', 'data1', 'data3'] }) it('should display each tooltip value in data order given by array', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with tooltip_order as desc', function() { beforeAll(function() { tooltipConfiguration = { order: 'desc' } // this should be ignored dataOrder = 'asc' dataGroups = [['data1', 'data2', 'data3']] }) it('should display each tooltip value descending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data3') // 110 expect(classes[2]).toBe('c3-tooltip-name--data1') // 100 expect(classes[3]).toBe('c3-tooltip-name--data2') // 10 }) }) describe('tooltip with tooltip_order as asc', function() { beforeAll(function() { tooltipConfiguration = { order: 'asc' } // this should be ignored dataOrder = 'desc' dataGroups = [['data1', 'data2', 'data3']] }) it('should display each tooltip value in ascending order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(220) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') // 10 expect(classes[2]).toBe('c3-tooltip-name--data1') // 100 expect(classes[3]).toBe('c3-tooltip-name--data3') // 110 }) }) describe('tooltip with tooltip_order as NULL', function() { beforeAll(function() { tooltipConfiguration = { order: null } }) it('should display each tooltip value in given order', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data1') expect(classes[2]).toBe('c3-tooltip-name--data2') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with tooltip_order as Function', function() { beforeAll(function() { var order = ['data2', 'data1', 'data3'] tooltipConfiguration = { order: function(data1, data2) { return order.indexOf(data1.id) - order.indexOf(data2.id) } } }) it('should display each tooltip value in data order given by function', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) describe('tooltip with tooltip_order as Array', function() { beforeAll(function() { tooltipConfiguration = { order: ['data2', 'data1', 'data3'] } }) it('should display each tooltip value in data order given by array', function() { var eventRect = d3.select('.c3-event-rect').node(), x = chart.internal.x(2), y = chart.internal.y(100) setMouseEvent(chart, 'mousemove', x, y, eventRect) var classes = d3 .selectAll('.c3-tooltip tr') .nodes() .map(function(node) { return (node as any).className }) expect(classes[0]).toBe('') // header expect(classes[1]).toBe('c3-tooltip-name--data2') expect(classes[2]).toBe('c3-tooltip-name--data1') expect(classes[3]).toBe('c3-tooltip-name--data3') }) }) }) ================================================ FILE: spec/type-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 chart types', function() { 'use strict' var chart, args beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('internal.hasArcType', function() { describe('with data', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ], type: 'pie' } } }) it('should return true', function() { expect(chart.internal.hasArcType()).toBeTruthy() }) describe('should change chart type to "bar"', function() { beforeAll(function() { args.data.type = 'bar' }) it('should return false', function() { expect(chart.internal.hasArcType()).toBeFalsy() }) }) }) describe('with empty data', function() { beforeAll(function() { args = { data: { columns: [], type: 'pie' } } }) it('should return true', function() { expect(chart.internal.hasArcType()).toBeTruthy() }) describe('should change chart type to "bar"', function() { beforeAll(function() { args.data.type = 'bar' }) it('should return false', function() { expect(chart.internal.hasArcType()).toBeFalsy() }) }) }) }) describe('internal.hasType', function() { beforeAll(function() { args = { data: { columns: [ ['data1', 30, 200, 100, 400, 150, 250], ['data2', 50, 20, 10, 40, 15, 25], ['data3', 150, 120, 110, 140, 115, 125] ], type: 'pie' } } }) it('should return true for "pie" type', function() { expect(chart.internal.hasType('pie')).toBeTruthy() }) it('should return false for "line" type', function() { expect(chart.internal.hasType('line')).toBeFalsy() }) it('should return false for "bar" type', function() { expect(chart.internal.hasType('bar')).toBeFalsy() }) describe('should unload successfully', function() { beforeAll(function() { chart.unload([]) }) it('should return true for "pie" type even if no data', function() { expect(chart.internal.hasType('pie')).toBeTruthy() }) it('should return false for "line" type even if no data', function() { expect(chart.internal.hasType('line')).toBeFalsy() }) it('should return false for "bar" type even if no data', function() { expect(chart.internal.hasType('bar')).toBeFalsy() }) describe('should change chart type to "bar" successfully', function() { beforeAll(function() { args.data.type = 'bar' }) it('should return false for "pie" type even if no data', function() { expect(chart.internal.hasType('pie')).toBeFalsy() }) it('should return false for "line" type even if no data', function() { expect(chart.internal.hasType('line')).toBeFalsy() }) it('should return true for "bar" type even if no data', function() { expect(chart.internal.hasType('bar')).toBeTruthy() }) }) }) }) }) ================================================ FILE: spec/util-spec.ts ================================================ import { asHalfPixel, ceil10, diffDomain, getOption, hasValue, isArray, isDefined, isEmpty, isFunction, isString, isUndefined, isValue, notEmpty, sanitise, isNumber, flattenArray, getIEVersion, isIE } from '../src/util' describe('util.js tests', function() { 'use strict' var undefined_var var html_str = '
Hello there
' var html_entities_str = '<div>Hello there</div>' var empty_string = '' var nonempty_string = 'hello there' var nonempty_number = 1234.2 var zero_number = 0 var null_var = null var empty_object = {} var nonempty_object = { a: 1 } var empty_array = [] var nonempty_array = [1, 3] var nonempty_function = function(a) { return a } describe('asHalfPixel, ceil10 and diffDomain functions', function() { it('asHalfPixel should return correct value', function() { expect(asHalfPixel(nonempty_number)).toBe(1235.5) }) it('ceil10 should return correct value', function() { expect(ceil10(nonempty_number)).toBe(1240) }) it('diffDomain should return correct value', function() { expect(diffDomain(nonempty_array)).toBe(2) }) }) describe('getOption and hasValue functions', function() { it('getOption should return value if options dict has specified key', function() { expect(getOption(nonempty_object, 'a', 'b')).toBe(1) }) it('getOption should return default value if options dict lacks specified key', function() { expect(getOption(empty_object, 'a', 'b')).toBe('b') }) it('hasValue should return true if dict has requested value', function() { expect(hasValue(nonempty_object, 1)).toBe(true) }) it('hasValue should return false if dict lacks requested value', function() { expect(hasValue(nonempty_object, 2)).toBe(false) }) }) describe('sanitise function', function() { it('should replace < and > tags', function() { expect(sanitise(html_str)).toBe(html_entities_str) }) it('should not modify a string not containing < and > tags', function() { expect(sanitise(html_entities_str)).toBe(html_entities_str) }) it('should not modify an imput whose type is not string', function() { expect(sanitise(nonempty_number)).toBe(nonempty_number) }) }) describe('flattenArray', function() { it('returns empty array for undefined value', function() { expect(flattenArray(undefined_var)).toEqual([]) }) it('returns flattened arrays (1 array)', function() { expect(flattenArray([[1]])).toEqual([1]) }) it('returns flatten arrays (n arrays)', function() { expect(flattenArray([[1], ['2']])).toEqual([1, '2']) }) }) describe('isArray, isDefined, isEmpty, isFunction, isNumber, isString, isUndefined, isValue and notEmpty functions', function() { it('isArray should return true for array var', function() { expect(isArray(nonempty_array)).toBe(true) }) it('isDefined should return true for defined var', function() { expect(isDefined(nonempty_string)).toBe(true) }) it('isFunction should return true for function var', function() { expect(isFunction(nonempty_function)).toBe(true) }) it('isString should return true for string var', function() { expect(isString(nonempty_string)).toBe(true) }) it('isUndefined should return true for undefined var', function() { expect(isUndefined(undefined_var)).toBe(true) }) it('isValue should return true for value var', function() { expect(isValue(nonempty_number)).toBe(1234.2) }) it('isArray should return false for non array var', function() { expect(isArray(nonempty_string)).toBe(false) }) it('isDefined should return false for non defined var', function() { expect(isDefined(undefined_var)).toBe(false) }) it('isFunction should return false for non function var', function() { expect(isFunction(nonempty_string)).toBe(false) }) it('isString should return false for non string var', function() { expect(isString(undefined_var)).toBe(false) }) it('isUndefined should return false for non undefined var', function() { expect(isUndefined(nonempty_string)).toBe(false) }) it('isValue should return false for null var', function() { expect(isValue(null_var)).toBe(false) }) it('isNumber should return false for undefined var', function() { expect(isNumber(undefined_var)).toBe(false) }) it('isNumber should return false for null var', function() { expect(isNumber(null_var)).toBe(false) }) it('isNumber should return false for string var', function() { expect(isNumber(nonempty_string)).toBe(false) }) it('isNumber should return true for nonempty_number', function() { expect(isNumber(nonempty_number)).toBe(true) }) it('isNumber should return true for zero_number', function() { expect(isNumber(zero_number)).toBe(true) }) it('isEmpty should return false for nonempty_array', function() { expect(isEmpty(nonempty_array)).toBe(false) }) it('isEmpty should return false for nonempty_number', function() { expect(isEmpty(nonempty_number)).toBe(false) }) it('isEmpty should return false for nonempty_object', function() { expect(isEmpty(nonempty_object)).toBe(false) }) it('isEmpty should return false for nonempty_string', function() { expect(isEmpty(nonempty_string)).toBe(false) }) it('isEmpty should return true for empty_array', function() { expect(isEmpty(empty_array)).toBe(true) }) it('isEmpty should return true for empty_object', function() { expect(isEmpty(empty_object)).toBe(true) }) it('isEmpty should return true for empty_string', function() { expect(isEmpty(empty_string)).toBe(true) }) it('isEmpty should return true for null_var', function() { expect(isEmpty(null_var)).toBe(true) }) it('isEmpty should return true for undefined_var', function() { expect(isEmpty(undefined_var)).toBe(true) }) it('notEmpty should return false for empty_array', function() { expect(notEmpty(empty_array)).toBe(false) }) it('notEmpty should return false for empty_object', function() { expect(notEmpty(empty_object)).toBe(false) }) it('notEmpty should return false for empty_string', function() { expect(notEmpty(empty_string)).toBe(false) }) it('notEmpty should return false for null_var', function() { expect(notEmpty(null_var)).toBe(false) }) it('notEmpty should return false for undefined_var', function() { expect(notEmpty(undefined_var)).toBe(false) }) it('notEmpty should return true for nonempty_array', function() { expect(notEmpty(nonempty_array)).toBe(true) }) it('notEmpty should return true for nonempty_number', function() { expect(notEmpty(nonempty_number)).toBe(true) }) it('notEmpty should return true for nonempty_object', function() { expect(notEmpty(nonempty_object)).toBe(true) }) it('notEmpty should return true for nonempty_string', function() { expect(notEmpty(nonempty_string)).toBe(true) }) }) describe('getIEVersion and isIE', function() { it('getIEVersion should return 10 for user agent string "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)"', function() { expect( getIEVersion( 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)' ) ).toBe(10) }) it('getIEVersion should return 11 for user agent string "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko"', function() { expect( getIEVersion( 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko' ) ).toBe(11) }) it('getIEVersion should return false for user agent string "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0"', function() { expect( getIEVersion( 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0' ) ).toBe(false) }) it('getIEVersion should return a number or false if parameter "agent" is not used', function() { expect(getIEVersion()).toMatch(/(\d+|false)/) }) it('isIE should return false for version number 6', function() { expect(isIE(6)).toBe(false) }) it('isIE should return true or false if parameter "version" is not used', function() { expect(isIE()).toMatch(/(true|false)/) }) }) }) ================================================ FILE: spec/zoom-spec.ts ================================================ import { initChart } from './c3-helper' describe('c3 chart zoom', function() { 'use strict' var chart var args = { data: { columns: [ ['data1', 30, 200, 100, 400, 3150, 250], ['data2', 50, 20, 10, 40, 15, 6025] ] }, zoom: { enabled: true, initialRange: [1, 2] }, subchart: { show: true } } beforeEach(function(done) { chart = initChart(chart, args, done) }) describe('default extent', function() { describe('main chart domain', function() { it('should have original y domain', function() { var yDomain = chart.internal.y.domain(), expectedYDomain = [-591.5, 6626.5] expect(yDomain[0]).toBe(expectedYDomain[0]) expect(yDomain[1]).toBe(expectedYDomain[1]) }) }) describe('main chart domain', function() { it('should have original y domain in subchart', function() { var yDomain = chart.internal.y.domain(), subYDomain = chart.internal.subY.domain() expect(subYDomain[0]).toBe(yDomain[0]) expect(subYDomain[1]).toBe(yDomain[1]) }) }) describe('main chart domain', function() { it('should have specified brush extent', function() { var brushSelection = chart.internal.brush.selectionAsValue(), expectedBrushSelection = [1, 2] expect(brushSelection[0]).toBeCloseTo(expectedBrushSelection[0], 1) expect(brushSelection[1]).toBeCloseTo(expectedBrushSelection[1], 1) }) }) }) }) ================================================ FILE: src/api.axis.ts ================================================ import { Chart } from './core' import { isValue, isDefined } from './util' Chart.prototype.axis = function() {} Chart.prototype.axis.labels = function(labels) { var $$ = this.internal if (arguments.length) { Object.keys(labels).forEach(function(axisId) { $$.axis.setLabelText(axisId, labels[axisId]) }) $$.axis.updateLabels() } // TODO: return some values? } Chart.prototype.axis.max = function(max) { var $$ = this.internal, config = $$.config if (arguments.length) { if (typeof max === 'object') { if (isValue(max.x)) { config.axis_x_max = max.x } if (isValue(max.y)) { config.axis_y_max = max.y } if (isValue(max.y2)) { config.axis_y2_max = max.y2 } } else { config.axis_y_max = config.axis_y2_max = max } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }) } else { return { x: config.axis_x_max, y: config.axis_y_max, y2: config.axis_y2_max } } } Chart.prototype.axis.min = function(min) { var $$ = this.internal, config = $$.config if (arguments.length) { if (typeof min === 'object') { if (isValue(min.x)) { config.axis_x_min = min.x } if (isValue(min.y)) { config.axis_y_min = min.y } if (isValue(min.y2)) { config.axis_y2_min = min.y2 } } else { config.axis_y_min = config.axis_y2_min = min } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }) } else { return { x: config.axis_x_min, y: config.axis_y_min, y2: config.axis_y2_min } } } Chart.prototype.axis.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.axis.max(range.max) } if (isDefined(range.min)) { this.axis.min(range.min) } } else { return { max: this.axis.max(), min: this.axis.min() } } } Chart.prototype.axis.types = function(types) { const $$ = this.internal if (types === undefined) { return { y: $$.config.axis_y_type, y2: $$.config.axis_y2_type } } else { if (isDefined(types.y)) { $$.config.axis_y_type = types.y } if (isDefined(types.y2)) { $$.config.axis_y2_type = types.y2 } $$.updateScales() $$.redraw() } } ================================================ FILE: src/api.category.ts ================================================ import { Chart } from './core' Chart.prototype.category = function(i, category) { var $$ = this.internal, config = $$.config if (arguments.length > 1) { config.axis_x_categories[i] = category $$.redraw() } return config.axis_x_categories[i] } Chart.prototype.categories = function(categories) { var $$ = this.internal, config = $$.config if (!arguments.length) { return config.axis_x_categories } config.axis_x_categories = categories $$.redraw() return config.axis_x_categories } ================================================ FILE: src/api.chart.ts ================================================ import { Chart } from './core' Chart.prototype.resize = function(size) { var $$ = this.internal, config = $$.config config.size_width = size ? size.width : null config.size_height = size ? size.height : null this.flush() } Chart.prototype.flush = function() { var $$ = this.internal $$.updateAndRedraw({ withLegend: true, withTransition: false, withTransitionForTransform: false }) } Chart.prototype.destroy = function() { var $$ = this.internal window.clearInterval($$.intervalForObserveInserted) if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout) } window.removeEventListener('resize', $$.resizeIfElementDisplayed) // Removes the inner resize functions $$.resizeFunction.remove() // Unbinds from the window focus event $$.unbindWindowFocus() $$.selectChart.classed('c3', false).html('') // MEMO: this is needed because the reference of some elements will not be released, then memory leak will happen. Object.keys($$).forEach(function(key) { $$[key] = null }) return null } ================================================ FILE: src/api.color.ts ================================================ import { Chart } from './core' // TODO: fix Chart.prototype.color = function(id) { var $$ = this.internal return $$.color(id) // more patterns } ================================================ FILE: src/api.data.ts ================================================ import { Chart } from './core' import { isArray } from './util' Chart.prototype.data = function(targetIds) { var targets = this.internal.data.targets return typeof targetIds === 'undefined' ? targets : targets.filter(function(t) { return [].concat(targetIds).indexOf(t.id) >= 0 }) } Chart.prototype.data.shown = function(targetIds) { return this.internal.filterTargetsToShow(this.data(targetIds)) } /** * Get values of the data loaded in the chart. * * @param {String|Array} targetId This API returns the value of specified target. * @param flat * @return {Array} Data values */ Chart.prototype.data.values = function(targetId, flat = true) { let values = null if (targetId) { const targets = this.data(targetId) if (targets && isArray(targets)) { values = targets.reduce((ret, v) => { const dataValue = v.values.map(d => d.value) if (flat) { ret = ret.concat(dataValue) } else { ret.push(dataValue) } return ret }, []) } } return values } Chart.prototype.data.names = function(names) { this.internal.clearLegendItemTextBoxCache() return this.internal.updateDataAttributes('names', names) } Chart.prototype.data.colors = function(colors) { return this.internal.updateDataAttributes('colors', colors) } Chart.prototype.data.axes = function(axes) { return this.internal.updateDataAttributes('axes', axes) } Chart.prototype.data.stackNormalized = function(normalized) { if (normalized === undefined) { return this.internal.isStackNormalized() } this.internal.config.data_stack_normalize = !!normalized this.internal.redraw() } ================================================ FILE: src/api.donut.ts ================================================ import { Chart } from './core' Chart.prototype.donut = function() {} Chart.prototype.donut.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.donut_padAngle } this.internal.config.donut_padAngle = padAngle this.flush() } ================================================ FILE: src/api.flow.ts ================================================ import CLASS from './class' import { Chart, ChartInternal } from './core' import { isValue, isDefined, diffDomain } from './util' Chart.prototype.flow = function(args) { var $$ = this.internal, targets, data, notfoundIds = [], orgDataCount = $$.getMaxDataCount(), dataCount, domain, baseTarget, baseValue, length = 0, tail = 0, diff, to if (args.json) { data = $$.convertJsonToData(args.json, args.keys) } else if (args.rows) { data = $$.convertRowsToData(args.rows) } else if (args.columns) { data = $$.convertColumnsToData(args.columns) } else { return } targets = $$.convertDataToTargets(data, true) // Update/Add data $$.data.targets.forEach(function(t) { var found = false, i, j for (i = 0; i < targets.length; i++) { if (t.id === targets[i].id) { found = true if (t.values[t.values.length - 1]) { tail = t.values[t.values.length - 1].index + 1 } length = targets[i].values.length for (j = 0; j < length; j++) { targets[i].values[j].index = tail + j if (!$$.isTimeSeries()) { targets[i].values[j].x = tail + j } } t.values = t.values.concat(targets[i].values) targets.splice(i, 1) break } } if (!found) { notfoundIds.push(t.id) } }) // Append null for not found targets $$.data.targets.forEach(function(t) { var i, j for (i = 0; i < notfoundIds.length; i++) { if (t.id === notfoundIds[i]) { tail = t.values[t.values.length - 1].index + 1 for (j = 0; j < length; j++) { t.values.push({ id: t.id, index: tail + j, x: $$.isTimeSeries() ? $$.getOtherTargetX(tail + j) : tail + j, value: null }) } } } }) // Generate null values for new target if ($$.data.targets.length) { targets.forEach(function(t) { var i, missing = [] for (i = $$.data.targets[0].values[0].index; i < tail; i++) { missing.push({ id: t.id, index: i, x: $$.isTimeSeries() ? $$.getOtherTargetX(i) : i, value: null }) } t.values.forEach(function(v) { v.index += tail if (!$$.isTimeSeries()) { v.x += tail } }) t.values = missing.concat(t.values) }) } $$.data.targets = $$.data.targets.concat(targets) // add remained // check data count because behavior needs to change when it's only one dataCount = $$.getMaxDataCount() baseTarget = $$.data.targets[0] baseValue = baseTarget.values[0] // Update length to flow if needed if (isDefined(args.to)) { length = 0 to = $$.isTimeSeries() ? $$.parseDate(args.to) : args.to baseTarget.values.forEach(function(v) { if (v.x < to) { length++ } }) } else if (isDefined(args.length)) { length = args.length } // If only one data, update the domain to flow from left edge of the chart if (!orgDataCount) { if ($$.isTimeSeries()) { if (baseTarget.values.length > 1) { diff = baseTarget.values[baseTarget.values.length - 1].x - baseValue.x } else { diff = baseValue.x - $$.getXDomain($$.data.targets)[0] } } else { diff = 1 } domain = [baseValue.x - diff, baseValue.x] $$.updateXDomain(null, true, true, false, domain) } else if (orgDataCount === 1) { if ($$.isTimeSeries()) { diff = (baseTarget.values[baseTarget.values.length - 1].x - baseValue.x) / 2 domain = [new Date(+baseValue.x - diff), new Date(+baseValue.x + diff)] $$.updateXDomain(null, true, true, false, domain) } } // Set targets $$.updateTargets($$.data.targets) // Redraw with new targets $$.redraw({ flow: { index: baseValue.index, length: length, duration: isValue(args.duration) ? args.duration : $$.config.transition_duration, done: args.done, orgDataCount: orgDataCount }, withLegend: true, withTransition: orgDataCount > 1, withTrimXDomain: false, withUpdateXAxis: true }) } ChartInternal.prototype.generateFlow = function(args) { var $$ = this, config = $$.config, d3 = $$.d3 return function() { var targets = args.targets, flow = args.flow, drawBar = args.drawBar, drawLine = args.drawLine, drawArea = args.drawArea, cx = args.cx, cy = args.cy, xv = args.xv, xForText = args.xForText, yForText = args.yForText, duration = args.duration var translateX, scaleX = 1, transform, flowIndex = flow.index, flowLength = flow.length, flowStart = $$.getValueOnIndex($$.data.targets[0].values, flowIndex), flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, flowIndex + flowLength ), orgDomain = $$.x.domain(), domain, durationForFlow = flow.duration || duration, done = flow.done || function() {}, wait = $$.generateWait() var xgrid, xgridLines, mainRegion, mainText, mainBar, mainLine, mainArea, mainCircle // set flag $$.flowing = true // remove head data after rendered $$.data.targets.forEach(function(d) { d.values.splice(0, flowLength) }) // update x domain to generate axis elements for flow domain = $$.updateXDomain(targets, true, true) // update elements related to x scale if ($$.updateXGrid) { $$.updateXGrid(true) } xgrid = $$.xgrid || d3.selectAll([]) // xgrid needs to be obtained after updateXGrid xgridLines = $$.xgridLines || d3.selectAll([]) mainRegion = $$.mainRegion || d3.selectAll([]) mainText = $$.mainText || d3.selectAll([]) mainBar = $$.mainBar || d3.selectAll([]) mainLine = $$.mainLine || d3.selectAll([]) mainArea = $$.mainArea || d3.selectAll([]) mainCircle = $$.mainCircle || d3.selectAll([]) // generate transform to flow if (!flow.orgDataCount) { // if empty if ($$.data.targets[0].values.length !== 1) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]) } else { if ($$.isTimeSeries()) { flowStart = $$.getValueOnIndex($$.data.targets[0].values, 0) flowEnd = $$.getValueOnIndex( $$.data.targets[0].values, $$.data.targets[0].values.length - 1 ) translateX = $$.x(flowStart.x) - $$.x(flowEnd.x) } else { translateX = diffDomain(domain) / 2 } } } else if ( flow.orgDataCount === 1 || (flowStart && flowStart.x) === (flowEnd && flowEnd.x) ) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]) } else { if ($$.isTimeSeries()) { translateX = $$.x(orgDomain[0]) - $$.x(domain[0]) } else { translateX = $$.x(flowStart.x) - $$.x(flowEnd.x) } } scaleX = diffDomain(orgDomain) / diffDomain(domain) transform = 'translate(' + translateX + ',0) scale(' + scaleX + ',1)' $$.hideXGridFocus() var flowTransition = d3 .transition() .ease(d3.easeLinear) .duration(durationForFlow) wait.add($$.xAxis($$.axes.x, flowTransition)) wait.add(mainBar.transition(flowTransition).attr('transform', transform)) wait.add(mainLine.transition(flowTransition).attr('transform', transform)) wait.add(mainArea.transition(flowTransition).attr('transform', transform)) wait.add(mainCircle.transition(flowTransition).attr('transform', transform)) wait.add(mainText.transition(flowTransition).attr('transform', transform)) wait.add( mainRegion .filter($$.isRegionOnX) .transition(flowTransition) .attr('transform', transform) ) wait.add(xgrid.transition(flowTransition).attr('transform', transform)) wait.add(xgridLines.transition(flowTransition).attr('transform', transform)) wait(function() { var i, shapes = [], texts = [] // remove flowed elements if (flowLength) { for (i = 0; i < flowLength; i++) { shapes.push('.' + CLASS.shape + '-' + (flowIndex + i)) texts.push('.' + CLASS.text + '-' + (flowIndex + i)) } $$.svg .selectAll('.' + CLASS.shapes) .selectAll(shapes) .remove() $$.svg .selectAll('.' + CLASS.texts) .selectAll(texts) .remove() $$.svg.select('.' + CLASS.xgrid).remove() } // draw again for removing flowed elements and reverting attr xgrid .attr('transform', null) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity) xgridLines.attr('transform', null) xgridLines .select('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) xgridLines .select('text') .attr('x', config.axis_rotated ? $$.width : 0) .attr('y', xv) mainBar.attr('transform', null).attr('d', drawBar) mainLine.attr('transform', null).attr('d', drawLine) mainArea.attr('transform', null).attr('d', drawArea) mainCircle .attr('transform', null) .attr('cx', cx) .attr('cy', cy) mainText .attr('transform', null) .attr('x', xForText) .attr('y', yForText) .style('fill-opacity', $$.opacityForText.bind($$)) mainRegion.attr('transform', null) mainRegion .filter($$.isRegionOnX) .attr('x', $$.regionX.bind($$)) .attr('width', $$.regionWidth.bind($$)) // callback for end of flow done() $$.flowing = false }) } } ================================================ FILE: src/api.focus.ts ================================================ import CLASS from './class' import { Chart } from './core' Chart.prototype.focus = function(targetIds) { var $$ = this.internal, candidates targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), this.revert() this.defocus() candidates.classed(CLASS.focused, true).classed(CLASS.defocused, false) if ($$.hasArcType()) { $$.expandArc(targetIds) } $$.toggleFocusLegend(targetIds, true) $$.focusedTargetIds = targetIds $$.defocusedTargetIds = $$.defocusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }) } Chart.prototype.defocus = function(targetIds) { var $$ = this.internal, candidates targetIds = $$.mapToTargetIds(targetIds) ;(candidates = $$.svg.selectAll( $$.selectorTargets(targetIds.filter($$.isTargetToShow, $$)) )), candidates.classed(CLASS.focused, false).classed(CLASS.defocused, true) if ($$.hasArcType()) { $$.unexpandArc(targetIds) } $$.toggleFocusLegend(targetIds, false) $$.focusedTargetIds = $$.focusedTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }) $$.defocusedTargetIds = targetIds } Chart.prototype.revert = function(targetIds) { var $$ = this.internal, candidates targetIds = $$.mapToTargetIds(targetIds) candidates = $$.svg.selectAll($$.selectorTargets(targetIds)) // should be for all targets candidates.classed(CLASS.focused, false).classed(CLASS.defocused, false) if ($$.hasArcType()) { $$.unexpandArc(targetIds) } if ($$.config.legend_show) { $$.showLegend(targetIds.filter($$.isLegendToShow.bind($$))) $$.legend .selectAll($$.selectorLegends(targetIds)) .filter(function() { return $$.d3.select(this).classed(CLASS.legendItemFocused) }) .classed(CLASS.legendItemFocused, false) } $$.focusedTargetIds = [] $$.defocusedTargetIds = [] } ================================================ FILE: src/api.grid.ts ================================================ import { Chart } from './core' Chart.prototype.xgrids = function(grids) { var $$ = this.internal, config = $$.config if (!grids) { return config.grid_x_lines } config.grid_x_lines = grids $$.redrawWithoutRescale() return config.grid_x_lines } Chart.prototype.xgrids.add = function(grids) { var $$ = this.internal return this.xgrids($$.config.grid_x_lines.concat(grids ? grids : [])) } Chart.prototype.xgrids.remove = function(params) { // TODO: multiple var $$ = this.internal $$.removeGridLines(params, true) } Chart.prototype.ygrids = function(grids) { var $$ = this.internal, config = $$.config if (!grids) { return config.grid_y_lines } config.grid_y_lines = grids $$.redrawWithoutRescale() return config.grid_y_lines } Chart.prototype.ygrids.add = function(grids) { var $$ = this.internal return this.ygrids($$.config.grid_y_lines.concat(grids ? grids : [])) } Chart.prototype.ygrids.remove = function(params) { // TODO: multiple var $$ = this.internal $$.removeGridLines(params, false) } ================================================ FILE: src/api.group.ts ================================================ import { Chart } from './core' import { isUndefined } from './util' Chart.prototype.groups = function(groups) { var $$ = this.internal, config = $$.config if (isUndefined(groups)) { return config.data_groups } config.data_groups = groups $$.redraw() return config.data_groups } ================================================ FILE: src/api.legend.ts ================================================ import { Chart } from './core' Chart.prototype.legend = function() {} Chart.prototype.legend.show = function(targetIds) { var $$ = this.internal $$.showLegend($$.mapToTargetIds(targetIds)) $$.updateAndRedraw({ withLegend: true }) } Chart.prototype.legend.hide = function(targetIds) { var $$ = this.internal $$.hideLegend($$.mapToTargetIds(targetIds)) $$.updateAndRedraw({ withLegend: false }) } ================================================ FILE: src/api.load.ts ================================================ import { Chart } from './core' Chart.prototype.load = function(args) { var $$ = this.internal, config = $$.config // update xs if specified if (args.xs) { $$.addXs(args.xs) } // update names if exists if ('names' in args) { Chart.prototype.data.names.bind(this)(args.names) } // update classes if exists if ('classes' in args) { Object.keys(args.classes).forEach(function(id) { config.data_classes[id] = args.classes[id] }) } // update categories if exists if ('categories' in args && $$.isCategorized()) { config.axis_x_categories = args.categories } // update axes if exists if ('axes' in args) { Object.keys(args.axes).forEach(function(id) { config.data_axes[id] = args.axes[id] }) } // update colors if exists if ('colors' in args) { Object.keys(args.colors).forEach(function(id) { config.data_colors[id] = args.colors[id] }) } // use cache if exists if ('cacheIds' in args && $$.hasCaches(args.cacheIds)) { $$.load($$.getCaches(args.cacheIds), args.done) return } // unload if needed if (args.unload) { // TODO: do not unload if target will load (included in url/rows/columns) $$.unload( $$.mapToTargetIds(args.unload === true ? null : args.unload), function() { $$.loadFromArgs(args) } ) } else { $$.loadFromArgs(args) } } Chart.prototype.unload = function(args) { var $$ = this.internal args = args || {} if (args instanceof Array) { args = { ids: args } } else if (typeof args === 'string') { args = { ids: [args] } } $$.unload($$.mapToTargetIds(args.ids), function() { $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }) if (args.done) { args.done() } }) } ================================================ FILE: src/api.pie.ts ================================================ import { Chart } from './core' Chart.prototype.pie = function() {} Chart.prototype.pie.padAngle = function(padAngle) { if (padAngle === undefined) { return this.internal.config.pie_padAngle } this.internal.config.pie_padAngle = padAngle this.flush() } ================================================ FILE: src/api.region.ts ================================================ import CLASS from './class' import { Chart } from './core' import { getOption } from './util' Chart.prototype.regions = function(regions) { var $$ = this.internal, config = $$.config if (!regions) { return config.regions } config.regions = regions $$.redrawWithoutRescale() return config.regions } Chart.prototype.regions.add = function(regions) { var $$ = this.internal, config = $$.config if (!regions) { return config.regions } config.regions = config.regions.concat(regions) $$.redrawWithoutRescale() return config.regions } Chart.prototype.regions.remove = function(options) { var $$ = this.internal, config = $$.config, duration, classes, regions options = options || {} duration = getOption(options, 'duration', config.transition_duration) classes = getOption(options, 'classes', [CLASS.region]) regions = $$.main.select('.' + CLASS.regions).selectAll( classes.map(function(c) { return '.' + c }) ) ;(duration ? regions.transition().duration(duration) : regions) .style('opacity', 0) .remove() config.regions = config.regions.filter(function(region) { var found = false if (!region['class']) { return true } region['class'].split(' ').forEach(function(c) { if (classes.indexOf(c) >= 0) { found = true } }) return !found }) return config.regions } ================================================ FILE: src/api.selection.ts ================================================ import CLASS from './class' import { Chart } from './core' import { isDefined } from './util' Chart.prototype.selected = function(targetId) { var $$ = this.internal, d3 = $$.d3 return $$.main .selectAll('.' + CLASS.shapes + $$.getTargetSelectorSuffix(targetId)) .selectAll('.' + CLASS.shape) .filter(function() { return d3.select(this).classed(CLASS.SELECTED) }) .nodes() .map(function(d) { var data = d.__data__ return data.data ? data.data : data }) } Chart.prototype.select = function(ids, indices, resetOther) { var $$ = this.internal, d3 = $$.d3, config = $$.config if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED) // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d) && !isSelected) { toggle(true, shape.classed(CLASS.SELECTED, true), d, i) } } else if (isDefined(resetOther) && resetOther) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i) } } }) } Chart.prototype.unselect = function(ids, indices) { var $$ = this.internal, d3 = $$.d3, config = $$.config if (!config.data_selection_enabled) { return } $$.main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this), id = d.data ? d.data.id : d.id, toggle = $$.getToggle(this, d).bind($$), isTargetId = config.data_selection_grouped || !ids || ids.indexOf(id) >= 0, isTargetIndex = !indices || indices.indexOf(i) >= 0, isSelected = shape.classed(CLASS.SELECTED) // line/area selection not supported yet if (shape.classed(CLASS.line) || shape.classed(CLASS.area)) { return } if (isTargetId && isTargetIndex) { if (config.data_selection_isselectable(d)) { if (isSelected) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i) } } } }) } ================================================ FILE: src/api.show.ts ================================================ import { Chart } from './core' import { isIE } from './util' Chart.prototype.show = function(targetIds, options) { var $$ = this.internal, targets targetIds = $$.mapToTargetIds(targetIds) options = options || {} $$.removeHiddenTargetIds(targetIds) targets = $$.svg.selectAll($$.selectorTargets(targetIds)) targets .transition() .style('display', isIE() ? 'block' : 'initial', 'important') .style('opacity', 1, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 1) }) if (options.withLegend) { $$.showLegend(targetIds) } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }) } Chart.prototype.hide = function(targetIds, options) { var $$ = this.internal, targets targetIds = $$.mapToTargetIds(targetIds) options = options || {} $$.addHiddenTargetIds(targetIds) targets = $$.svg.selectAll($$.selectorTargets(targetIds)) targets .transition() .style('opacity', 0, 'important') .call($$.endall, function() { targets.style('opacity', null).style('opacity', 0) targets.style('display', 'none') }) if (options.withLegend) { $$.hideLegend(targetIds) } $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }) } Chart.prototype.toggle = function(targetIds, options) { var that = this, $$ = this.internal $$.mapToTargetIds(targetIds).forEach(function(targetId) { $$.isTargetToShow(targetId) ? that.hide(targetId, options) : that.show(targetId, options) }) } ================================================ FILE: src/api.subchart.ts ================================================ import { Chart } from './core' Chart.prototype.subchart = function() {} Chart.prototype.subchart.isShown = function() { const $$ = this.internal return $$.config.subchart_show } Chart.prototype.subchart.show = function() { const $$ = this.internal if ($$.config.subchart_show) { return } $$.config.subchart_show = true // insert DOM $$.initSubchart() // update dimensions with sub chart now visible $$.updateDimension() // insert brush (depends on sizes previously updated) $$.initSubchartBrush() // attach data $$.updateTargetsForSubchart($$.getTargets()) // reset fade-in state $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = false }) // redraw chart ! $$.updateAndRedraw() // update visible targets ! $$.showTargets() } Chart.prototype.subchart.hide = function() { const $$ = this.internal if (!$$.config.subchart_show) { return } $$.config.subchart_show = false // remove DOM $$.removeSubchart() // re-render chart $$.redraw() } ================================================ FILE: src/api.tooltip.ts ================================================ import { Chart } from './core' Chart.prototype.tooltip = function() {} Chart.prototype.tooltip.show = function(args) { var $$ = this.internal, targets, data, mouse = {} // determine mouse position on the chart if (args.mouse) { mouse = args.mouse } else { // determine focus data if (args.data) { data = args.data } else if (typeof args.x !== 'undefined') { if (args.id) { targets = $$.data.targets.filter(function(t) { return t.id === args.id }) } else { targets = $$.data.targets } data = $$.filterByX(targets, args.x).slice(0, 1)[0] } mouse = data ? $$.getMousePosition(data) : null } // emulate mouse events to show $$.dispatchEvent('mousemove', mouse) $$.config.tooltip_onshow.call($$, data) } Chart.prototype.tooltip.hide = function() { // TODO: get target data by checking the state of focus this.internal.dispatchEvent('mouseout', 0) this.internal.config.tooltip_onhide.call(this) } ================================================ FILE: src/api.transform.ts ================================================ import { Chart, ChartInternal } from './core' Chart.prototype.transform = function(type, targetIds) { var $$ = this.internal, options = ['pie', 'donut'].indexOf(type) >= 0 ? { withTransform: true } : null $$.transformTo(targetIds, type, options) } ChartInternal.prototype.transformTo = function( targetIds, type, optionsForRedraw ) { var $$ = this, withTransitionForAxis = !$$.hasArcType(), options = optionsForRedraw || { withTransitionForAxis: withTransitionForAxis } options.withTransitionForTransform = false $$.transiting = false $$.setTargetType(targetIds, type) $$.updateTargets($$.data.targets) // this is needed when transforming to arc $$.updateAndRedraw(options) } ================================================ FILE: src/api.x.ts ================================================ import { Chart } from './core' Chart.prototype.x = function(x) { var $$ = this.internal if (arguments.length) { $$.updateTargetX($$.data.targets, x) $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }) } return $$.data.xs } Chart.prototype.xs = function(xs) { var $$ = this.internal if (arguments.length) { $$.updateTargetXs($$.data.targets, xs) $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true }) } return $$.data.xs } ================================================ FILE: src/api.zoom.ts ================================================ import { Chart } from './core' import { isDefined } from './util' Chart.prototype.zoom = function(domain) { var $$ = this.internal if (domain) { if ($$.isTimeSeries()) { domain = domain.map(function(x) { return $$.parseDate(x) }) } if ($$.config.subchart_show) { $$.brush.selectionAsValue(domain, true) } else { $$.updateXDomain(null, true, false, false, domain) $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }) } $$.config.zoom_onzoom.call(this, $$.x.orgDomain()) return domain } else { return $$.x.domain() } } Chart.prototype.zoom.enable = function(enabled) { var $$ = this.internal $$.config.zoom_enabled = enabled $$.updateAndRedraw() } Chart.prototype.unzoom = function() { var $$ = this.internal if ($$.config.subchart_show) { $$.brush.clear() } else { $$.updateXDomain(null, true, false, false, $$.subX.domain()) $$.redraw({ withY: $$.config.zoom_rescale, withSubchart: false }) } } Chart.prototype.zoom.max = function(max) { var $$ = this.internal, config = $$.config, d3 = $$.d3 if (max === 0 || max) { config.zoom_x_max = d3.max([$$.orgXDomain[1], max]) } else { return config.zoom_x_max } } Chart.prototype.zoom.min = function(min) { var $$ = this.internal, config = $$.config, d3 = $$.d3 if (min === 0 || min) { config.zoom_x_min = d3.min([$$.orgXDomain[0], min]) } else { return config.zoom_x_min } } Chart.prototype.zoom.range = function(range) { if (arguments.length) { if (isDefined(range.max)) { this.domain.max(range.max) } if (isDefined(range.min)) { this.domain.min(range.min) } } else { return { max: this.domain.max(), min: this.domain.min() } } } ================================================ FILE: src/arc.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isFunction } from './util' ChartInternal.prototype.initPie = function() { var $$ = this, d3 = $$.d3 $$.pie = d3 .pie() .padAngle(this.getPadAngle.bind(this)) .value(function(d) { return d.values.reduce(function(a, b) { return a + b.value }, 0) }) let orderFct = $$.getOrderFunction() // we need to reverse the returned order if asc or desc to have the slice in expected order. if (orderFct && ($$.isOrderAsc() || $$.isOrderDesc())) { let defaultSort = orderFct orderFct = (t1, t2) => defaultSort(t1, t2) * -1 } $$.pie.sort(orderFct || null) } ChartInternal.prototype.updateRadius = function() { var $$ = this, config = $$.config, w = config.gauge_width || config.donut_width, gaugeArcWidth = $$.filterTargetsToShow($$.data.targets).length * $$.config.gauge_arcs_minWidth $$.radiusExpanded = (Math.min($$.arcWidth, $$.arcHeight) / 2) * ($$.hasType('gauge') ? 0.85 : 1) $$.radius = $$.radiusExpanded * 0.95 $$.innerRadiusRatio = w ? ($$.radius - w) / $$.radius : 0.6 $$.innerRadius = $$.hasType('donut') || $$.hasType('gauge') ? $$.radius * $$.innerRadiusRatio : 0 $$.gaugeArcWidth = w ? w : gaugeArcWidth <= $$.radius - $$.innerRadius ? $$.radius - $$.innerRadius : gaugeArcWidth <= $$.radius ? gaugeArcWidth : $$.radius } ChartInternal.prototype.getPadAngle = function() { if (this.hasType('pie')) { return this.config.pie_padAngle || 0 } else if (this.hasType('donut')) { return this.config.donut_padAngle || 0 } else { return 0 } } ChartInternal.prototype.updateArc = function() { var $$ = this $$.svgArc = $$.getSvgArc() $$.svgArcExpanded = $$.getSvgArcExpanded() $$.svgArcExpandedSub = $$.getSvgArcExpanded(0.98) } ChartInternal.prototype.updateAngle = function(d) { var $$ = this, config = $$.config, found = false, index = 0, gMin, gMax, gTic, gValue if (!config) { return null } $$.pie($$.filterTargetsToShow($$.data.targets)).forEach(function(t) { if (!found && t.data.id === d.data.id) { found = true d = t d.index = index } index++ }) if (isNaN(d.startAngle)) { d.startAngle = 0 } if (isNaN(d.endAngle)) { d.endAngle = d.startAngle } if ($$.isGaugeType(d.data)) { gMin = config.gauge_min gMax = config.gauge_max gTic = (Math.PI * (config.gauge_fullCircle ? 2 : 1)) / (gMax - gMin) gValue = d.value < gMin ? 0 : d.value < gMax ? d.value - gMin : gMax - gMin d.startAngle = config.gauge_startingAngle d.endAngle = d.startAngle + gTic * gValue } return found ? d : null } ChartInternal.prototype.getSvgArc = function() { var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index : $$.radius }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }), newArc = function(d, withoutUpdate) { var updated if (withoutUpdate) { return arc(d) } // for interpolate updated = $$.updateAngle(d) return updated ? arc(updated) : 'M 0 0' } // TODO: extends all function ;(newArc as any).centroid = arc.centroid return newArc } ChartInternal.prototype.getSvgArcExpanded = function(rate) { rate = rate || 1 var $$ = this, hasGaugeType = $$.hasType('gauge'), singleArcWidth = $$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length, expandWidth = Math.min( $$.radiusExpanded * rate - $$.radius, singleArcWidth * 0.8 - (1 - rate) * 100 ), arc = $$.d3 .arc() .outerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * d.index + expandWidth : $$.radiusExpanded * rate }) .innerRadius(function(d) { return hasGaugeType ? $$.radius - singleArcWidth * (d.index + 1) : $$.innerRadius }) return function(d) { var updated = $$.updateAngle(d) return updated ? arc(updated) : 'M 0 0' } } ChartInternal.prototype.getArc = function(d, withoutUpdate, force) { return force || this.isArcType(d.data) ? this.svgArc(d, withoutUpdate) : 'M 0 0' } ChartInternal.prototype.transformForArcLabel = function(d) { var $$ = this, config = $$.config, updated = $$.updateAngle(d), c, x, y, h, ratio, translate = '', hasGauge = $$.hasType('gauge') if (updated && !hasGauge) { c = this.svgArc.centroid(updated) x = isNaN(c[0]) ? 0 : c[0] y = isNaN(c[1]) ? 0 : c[1] h = Math.sqrt(x * x + y * y) if ($$.hasType('donut') && config.donut_label_ratio) { ratio = isFunction(config.donut_label_ratio) ? config.donut_label_ratio(d, $$.radius, h) : config.donut_label_ratio } else if ($$.hasType('pie') && config.pie_label_ratio) { ratio = isFunction(config.pie_label_ratio) ? config.pie_label_ratio(d, $$.radius, h) : config.pie_label_ratio } else { ratio = $$.radius && h ? ((36 / $$.radius > 0.375 ? 1.175 - 36 / $$.radius : 0.8) * $$.radius) / h : 0 } translate = 'translate(' + x * ratio + ',' + y * ratio + ')' } else if ( updated && hasGauge && $$.filterTargetsToShow($$.data.targets).length > 1 ) { var y1 = Math.sin(updated.endAngle - Math.PI / 2) x = Math.cos(updated.endAngle - Math.PI / 2) * ($$.radiusExpanded + 25) y = y1 * ($$.radiusExpanded + 15 - Math.abs(y1 * 10)) + 3 translate = 'translate(' + x + ',' + y + ')' } return translate } /** * @deprecated Use `getRatio('arc', d)` instead. */ ChartInternal.prototype.getArcRatio = function(d) { return this.getRatio('arc', d) } ChartInternal.prototype.convertToArcData = function(d) { return this.addName({ id: d.data.id, value: d.value, ratio: this.getRatio('arc', d), index: d.index }) } ChartInternal.prototype.textForArcLabel = function(d) { var $$ = this, updated, value, ratio, id, format if (!$$.shouldShowArcLabel()) { return '' } updated = $$.updateAngle(d) value = updated ? updated.value : null ratio = $$.getRatio('arc', updated) id = d.data.id if (!$$.hasType('gauge') && !$$.meetsArcLabelThreshold(ratio)) { return '' } format = $$.getArcLabelFormat() return format ? format(value, ratio, id) : $$.defaultArcValueFormat(value, ratio) } ChartInternal.prototype.textForGaugeMinMax = function(value, isMax) { var $$ = this, format = $$.getGaugeLabelExtents() return format ? format(value, isMax) : value } ChartInternal.prototype.expandArc = function(targetIds) { var $$ = this, interval // MEMO: avoid to cancel transition if ($$.transiting) { interval = window.setInterval(function() { if (!$$.transiting) { window.clearInterval(interval) if ($$.legend.selectAll('.c3-legend-item-focused').size() > 0) { $$.expandArc(targetIds) } } }, 10) return } targetIds = $$.mapToTargetIds(targetIds) $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .each(function(d) { if (!$$.shouldExpand(d.data.id)) { return } $$.d3 .select(this) .selectAll('path') .transition() .duration($$.expandDuration(d.data.id)) .attr('d', $$.svgArcExpanded) .transition() .duration($$.expandDuration(d.data.id) * 2) .attr('d', $$.svgArcExpandedSub) .each(function(d) { if ($$.isDonutType(d.data)) { // callback here } }) }) } ChartInternal.prototype.unexpandArc = function(targetIds) { var $$ = this if ($$.transiting) { return } targetIds = $$.mapToTargetIds(targetIds) $$.svg .selectAll($$.selectorTargets(targetIds, '.' + CLASS.chartArc)) .selectAll('path') .transition() .duration(function(d) { return $$.expandDuration(d.data.id) }) .attr('d', $$.svgArc) $$.svg.selectAll('.' + CLASS.arc) } ChartInternal.prototype.expandDuration = function(id) { var $$ = this, config = $$.config if ($$.isDonutType(id)) { return config.donut_expand_duration } else if ($$.isGaugeType(id)) { return config.gauge_expand_duration } else if ($$.isPieType(id)) { return config.pie_expand_duration } else { return 50 } } ChartInternal.prototype.shouldExpand = function(id) { var $$ = this, config = $$.config return ( ($$.isDonutType(id) && config.donut_expand) || ($$.isGaugeType(id) && config.gauge_expand) || ($$.isPieType(id) && config.pie_expand) ) } ChartInternal.prototype.shouldShowArcLabel = function() { var $$ = this, config = $$.config, shouldShow = true if ($$.hasType('donut')) { shouldShow = config.donut_label_show } else if ($$.hasType('pie')) { shouldShow = config.pie_label_show } // when gauge, always true return shouldShow } ChartInternal.prototype.meetsArcLabelThreshold = function(ratio) { var $$ = this, config = $$.config, threshold = $$.hasType('donut') ? config.donut_label_threshold : config.pie_label_threshold return ratio >= threshold } ChartInternal.prototype.getArcLabelFormat = function() { var $$ = this, config = $$.config, format = config.pie_label_format if ($$.hasType('gauge')) { format = config.gauge_label_format } else if ($$.hasType('donut')) { format = config.donut_label_format } return format } ChartInternal.prototype.getGaugeLabelExtents = function() { var $$ = this, config = $$.config return config.gauge_label_extents } ChartInternal.prototype.getArcTitle = function() { var $$ = this return $$.hasType('donut') ? $$.config.donut_title : '' } ChartInternal.prototype.updateTargetsForArc = function(targets) { var $$ = this, main = $$.main, mainPies, mainPieEnter, classChartArc = $$.classChartArc.bind($$), classArcs = $$.classArcs.bind($$), classFocus = $$.classFocus.bind($$) mainPies = main .select('.' + CLASS.chartArcs) .selectAll('.' + CLASS.chartArc) .data($$.pie(targets)) .attr('class', function(d) { return classChartArc(d) + classFocus(d.data) }) mainPieEnter = mainPies .enter() .append('g') .attr('class', classChartArc) mainPieEnter.append('g').attr('class', classArcs) mainPieEnter .append('text') .attr('dy', $$.hasType('gauge') ? '-.1em' : '.35em') .style('opacity', 0) .style('text-anchor', 'middle') .style('pointer-events', 'none') // MEMO: can not keep same color..., but not bad to update color in redraw //mainPieUpdate.exit().remove(); } ChartInternal.prototype.initArc = function() { var $$ = this $$.arcs = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')) $$.arcs .append('text') .attr('class', CLASS.chartArcsTitle) .style('text-anchor', 'middle') .text($$.getArcTitle()) } ChartInternal.prototype.redrawArc = function( duration, durationForExit, withTransform ) { var $$ = this, d3 = $$.d3, config = $$.config, main = $$.main, arcs, mainArc, arcLabelLines, mainArcLabelLine, hasGaugeType = $$.hasType('gauge') arcs = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arc) .data($$.arcData.bind($$)) mainArc = arcs .enter() .append('path') .attr('class', $$.classArc.bind($$)) .style('fill', function(d) { return $$.color(d.data) }) .style('cursor', function(d) { return config.interaction_enabled && config.data_selection_isselectable(d) ? 'pointer' : null }) .each(function(d) { if ($$.isGaugeType(d.data)) { d.startAngle = d.endAngle = config.gauge_startingAngle } this._current = d }) .merge(arcs) if (hasGaugeType) { arcLabelLines = main .selectAll('.' + CLASS.arcs) .selectAll('.' + CLASS.arcLabelLine) .data($$.arcData.bind($$)) mainArcLabelLine = arcLabelLines .enter() .append('rect') .attr('class', function(d) { return ( CLASS.arcLabelLine + ' ' + CLASS.target + ' ' + CLASS.target + '-' + d.data.id ) }) .merge(arcLabelLines) if ($$.filterTargetsToShow($$.data.targets).length === 1) { mainArcLabelLine.style('display', 'none') } else { mainArcLabelLine .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data) }) .style('display', config.gauge_labelLine_show ? '' : 'none') .each(function(d) { var lineLength = 0, lineThickness = 2, x = 0, y = 0, transform = '' if ($$.hiddenTargetIds.indexOf(d.data.id) < 0) { var updated = $$.updateAngle(d), innerLineLength = ($$.gaugeArcWidth / $$.filterTargetsToShow($$.data.targets).length) * (updated.index + 1), lineAngle = updated.endAngle - Math.PI / 2, arcInnerRadius = $$.radius - innerLineLength, linePositioningAngle = lineAngle - (arcInnerRadius === 0 ? 0 : 1 / arcInnerRadius) lineLength = $$.radiusExpanded - $$.radius + innerLineLength x = Math.cos(linePositioningAngle) * arcInnerRadius y = Math.sin(linePositioningAngle) * arcInnerRadius transform = 'rotate(' + (lineAngle * 180) / Math.PI + ', ' + x + ', ' + y + ')' } d3.select(this) .attr('x', x) .attr('y', y) .attr('width', lineLength) .attr('height', lineThickness) .attr('transform', transform) .style( 'stroke-dasharray', '0, ' + (lineLength + lineThickness) + ', 0' ) }) } } mainArc .attr('transform', function(d) { return !$$.isGaugeType(d.data) && withTransform ? 'scale(0)' : '' }) .on( 'mouseover', config.interaction_enabled ? function(d) { var updated, arcData if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d) if (updated) { arcData = $$.convertToArcData(updated) // transitions $$.expandArc(updated.data.id) $$.api.focus(updated.data.id) $$.toggleFocusLegend(updated.data.id, true) $$.config.data_onmouseover(arcData, this) } } : null ) .on( 'mousemove', config.interaction_enabled ? function(d) { var updated = $$.updateAngle(d), arcData, selectedData if (updated) { ;(arcData = $$.convertToArcData(updated)), (selectedData = [arcData]) $$.showTooltip(selectedData, this) } } : null ) .on( 'mouseout', config.interaction_enabled ? function(d) { var updated, arcData if ($$.transiting) { // skip while transiting return } updated = $$.updateAngle(d) if (updated) { arcData = $$.convertToArcData(updated) // transitions $$.unexpandArc(updated.data.id) $$.api.revert() $$.revertLegend() $$.hideTooltip() $$.config.data_onmouseout(arcData, this) } } : null ) .on( 'click', config.interaction_enabled ? function(d, i) { var updated = $$.updateAngle(d), arcData if (updated) { arcData = $$.convertToArcData(updated) if ($$.toggleShape) { $$.toggleShape(this, arcData, i) } $$.config.data_onclick.call($$.api, arcData, this) } } : null ) .each(function() { $$.transiting = true }) .transition() .duration(duration) .attrTween('d', function(d) { var updated = $$.updateAngle(d), interpolate if (!updated) { return function() { return 'M 0 0' } } // if (this._current === d) { // this._current = { // startAngle: Math.PI*2, // endAngle: Math.PI*2, // }; // } if (isNaN(this._current.startAngle)) { this._current.startAngle = 0 } if (isNaN(this._current.endAngle)) { this._current.endAngle = this._current.startAngle } interpolate = d3.interpolate(this._current, updated) this._current = interpolate(0) return function(t) { // prevents crashing the charts once in transition and chart.destroy() has been called if ($$.config === null) { return 'M 0 0' } var interpolated = interpolate(t) interpolated.data = d.data // data.id will be updated by interporator return $$.getArc(interpolated, true) } }) .attr('transform', withTransform ? 'scale(1)' : '') .style('fill', function(d) { return $$.levelColor ? $$.levelColor( d.data.values.reduce(function(total, item) { return total + item.value }, 0) ) : $$.color(d.data.id) }) // Where gauge reading color would receive customization. .call($$.endall, function() { $$.transiting = false }) arcs .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() main .selectAll('.' + CLASS.chartArc) .select('text') .style('opacity', 0) .attr('class', function(d) { return $$.isGaugeType(d.data) ? CLASS.gaugeValue : '' }) .text($$.textForArcLabel.bind($$)) .attr('transform', $$.transformForArcLabel.bind($$)) .style('font-size', function(d) { return $$.isGaugeType(d.data) && $$.filterTargetsToShow($$.data.targets).length === 1 ? Math.round($$.radius / 5) + 'px' : '' }) .transition() .duration(duration) .style('opacity', function(d) { return $$.isTargetToShow(d.data.id) && $$.isArcType(d.data) ? 1 : 0 }) main .select('.' + CLASS.chartArcsTitle) .style('opacity', $$.hasType('donut') || hasGaugeType ? 1 : 0) if (hasGaugeType) { let index = 0 const backgroundArc = $$.arcs .select('g.' + CLASS.chartArcsBackground) .selectAll('path.' + CLASS.chartArcsBackground) .data($$.data.targets) backgroundArc .enter() .append('path') .attr( 'class', (d, i) => CLASS.chartArcsBackground + ' ' + CLASS.chartArcsBackground + '-' + i ) .merge(backgroundArc) .attr('d', d1 => { if ($$.hiddenTargetIds.indexOf(d1.id) >= 0) { return 'M 0 0' } var d = { data: [{ value: config.gauge_max }], startAngle: config.gauge_startingAngle, endAngle: -1 * config.gauge_startingAngle * (config.gauge_fullCircle ? Math.PI : 1), index: index++ } return $$.getArc(d, true, true) }) backgroundArc.exit().remove() $$.arcs .select('.' + CLASS.chartArcsGaugeUnit) .attr('dy', '.75em') .text(config.gauge_label_show ? config.gauge_units : '') $$.arcs .select('.' + CLASS.chartArcsGaugeMin) .attr( 'dx', -1 * ($$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2)) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_min, false) : '' ) $$.arcs .select('.' + CLASS.chartArcsGaugeMax) .attr( 'dx', $$.innerRadius + ($$.radius - $$.innerRadius) / (config.gauge_fullCircle ? 1 : 2) + 'px' ) .attr('dy', '1.2em') .text( config.gauge_label_show ? $$.textForGaugeMinMax(config.gauge_max, true) : '' ) } } ChartInternal.prototype.initGauge = function() { var arcs = this.arcs if (this.hasType('gauge')) { arcs.append('g').attr('class', CLASS.chartArcsBackground) arcs .append('text') .attr('class', CLASS.chartArcsGaugeUnit) .style('text-anchor', 'middle') .style('pointer-events', 'none') arcs .append('text') .attr('class', CLASS.chartArcsGaugeMin) .style('text-anchor', 'middle') .style('pointer-events', 'none') arcs .append('text') .attr('class', CLASS.chartArcsGaugeMax) .style('text-anchor', 'middle') .style('pointer-events', 'none') } } ChartInternal.prototype.getGaugeLabelHeight = function() { return this.config.gauge_label_show ? 20 : 0 } ================================================ FILE: src/axis-internal.ts ================================================ import { getBBox } from './util' function AxisInternal(component, params) { var internal = this internal.component = component internal.params = params || {} internal.d3 = component.d3 internal.scale = internal.d3.scaleLinear() internal.range internal.orient = 'bottom' internal.innerTickSize = 6 internal.outerTickSize = this.params.withOuterTick ? 6 : 0 internal.tickPadding = 3 internal.tickValues = null internal.tickFormat internal.tickArguments internal.tickOffset = 0 internal.tickCulling = true internal.tickCentered internal.tickTextCharSize internal.tickTextRotate = internal.params.tickTextRotate internal.tickLength internal.axis = internal.generateAxis() } AxisInternal.prototype.axisX = function(selection, x, tickOffset) { selection.attr('transform', function(d) { return 'translate(' + Math.ceil(x(d) + tickOffset) + ', 0)' }) } AxisInternal.prototype.axisY = function(selection, y) { selection.attr('transform', function(d) { return 'translate(0,' + Math.ceil(y(d)) + ')' }) } AxisInternal.prototype.scaleExtent = function(domain) { var start = domain[0], stop = domain[domain.length - 1] return start < stop ? [start, stop] : [stop, start] } AxisInternal.prototype.generateTicks = function(scale) { var internal = this var i, domain, ticks = [] if (scale.ticks) { return scale.ticks.apply(scale, internal.tickArguments) } domain = scale.domain() for (i = Math.ceil(domain[0]); i < domain[1]; i++) { ticks.push(i) } if (ticks.length > 0 && ticks[0] > 0) { ticks.unshift(ticks[0] - (ticks[1] - ticks[0])) } return ticks } AxisInternal.prototype.copyScale = function() { var internal = this var newScale = internal.scale.copy(), domain if (internal.params.isCategory) { domain = internal.scale.domain() newScale.domain([domain[0], domain[1] - 1]) } return newScale } AxisInternal.prototype.textFormatted = function(v) { var internal = this, formatted = internal.tickFormat ? internal.tickFormat(v) : v return typeof formatted !== 'undefined' ? formatted : '' } AxisInternal.prototype.updateRange = function() { var internal = this internal.range = internal.scale.rangeExtent ? internal.scale.rangeExtent() : internal.scaleExtent(internal.scale.range()) return internal.range } AxisInternal.prototype.updateTickTextCharSize = function(tick) { var internal = this if (internal.tickTextCharSize) { return internal.tickTextCharSize } var size = { h: 11.5, w: 5.5 } tick .select('text') .text(function(d) { return internal.textFormatted(d) }) .each(function(d) { var box = getBBox(this), text = internal.textFormatted(d), h = box.height, w = text ? box.width / text.length : undefined if (h && w) { size.h = h size.w = w } }) .text('') internal.tickTextCharSize = size return size } AxisInternal.prototype.isVertical = function() { return this.orient === 'left' || this.orient === 'right' } AxisInternal.prototype.tspanData = function(d, i, scale) { var internal = this var splitted = internal.params.tickMultiline ? internal.splitTickText(d, scale) : [].concat(internal.textFormatted(d)) if (internal.params.tickMultiline && internal.params.tickMultilineMax > 0) { splitted = internal.ellipsify(splitted, internal.params.tickMultilineMax) } return splitted.map(function(s) { return { index: i, splitted: s, length: splitted.length } }) } AxisInternal.prototype.splitTickText = function(d, scale) { var internal = this, tickText = internal.textFormatted(d), maxWidth = internal.params.tickWidth, subtext, spaceIndex, textWidth, splitted = [] if (Object.prototype.toString.call(tickText) === '[object Array]') { return tickText } if (!maxWidth || maxWidth <= 0) { maxWidth = internal.isVertical() ? 95 : internal.params.isCategory ? Math.ceil(scale(1) - scale(0)) - 12 : 110 } function split(splitted, text) { spaceIndex = undefined for (var i = 1; i < text.length; i++) { if (text.charAt(i) === ' ') { spaceIndex = i } subtext = text.substr(0, i + 1) textWidth = internal.tickTextCharSize.w * subtext.length // if text width gets over tick width, split by space index or crrent index if (maxWidth < textWidth) { return split( splitted.concat(text.substr(0, spaceIndex ? spaceIndex : i)), text.slice(spaceIndex ? spaceIndex + 1 : i) ) } } return splitted.concat(text) } return split(splitted, tickText + '') } AxisInternal.prototype.ellipsify = function(splitted, max) { if (splitted.length <= max) { return splitted } var ellipsified = splitted.slice(0, max) var remaining = 3 for (var i = max - 1; i >= 0; i--) { var available = ellipsified[i].length ellipsified[i] = ellipsified[i] .substr(0, available - remaining) .padEnd(available, '.') remaining -= available if (remaining <= 0) { break } } return ellipsified } AxisInternal.prototype.updateTickLength = function() { var internal = this internal.tickLength = Math.max(internal.innerTickSize, 0) + internal.tickPadding } AxisInternal.prototype.lineY2 = function(d) { var internal = this, tickPosition = internal.scale(d) + (internal.tickCentered ? 0 : internal.tickOffset) return internal.range[0] < tickPosition && tickPosition < internal.range[1] ? internal.innerTickSize : 0 } AxisInternal.prototype.textY = function() { var internal = this, rotate = internal.tickTextRotate return rotate ? 11.5 - 2.5 * (rotate / 15) * (rotate > 0 ? 1 : -1) : internal.tickLength } AxisInternal.prototype.textTransform = function() { var internal = this, rotate = internal.tickTextRotate return rotate ? 'rotate(' + rotate + ')' : '' } AxisInternal.prototype.textTextAnchor = function() { var internal = this, rotate = internal.tickTextRotate return rotate ? (rotate > 0 ? 'start' : 'end') : 'middle' } AxisInternal.prototype.tspanDx = function() { var internal = this, rotate = internal.tickTextRotate return rotate ? 8 * Math.sin(Math.PI * (rotate / 180)) : 0 } AxisInternal.prototype.tspanDy = function(d, i) { var internal = this, dy = internal.tickTextCharSize.h if (i === 0) { if (internal.isVertical()) { dy = -((d.length - 1) * (internal.tickTextCharSize.h / 2) - 3) } else { dy = '.71em' } } return dy } AxisInternal.prototype.generateAxis = function() { var internal = this, d3 = internal.d3, params = internal.params function axis(g, transition) { var self g.each(function() { var g = ((axis as any).g = d3.select(this)) var scale0 = this.__chart__ || internal.scale, scale1 = (this.__chart__ = internal.copyScale()) var ticksValues = internal.tickValues ? internal.tickValues : internal.generateTicks(scale1), ticks = g.selectAll('.tick').data(ticksValues, scale1), tickEnter = ticks .enter() .insert('g', '.domain') .attr('class', 'tick') .style('opacity', 1e-6), // MEMO: No exit transition. The reason is this transition affects max tick width calculation because old tick will be included in the ticks. tickExit = ticks.exit().remove(), tickUpdate = ticks.merge(tickEnter), tickTransform, tickX, tickY if (params.isCategory) { internal.tickOffset = Math.ceil((scale1(1) - scale1(0)) / 2) tickX = internal.tickCentered ? 0 : internal.tickOffset tickY = internal.tickCentered ? internal.tickOffset : 0 } else { internal.tickOffset = tickX = 0 } internal.updateRange() internal.updateTickLength() internal.updateTickTextCharSize(g.select('.tick')) var lineUpdate = tickUpdate .select('line') .merge(tickEnter.append('line')), textUpdate = tickUpdate.select('text').merge(tickEnter.append('text')) var tspans = tickUpdate .selectAll('text') .selectAll('tspan') .data(function(d, i) { return internal.tspanData(d, i, scale1) }), tspanEnter = tspans.enter().append('tspan'), tspanUpdate = tspanEnter.merge(tspans).text(function(d) { return d.splitted }) tspans.exit().remove() var path = g.selectAll('.domain').data([0]), pathUpdate = path .enter() .append('path') .merge(path) .attr('class', 'domain') // TODO: each attr should be one function and change its behavior by internal.orient, probably switch (internal.orient) { case 'bottom': { tickTransform = internal.axisX lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return internal.lineY2(d, i) }) textUpdate .attr('x', 0) .attr('y', function(d, i) { return internal.textY(d, i) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }) tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }) pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + internal.outerTickSize ) break } case 'top': { // TODO: rotated tick text tickTransform = internal.axisX lineUpdate .attr('x1', tickX) .attr('x2', tickX) .attr('y2', function(d, i) { return -1 * internal.lineY2(d, i) }) textUpdate .attr('x', 0) .attr('y', function(d, i) { return ( -1 * internal.textY(d, i) - (params.isCategory ? 2 : internal.tickLength - 2) ) }) .attr('transform', function(d, i) { return internal.textTransform(d, i) }) .style('text-anchor', function(d, i) { return internal.textTextAnchor(d, i) }) tspanUpdate .attr('x', 0) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) .attr('dx', function(d, i) { return internal.tspanDx(d, i) }) pathUpdate.attr( 'd', 'M' + internal.range[0] + ',' + -internal.outerTickSize + 'V0H' + internal.range[1] + 'V' + -internal.outerTickSize ) break } case 'left': { tickTransform = internal.axisY lineUpdate .attr('x2', -internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY) textUpdate .attr('x', -internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'end') tspanUpdate .attr('x', -internal.tickLength) .attr('dy', function(d, i) { return internal.tspanDy(d, i) }) pathUpdate.attr( 'd', 'M' + -internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + -internal.outerTickSize ) break } case 'right': { tickTransform = internal.axisY lineUpdate .attr('x2', internal.innerTickSize) .attr('y1', tickY) .attr('y2', tickY) textUpdate .attr('x', internal.tickLength) .attr('y', internal.tickOffset) .style('text-anchor', 'start') tspanUpdate.attr('x', internal.tickLength).attr('dy', function(d, i) { return internal.tspanDy(d, i) }) pathUpdate.attr( 'd', 'M' + internal.outerTickSize + ',' + internal.range[0] + 'H0V' + internal.range[1] + 'H' + internal.outerTickSize ) break } } if (scale1.rangeBand) { var x = scale1, dx = x.rangeBand() / 2 scale0 = scale1 = function(d) { return x(d) + dx } } else if (scale0.rangeBand) { scale0 = scale1 } else { tickExit.call(tickTransform, scale1, internal.tickOffset) } tickEnter.call(tickTransform, scale0, internal.tickOffset) self = (transition ? tickUpdate.transition(transition) : tickUpdate) .style('opacity', 1) .call(tickTransform, scale1, internal.tickOffset) }) return self } axis.scale = function(x) { if (!arguments.length) { return internal.scale } internal.scale = x return axis } axis.orient = function(x) { if (!arguments.length) { return internal.orient } internal.orient = x in { top: 1, right: 1, bottom: 1, left: 1 } ? x + '' : 'bottom' return axis } axis.tickFormat = function(format) { if (!arguments.length) { return internal.tickFormat } internal.tickFormat = format return axis } axis.tickCentered = function(isCentered) { if (!arguments.length) { return internal.tickCentered } internal.tickCentered = isCentered return axis } axis.tickOffset = function() { return internal.tickOffset } axis.tickInterval = function() { var interval, length if (params.isCategory) { interval = internal.tickOffset * 2 } else { length = (axis as any).g .select('path.domain') .node() .getTotalLength() - internal.outerTickSize * 2 interval = length / (axis as any).g.selectAll('line').size() } return interval === Infinity ? 0 : interval } axis.ticks = function() { if (!arguments.length) { return internal.tickArguments } internal.tickArguments = arguments return axis } axis.tickCulling = function(culling) { if (!arguments.length) { return internal.tickCulling } internal.tickCulling = culling return axis } axis.tickValues = function(x) { if (typeof x === 'function') { internal.tickValues = function() { return x(internal.scale.domain()) } } else { if (!arguments.length) { return internal.tickValues } internal.tickValues = x } return axis } return axis } export { AxisInternal } ================================================ FILE: src/axis.ts ================================================ import CLASS from './class' import { isValue, isFunction, isString, isEmpty, getBBox } from './util' import { AxisInternal } from './axis-internal' export default class AxisClass { owner: any d3: any internal: typeof AxisInternal constructor(owner) { this.owner = owner this.d3 = owner.d3 this.internal = AxisInternal } } const Axis = AxisClass as any Axis.prototype.init = function init() { var $$ = this.owner, config = $$.config, main = $$.main $$.axes.x = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisX) .attr('clip-path', config.axis_x_inner ? '' : $$.clipPathForXAxis) .attr('transform', $$.getTranslate('x')) .style('visibility', config.axis_x_show ? 'visible' : 'hidden') $$.axes.x .append('text') .attr('class', CLASS.axisXLabel) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .style('text-anchor', this.textAnchorForXAxisLabel.bind(this)) $$.axes.y = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY) .attr('clip-path', config.axis_y_inner ? '' : $$.clipPathForYAxis) .attr('transform', $$.getTranslate('y')) .style('visibility', config.axis_y_show ? 'visible' : 'hidden') $$.axes.y .append('text') .attr('class', CLASS.axisYLabel) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForYAxisLabel.bind(this)) $$.axes.y2 = main .append('g') .attr('class', CLASS.axis + ' ' + CLASS.axisY2) // clip-path? .attr('transform', $$.getTranslate('y2')) .style('visibility', config.axis_y2_show ? 'visible' : 'hidden') $$.axes.y2 .append('text') .attr('class', CLASS.axisY2Label) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .style('text-anchor', this.textAnchorForY2AxisLabel.bind(this)) } Axis.prototype.getXAxis = function getXAxis( scale, orient, tickFormat, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { var $$ = this.owner, config = $$.config, axisParams = { isCategory: $$.isCategorized(), withOuterTick: withOuterTick, tickMultiline: config.axis_x_tick_multiline, tickMultilineMax: config.axis_x_tick_multiline ? Number(config.axis_x_tick_multilineMax) : 0, tickWidth: config.axis_x_tick_width, tickTextRotate: withoutRotateTickText ? 0 : config.axis_x_tick_rotate, withoutTransition: withoutTransition }, axis = new this.internal(this, axisParams).axis.scale(scale).orient(orient) if ($$.isTimeSeries() && tickValues && typeof tickValues !== 'function') { tickValues = tickValues.map(function(v) { return $$.parseDate(v) }) } // Set tick axis.tickFormat(tickFormat).tickValues(tickValues) if ($$.isCategorized()) { axis.tickCentered(config.axis_x_tick_centered) if (isEmpty(config.axis_x_tick_culling)) { config.axis_x_tick_culling = false } } return axis } Axis.prototype.updateXAxisTickValues = function updateXAxisTickValues( targets, axis ) { var $$ = this.owner, config = $$.config, tickValues if (config.axis_x_tick_fit || config.axis_x_tick_count) { tickValues = this.generateTickValues( $$.mapTargetsToUniqueXs(targets), config.axis_x_tick_count, $$.isTimeSeries() ) } if (axis) { axis.tickValues(tickValues) } else { $$.xAxis.tickValues(tickValues) $$.subXAxis.tickValues(tickValues) } return tickValues } Axis.prototype.getYAxis = function getYAxis( axisId, scale, orient, tickValues, withOuterTick, withoutTransition, withoutRotateTickText ) { const $$ = this.owner const config = $$.config let tickFormat = config[`axis_${axisId}_tick_format`] if (!tickFormat && $$.isAxisNormalized(axisId)) { tickFormat = x => `${x}%` } const axis = new this.internal(this, { withOuterTick: withOuterTick, withoutTransition: withoutTransition, tickTextRotate: withoutRotateTickText ? 0 : config.axis_y_tick_rotate }).axis .scale(scale) .orient(orient) if (tickFormat) { axis.tickFormat(tickFormat) } if ($$.isTimeSeriesY()) { axis.ticks(config.axis_y_tick_time_type, config.axis_y_tick_time_interval) } else { axis.tickValues(tickValues) } return axis } Axis.prototype.getId = function getId(id) { var config = this.owner.config return id in config.data_axes ? config.data_axes[id] : 'y' } Axis.prototype.getXAxisTickFormat = function getXAxisTickFormat() { // #2251 previously set any negative values to a whole number, // however both should be truncated according to the users format specification var $$ = this.owner, config = $$.config let format = $$.isTimeSeries() ? $$.defaultAxisTimeFormat : $$.isCategorized() ? $$.categoryName : function(v) { return v } if (config.axis_x_tick_format) { if (isFunction(config.axis_x_tick_format)) { format = config.axis_x_tick_format } else if ($$.isTimeSeries()) { format = function(date) { return date ? $$.axisTimeFormat(config.axis_x_tick_format)(date) : '' } } } return isFunction(format) ? function(v) { return format.call($$, v) } : format } Axis.prototype.getTickValues = function getTickValues(tickValues, axis) { return tickValues ? tickValues : axis ? axis.tickValues() : undefined } Axis.prototype.getXAxisTickValues = function getXAxisTickValues() { return this.getTickValues( this.owner.config.axis_x_tick_values, this.owner.xAxis ) } Axis.prototype.getYAxisTickValues = function getYAxisTickValues() { return this.getTickValues( this.owner.config.axis_y_tick_values, this.owner.yAxis ) } Axis.prototype.getY2AxisTickValues = function getY2AxisTickValues() { return this.getTickValues( this.owner.config.axis_y2_tick_values, this.owner.y2Axis ) } Axis.prototype.getLabelOptionByAxisId = function getLabelOptionByAxisId( axisId ) { var $$ = this.owner, config = $$.config, option if (axisId === 'y') { option = config.axis_y_label } else if (axisId === 'y2') { option = config.axis_y2_label } else if (axisId === 'x') { option = config.axis_x_label } return option } Axis.prototype.getLabelText = function getLabelText(axisId) { var option = this.getLabelOptionByAxisId(axisId) return isString(option) ? option : option ? option.text : null } Axis.prototype.setLabelText = function setLabelText(axisId, text) { var $$ = this.owner, config = $$.config, option = this.getLabelOptionByAxisId(axisId) if (isString(option)) { if (axisId === 'y') { config.axis_y_label = text } else if (axisId === 'y2') { config.axis_y2_label = text } else if (axisId === 'x') { config.axis_x_label = text } } else if (option) { option.text = text } } Axis.prototype.getLabelPosition = function getLabelPosition( axisId, defaultPosition ) { var option = this.getLabelOptionByAxisId(axisId), position = option && typeof option === 'object' && option.position ? option.position : defaultPosition return { isInner: position.indexOf('inner') >= 0, isOuter: position.indexOf('outer') >= 0, isLeft: position.indexOf('left') >= 0, isCenter: position.indexOf('center') >= 0, isRight: position.indexOf('right') >= 0, isTop: position.indexOf('top') >= 0, isMiddle: position.indexOf('middle') >= 0, isBottom: position.indexOf('bottom') >= 0 } } Axis.prototype.getXAxisLabelPosition = function getXAxisLabelPosition() { return this.getLabelPosition( 'x', this.owner.config.axis_rotated ? 'inner-top' : 'inner-right' ) } Axis.prototype.getYAxisLabelPosition = function getYAxisLabelPosition() { return this.getLabelPosition( 'y', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) } Axis.prototype.getY2AxisLabelPosition = function getY2AxisLabelPosition() { return this.getLabelPosition( 'y2', this.owner.config.axis_rotated ? 'inner-right' : 'inner-top' ) } Axis.prototype.getLabelPositionById = function getLabelPositionById(id) { return id === 'y2' ? this.getY2AxisLabelPosition() : id === 'y' ? this.getYAxisLabelPosition() : this.getXAxisLabelPosition() } Axis.prototype.textForXAxisLabel = function textForXAxisLabel() { return this.getLabelText('x') } Axis.prototype.textForYAxisLabel = function textForYAxisLabel() { return this.getLabelText('y') } Axis.prototype.textForY2AxisLabel = function textForY2AxisLabel() { return this.getLabelText('y2') } Axis.prototype.xForAxisLabel = function xForAxisLabel(forHorizontal, position) { var $$ = this.owner if (forHorizontal) { return position.isLeft ? 0 : position.isCenter ? $$.width / 2 : $$.width } else { return position.isBottom ? -$$.height : position.isMiddle ? -$$.height / 2 : 0 } } Axis.prototype.dxForAxisLabel = function dxForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? '0.5em' : position.isRight ? '-0.5em' : '0' } else { return position.isTop ? '-0.5em' : position.isBottom ? '0.5em' : '0' } } Axis.prototype.textAnchorForAxisLabel = function textAnchorForAxisLabel( forHorizontal, position ) { if (forHorizontal) { return position.isLeft ? 'start' : position.isCenter ? 'middle' : 'end' } else { return position.isBottom ? 'start' : position.isMiddle ? 'middle' : 'end' } } Axis.prototype.xForXAxisLabel = function xForXAxisLabel() { return this.xForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) } Axis.prototype.xForYAxisLabel = function xForYAxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) } Axis.prototype.xForY2AxisLabel = function xForY2AxisLabel() { return this.xForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) } Axis.prototype.dxForXAxisLabel = function dxForXAxisLabel() { return this.dxForAxisLabel( !this.owner.config.axis_rotated, this.getXAxisLabelPosition() ) } Axis.prototype.dxForYAxisLabel = function dxForYAxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getYAxisLabelPosition() ) } Axis.prototype.dxForY2AxisLabel = function dxForY2AxisLabel() { return this.dxForAxisLabel( this.owner.config.axis_rotated, this.getY2AxisLabelPosition() ) } Axis.prototype.dyForXAxisLabel = function dyForXAxisLabel() { var $$ = this.owner, config = $$.config, position = this.getXAxisLabelPosition() if (config.axis_rotated) { return position.isInner ? '1.2em' : -25 - ($$.config.axis_x_inner ? 0 : this.getMaxTickWidth('x')) } else { return position.isInner ? '-0.5em' : $$.getHorizontalAxisHeight('x') - 10 } } Axis.prototype.dyForYAxisLabel = function dyForYAxisLabel() { var $$ = this.owner, position = this.getYAxisLabelPosition() if ($$.config.axis_rotated) { return position.isInner ? '-0.5em' : '3em' } else { return position.isInner ? '1.2em' : -10 - ($$.config.axis_y_inner ? 0 : this.getMaxTickWidth('y') + 10) } } Axis.prototype.dyForY2AxisLabel = function dyForY2AxisLabel() { var $$ = this.owner, position = this.getY2AxisLabelPosition() if ($$.config.axis_rotated) { return position.isInner ? '1.2em' : '-2.2em' } else { return position.isInner ? '-0.5em' : 15 + ($$.config.axis_y2_inner ? 0 : this.getMaxTickWidth('y2') + 15) } } Axis.prototype.textAnchorForXAxisLabel = function textAnchorForXAxisLabel() { var $$ = this.owner return this.textAnchorForAxisLabel( !$$.config.axis_rotated, this.getXAxisLabelPosition() ) } Axis.prototype.textAnchorForYAxisLabel = function textAnchorForYAxisLabel() { var $$ = this.owner return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getYAxisLabelPosition() ) } Axis.prototype.textAnchorForY2AxisLabel = function textAnchorForY2AxisLabel() { var $$ = this.owner return this.textAnchorForAxisLabel( $$.config.axis_rotated, this.getY2AxisLabelPosition() ) } Axis.prototype.getMaxTickWidth = function getMaxTickWidth( id, withoutRecompute ) { var $$ = this.owner, maxWidth = 0, targetsToShow, scale, axis, dummy, svg if (withoutRecompute && $$.currentMaxTickWidths[id]) { return $$.currentMaxTickWidths[id] } if ($$.svg) { targetsToShow = $$.filterTargetsToShow($$.data.targets) if (id === 'y') { scale = $$.y.copy().domain($$.getYDomain(targetsToShow, 'y')) axis = this.getYAxis( id, scale, $$.yOrient, $$.yAxisTickValues, false, true, true ) } else if (id === 'y2') { scale = $$.y2.copy().domain($$.getYDomain(targetsToShow, 'y2')) axis = this.getYAxis( id, scale, $$.y2Orient, $$.y2AxisTickValues, false, true, true ) } else { scale = $$.x.copy().domain($$.getXDomain(targetsToShow)) axis = this.getXAxis( scale, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, false, true, true ) this.updateXAxisTickValues(targetsToShow, axis) } dummy = $$.d3 .select('body') .append('div') .classed('c3', true) ;(svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0)), svg .append('g') .call(axis) .each(function() { $$.d3 .select(this) .selectAll('text') .each(function() { var box = getBBox(this) if (maxWidth < box.width) { maxWidth = box.width } }) dummy.remove() }) } $$.currentMaxTickWidths[id] = maxWidth <= 0 ? $$.currentMaxTickWidths[id] : maxWidth return $$.currentMaxTickWidths[id] } Axis.prototype.updateLabels = function updateLabels(withTransition) { var $$ = this.owner var axisXLabel = $$.main.select('.' + CLASS.axisX + ' .' + CLASS.axisXLabel), axisYLabel = $$.main.select('.' + CLASS.axisY + ' .' + CLASS.axisYLabel), axisY2Label = $$.main.select('.' + CLASS.axisY2 + ' .' + CLASS.axisY2Label) ;(withTransition ? axisXLabel.transition() : axisXLabel) .attr('x', this.xForXAxisLabel.bind(this)) .attr('dx', this.dxForXAxisLabel.bind(this)) .attr('dy', this.dyForXAxisLabel.bind(this)) .text(this.textForXAxisLabel.bind(this)) ;(withTransition ? axisYLabel.transition() : axisYLabel) .attr('x', this.xForYAxisLabel.bind(this)) .attr('dx', this.dxForYAxisLabel.bind(this)) .attr('dy', this.dyForYAxisLabel.bind(this)) .text(this.textForYAxisLabel.bind(this)) ;(withTransition ? axisY2Label.transition() : axisY2Label) .attr('x', this.xForY2AxisLabel.bind(this)) .attr('dx', this.dxForY2AxisLabel.bind(this)) .attr('dy', this.dyForY2AxisLabel.bind(this)) .text(this.textForY2AxisLabel.bind(this)) } Axis.prototype.getPadding = function getPadding( padding, key, defaultValue, domainLength ) { var p = typeof padding === 'number' ? padding : padding[key] if (!isValue(p)) { return defaultValue } if (padding.unit === 'ratio') { return padding[key] * domainLength } // assume padding is pixels if unit is not specified return this.convertPixelsToAxisPadding(p, domainLength) } Axis.prototype.convertPixelsToAxisPadding = function convertPixelsToAxisPadding( pixels, domainLength ) { var $$ = this.owner, length = $$.config.axis_rotated ? $$.width : $$.height return domainLength * (pixels / length) } Axis.prototype.generateTickValues = function generateTickValues( values, tickCount, forTimeSeries ) { var tickValues = values, targetCount, start, end, count, interval, i, tickValue if (tickCount) { targetCount = isFunction(tickCount) ? tickCount() : tickCount // compute ticks according to tickCount if (targetCount === 1) { tickValues = [values[0]] } else if (targetCount === 2) { tickValues = [values[0], values[values.length - 1]] } else if (targetCount > 2) { count = targetCount - 2 start = values[0] end = values[values.length - 1] interval = (end - start) / (count + 1) // re-construct unique values tickValues = [start] for (i = 0; i < count; i++) { tickValue = +start + interval * (i + 1) tickValues.push(forTimeSeries ? new Date(tickValue) : tickValue) } tickValues.push(end) } } if (!forTimeSeries) { tickValues = tickValues.sort(function(a, b) { return a - b }) } return tickValues } Axis.prototype.generateTransitions = function generateTransitions(duration) { var $$ = this.owner, axes = $$.axes return { axisX: duration ? axes.x.transition().duration(duration) : axes.x, axisY: duration ? axes.y.transition().duration(duration) : axes.y, axisY2: duration ? axes.y2.transition().duration(duration) : axes.y2, axisSubX: duration ? axes.subx.transition().duration(duration) : axes.subx } } Axis.prototype.redraw = function redraw(duration, isHidden) { var $$ = this.owner, transition = duration ? $$.d3.transition().duration(duration) : null $$.axes.x.style('opacity', isHidden ? 0 : 1).call($$.xAxis, transition) $$.axes.y.style('opacity', isHidden ? 0 : 1).call($$.yAxis, transition) $$.axes.y2.style('opacity', isHidden ? 0 : 1).call($$.y2Axis, transition) $$.axes.subx.style('opacity', isHidden ? 0 : 1).call($$.subXAxis, transition) } ================================================ FILE: src/cache.ts ================================================ import { ChartInternal } from './core' /** * Store value into cache * * @param key * @param value */ ChartInternal.prototype.addToCache = function(key, value) { this.cache[`$${key}`] = value } /** * Returns a cached value or undefined * * @param key * @return {*} */ ChartInternal.prototype.getFromCache = function(key) { return this.cache[`$${key}`] } /** * Reset cached data */ ChartInternal.prototype.resetCache = function() { Object.keys(this.cache) .filter(key => /^\$/.test(key)) .forEach(key => { delete this.cache[key] }) } // Old API that stores Targets ChartInternal.prototype.hasCaches = function(ids) { for (var i = 0; i < ids.length; i++) { if (!(ids[i] in this.cache)) { return false } } return true } ChartInternal.prototype.addCache = function(id, target) { this.cache[id] = this.cloneTarget(target) } ChartInternal.prototype.getCaches = function(ids) { var targets = [], i for (i = 0; i < ids.length; i++) { if (ids[i] in this.cache) { targets.push(this.cloneTarget(this.cache[ids[i]])) } } return targets } ================================================ FILE: src/category.ts ================================================ import { ChartInternal } from './core' ChartInternal.prototype.categoryName = function(i) { var config = this.config return i < config.axis_x_categories.length ? config.axis_x_categories[i] : i } ================================================ FILE: src/chart-internal.ts ================================================ export function ChartInternal(api) { var $$ = this // Note: This part will be replaced by rollup-plugin-modify // When bundling esm output. Beware of changing this line. // TODO: Maybe we should check that the modification by rollup-plugin-modify // is valid during unit tests. $$.d3 = (window as any).d3 ? (window as any).d3 : typeof require !== 'undefined' ? require('d3') : undefined $$.api = api $$.config = $$.getDefaultConfig() $$.data = {} $$.cache = {} $$.axes = {} } ================================================ FILE: src/chart.ts ================================================ import { ChartInternal } from './chart-internal' /** * The Chart class * * The methods of this class is the public APIs of the chart object. */ export function Chart(config) { this.internal = new ChartInternal(this) this.internal.loadConfig(config) this.internal.beforeInit(config) this.internal.init() this.internal.afterInit(config) // bind "this" to nested API ;(function bindThis(fn, target, argThis) { Object.keys(fn).forEach(function(key) { target[key] = fn[key].bind(argThis) if (Object.keys(fn[key]).length > 0) { bindThis(fn[key], target[key], argThis) } }) })(Chart.prototype, this, this) } ================================================ FILE: src/class-utils.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' ChartInternal.prototype.generateTargetClass = function(targetId) { return targetId || targetId === 0 ? ('-' + targetId).replace(/\s/g, '-') : '' } ChartInternal.prototype.generateClass = function(prefix, targetId) { return ' ' + prefix + ' ' + prefix + this.generateTargetClass(targetId) } ChartInternal.prototype.classText = function(d) { return this.generateClass(CLASS.text, d.index) } ChartInternal.prototype.classTexts = function(d) { return this.generateClass(CLASS.texts, d.id) } ChartInternal.prototype.classShape = function(d) { return this.generateClass(CLASS.shape, d.index) } ChartInternal.prototype.classShapes = function(d) { return this.generateClass(CLASS.shapes, d.id) } ChartInternal.prototype.classLine = function(d) { return this.classShape(d) + this.generateClass(CLASS.line, d.id) } ChartInternal.prototype.classLines = function(d) { return this.classShapes(d) + this.generateClass(CLASS.lines, d.id) } ChartInternal.prototype.classCircle = function(d) { return this.classShape(d) + this.generateClass(CLASS.circle, d.index) } ChartInternal.prototype.classCircles = function(d) { return this.classShapes(d) + this.generateClass(CLASS.circles, d.id) } ChartInternal.prototype.classBar = function(d) { return this.classShape(d) + this.generateClass(CLASS.bar, d.index) } ChartInternal.prototype.classBars = function(d) { return this.classShapes(d) + this.generateClass(CLASS.bars, d.id) } ChartInternal.prototype.classArc = function(d) { return this.classShape(d.data) + this.generateClass(CLASS.arc, d.data.id) } ChartInternal.prototype.classArcs = function(d) { return this.classShapes(d.data) + this.generateClass(CLASS.arcs, d.data.id) } ChartInternal.prototype.classArea = function(d) { return this.classShape(d) + this.generateClass(CLASS.area, d.id) } ChartInternal.prototype.classAreas = function(d) { return this.classShapes(d) + this.generateClass(CLASS.areas, d.id) } ChartInternal.prototype.classRegion = function(d, i) { return ( this.generateClass(CLASS.region, i) + ' ' + ('class' in d ? d['class'] : '') ) } ChartInternal.prototype.classEvent = function(d) { return this.generateClass(CLASS.eventRect, d.index) } ChartInternal.prototype.classTarget = function(id) { var $$ = this var additionalClassSuffix = $$.config.data_classes[id], additionalClass = '' if (additionalClassSuffix) { additionalClass = ' ' + CLASS.target + '-' + additionalClassSuffix } return $$.generateClass(CLASS.target, id) + additionalClass } ChartInternal.prototype.classFocus = function(d) { return this.classFocused(d) + this.classDefocused(d) } ChartInternal.prototype.classFocused = function(d) { return ' ' + (this.focusedTargetIds.indexOf(d.id) >= 0 ? CLASS.focused : '') } ChartInternal.prototype.classDefocused = function(d) { return ( ' ' + (this.defocusedTargetIds.indexOf(d.id) >= 0 ? CLASS.defocused : '') ) } ChartInternal.prototype.classChartText = function(d) { return CLASS.chartText + this.classTarget(d.id) } ChartInternal.prototype.classChartLine = function(d) { return CLASS.chartLine + this.classTarget(d.id) } ChartInternal.prototype.classChartBar = function(d) { return CLASS.chartBar + this.classTarget(d.id) } ChartInternal.prototype.classChartArc = function(d) { return CLASS.chartArc + this.classTarget(d.data.id) } ChartInternal.prototype.getTargetSelectorSuffix = function(targetId) { const targetClass = this.generateTargetClass(targetId) if (window.CSS && window.CSS.escape) { return window.CSS.escape(targetClass) } // fallback on imperfect method for old browsers (does not handles unicode) return targetClass.replace(/([?!@#$%^&*()=+,.<>'":;\[\]\/|~`{}\\])/g, '\\$1') } ChartInternal.prototype.selectorTarget = function(id, prefix) { return (prefix || '') + '.' + CLASS.target + this.getTargetSelectorSuffix(id) } ChartInternal.prototype.selectorTargets = function(ids, prefix) { var $$ = this ids = ids || [] return ids.length ? ids.map(function(id) { return $$.selectorTarget(id, prefix) }) : null } ChartInternal.prototype.selectorLegend = function(id) { return '.' + CLASS.legendItem + this.getTargetSelectorSuffix(id) } ChartInternal.prototype.selectorLegends = function(ids) { var $$ = this return ids && ids.length ? ids.map(function(id) { return $$.selectorLegend(id) }) : null } ================================================ FILE: src/class.ts ================================================ export default { target: 'c3-target', chart: 'c3-chart', chartLine: 'c3-chart-line', chartLines: 'c3-chart-lines', chartBar: 'c3-chart-bar', chartBars: 'c3-chart-bars', chartText: 'c3-chart-text', chartTexts: 'c3-chart-texts', chartArc: 'c3-chart-arc', chartArcs: 'c3-chart-arcs', chartArcsTitle: 'c3-chart-arcs-title', chartArcsBackground: 'c3-chart-arcs-background', chartArcsGaugeUnit: 'c3-chart-arcs-gauge-unit', chartArcsGaugeMax: 'c3-chart-arcs-gauge-max', chartArcsGaugeMin: 'c3-chart-arcs-gauge-min', selectedCircle: 'c3-selected-circle', selectedCircles: 'c3-selected-circles', eventRect: 'c3-event-rect', eventRects: 'c3-event-rects', eventRectsSingle: 'c3-event-rects-single', eventRectsMultiple: 'c3-event-rects-multiple', zoomRect: 'c3-zoom-rect', brush: 'c3-brush', dragZoom: 'c3-drag-zoom', focused: 'c3-focused', defocused: 'c3-defocused', region: 'c3-region', regions: 'c3-regions', title: 'c3-title', tooltipContainer: 'c3-tooltip-container', tooltip: 'c3-tooltip', tooltipName: 'c3-tooltip-name', shape: 'c3-shape', shapes: 'c3-shapes', line: 'c3-line', lines: 'c3-lines', bar: 'c3-bar', bars: 'c3-bars', circle: 'c3-circle', circles: 'c3-circles', arc: 'c3-arc', arcLabelLine: 'c3-arc-label-line', arcs: 'c3-arcs', area: 'c3-area', areas: 'c3-areas', empty: 'c3-empty', text: 'c3-text', texts: 'c3-texts', gaugeValue: 'c3-gauge-value', grid: 'c3-grid', gridLines: 'c3-grid-lines', xgrid: 'c3-xgrid', xgrids: 'c3-xgrids', xgridLine: 'c3-xgrid-line', xgridLines: 'c3-xgrid-lines', xgridFocus: 'c3-xgrid-focus', ygrid: 'c3-ygrid', ygrids: 'c3-ygrids', ygridLine: 'c3-ygrid-line', ygridLines: 'c3-ygrid-lines', colorScale: 'c3-colorscale', stanfordElements: 'c3-stanford-elements', stanfordLine: 'c3-stanford-line', stanfordLines: 'c3-stanford-lines', stanfordRegion: 'c3-stanford-region', stanfordRegions: 'c3-stanford-regions', stanfordText: 'c3-stanford-text', stanfordTexts: 'c3-stanford-texts', axis: 'c3-axis', axisX: 'c3-axis-x', axisXLabel: 'c3-axis-x-label', axisY: 'c3-axis-y', axisYLabel: 'c3-axis-y-label', axisY2: 'c3-axis-y2', axisY2Label: 'c3-axis-y2-label', legendBackground: 'c3-legend-background', legendItem: 'c3-legend-item', legendItemEvent: 'c3-legend-item-event', legendItemTile: 'c3-legend-item-tile', legendItemHidden: 'c3-legend-item-hidden', legendItemFocused: 'c3-legend-item-focused', dragarea: 'c3-dragarea', EXPANDED: '_expanded_', SELECTED: '_selected_', INCLUDED: '_included_' } ================================================ FILE: src/clip.ts ================================================ import { ChartInternal } from './core' import { isIE } from './util' ChartInternal.prototype.getClipPath = function(id) { return 'url(' + (isIE(9) ? '' : document.URL.split('#')[0]) + '#' + id + ')' } ChartInternal.prototype.appendClip = function(parent, id) { return parent .append('clipPath') .attr('id', id) .append('rect') } ChartInternal.prototype.getAxisClipX = function(forHorizontal) { // axis line width + padding for left var left = Math.max(30, this.margin.left) return forHorizontal ? -(1 + left) : -(left - 1) } ChartInternal.prototype.getAxisClipY = function(forHorizontal) { return forHorizontal ? -20 : -this.margin.top } ChartInternal.prototype.getXAxisClipX = function() { var $$ = this return $$.getAxisClipX(!$$.config.axis_rotated) } ChartInternal.prototype.getXAxisClipY = function() { var $$ = this return $$.getAxisClipY(!$$.config.axis_rotated) } ChartInternal.prototype.getYAxisClipX = function() { var $$ = this return $$.config.axis_y_inner ? -1 : $$.getAxisClipX($$.config.axis_rotated) } ChartInternal.prototype.getYAxisClipY = function() { var $$ = this return $$.getAxisClipY($$.config.axis_rotated) } ChartInternal.prototype.getAxisClipWidth = function(forHorizontal) { var $$ = this, left = Math.max(30, $$.margin.left), right = Math.max(30, $$.margin.right) // width + axis line width + padding for left/right return forHorizontal ? $$.width + 2 + left + right : $$.margin.left + 20 } ChartInternal.prototype.getAxisClipHeight = function(forHorizontal) { // less than 20 is not enough to show the axis label 'outer' without legend return ( (forHorizontal ? this.margin.bottom : this.margin.top + this.height) + 20 ) } ChartInternal.prototype.getXAxisClipWidth = function() { var $$ = this return $$.getAxisClipWidth(!$$.config.axis_rotated) } ChartInternal.prototype.getXAxisClipHeight = function() { var $$ = this return $$.getAxisClipHeight(!$$.config.axis_rotated) } ChartInternal.prototype.getYAxisClipWidth = function() { var $$ = this return ( $$.getAxisClipWidth($$.config.axis_rotated) + ($$.config.axis_y_inner ? 20 : 0) ) } ChartInternal.prototype.getYAxisClipHeight = function() { var $$ = this return $$.getAxisClipHeight($$.config.axis_rotated) } ================================================ FILE: src/color.ts ================================================ import { ChartInternal } from './core' import { notEmpty } from './util' ChartInternal.prototype.generateColor = function() { var $$ = this, config = $$.config, d3 = $$.d3, colors = config.data_colors, pattern = notEmpty(config.color_pattern) ? config.color_pattern : d3.schemeCategory10, callback = config.data_color, ids = [] return function(d) { var id = d.id || (d.data && d.data.id) || d, color // if callback function is provided if (colors[id] instanceof Function) { color = colors[id](d) } // if specified, choose that color else if (colors[id]) { color = colors[id] } // if not specified, choose from pattern else { if (ids.indexOf(id) < 0) { ids.push(id) } color = pattern[ids.indexOf(id) % pattern.length] colors[id] = color } return callback instanceof Function ? callback(color, d) : color } } ChartInternal.prototype.generateLevelColor = function() { var $$ = this, config = $$.config, colors = config.color_pattern, threshold = config.color_threshold, asValue = threshold.unit === 'value', values = threshold.values && threshold.values.length ? threshold.values : [], max = threshold.max || 100 return notEmpty(threshold) && notEmpty(colors) ? function(value) { var i, v, color = colors[colors.length - 1] for (i = 0; i < values.length; i++) { v = asValue ? value : (value * 100) / max if (v < values[i]) { color = colors[i] break } } return color } : null } ================================================ FILE: src/colorscale.ts ================================================ import { ChartInternal } from './core' import CLASS from './class' import { isFunction, getBBox } from './util' function powerOfTen(d) { return d / Math.pow(10, Math.ceil(Math.log(d) / Math.LN10 - 1e-12)) === 1 } ChartInternal.prototype.drawColorScale = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], barWidth, barHeight, axis, points, legendAxis, axisScale, inverseScale, height barWidth = !isNaN(config.stanford_scaleWidth) ? config.stanford_scaleWidth : 20 barHeight = 5 if (barHeight < 0 || barWidth < 0) { throw Error("Colorscale's barheight and barwidth must be greater than 0.") } height = $$.height - config.stanford_padding.bottom - config.stanford_padding.top points = d3.range(config.stanford_padding.bottom, height, barHeight) inverseScale = d3 .scaleSequential(target.colors) .domain([points[points.length - 1], points[0]]) if ($$.colorScale) { $$.colorScale.remove() } $$.colorScale = $$.svg .append('g') .attr('width', 50) .attr('height', height) .attr('class', CLASS.colorScale) $$.colorScale .append('g') .attr('transform', `translate(0, ${config.stanford_padding.top})`) .selectAll('bars') .data(points) .enter() .append('rect') .attr('y', (d, i) => i * barHeight) .attr('x', 0) .attr('width', barWidth) .attr('height', barHeight) .attr('fill', function(d) { return inverseScale(d) }) // Legend Axis axisScale = d3 .scaleLog() .domain([target.minEpochs, target.maxEpochs]) .range([ points[0] + config.stanford_padding.top + points[points.length - 1] + barHeight - 1, points[0] + config.stanford_padding.top ]) legendAxis = d3.axisRight(axisScale) if (config.stanford_scaleFormat === 'pow10') { legendAxis.tickValues([1, 10, 100, 1000, 10000, 100000, 1000000, 10000000]) } else if (isFunction(config.stanford_scaleFormat)) { legendAxis.tickFormat(config.stanford_scaleFormat) } else { legendAxis.tickFormat(d3.format('d')) } if (isFunction(config.stanford_scaleValues)) { legendAxis.tickValues( config.stanford_scaleValues(target.minEpochs, target.maxEpochs) ) } // Draw Axis axis = $$.colorScale .append('g') .attr('class', 'legend axis') .attr('transform', `translate(${barWidth},0)`) .call(legendAxis) if (config.stanford_scaleFormat === 'pow10') { axis .selectAll('.tick text') .text(null) .filter(powerOfTen) .text(10) .append('tspan') .attr('dy', '-.7em') // https://bl.ocks.org/mbostock/6738229 .text(function(d) { return Math.round(Math.log(d) / Math.LN10) }) } $$.colorScale.attr( 'transform', `translate(${$$.currentWidth - $$.xForColorScale()}, 0)` ) } ChartInternal.prototype.xForColorScale = function() { var $$ = this return $$.config.stanford_padding.right + getBBox($$.colorScale.node()).width } ChartInternal.prototype.getColorScalePadding = function() { var $$ = this return $$.xForColorScale() + $$.config.stanford_padding.left + 20 } ================================================ FILE: src/config.ts ================================================ import { ChartInternal } from './core' import { isDefined } from './util' ChartInternal.prototype.getDefaultConfig = function() { var config = { bindto: '#chart', svg_classname: undefined, size_width: undefined, size_height: undefined, padding_left: undefined, padding_right: undefined, padding_top: undefined, padding_bottom: undefined, resize_auto: true, zoom_enabled: false, zoom_initialRange: undefined, zoom_type: 'scroll', zoom_disableDefaultBehavior: false, zoom_privileged: false, zoom_rescale: false, zoom_onzoom: function() {}, zoom_onzoomstart: function() {}, zoom_onzoomend: function() {}, zoom_x_min: undefined, zoom_x_max: undefined, interaction_brighten: true, interaction_enabled: true, onmouseover: function() {}, onmouseout: function() {}, onresize: function() {}, onresized: function() {}, oninit: function() {}, onrendered: function() {}, transition_duration: 350, data_epochs: 'epochs', data_x: undefined, data_xs: {}, data_xFormat: '%Y-%m-%d', data_xLocaltime: true, data_xSort: true, data_idConverter: function(id) { return id }, data_names: {}, data_classes: {}, data_groups: [], data_axes: {}, data_type: undefined, data_types: {}, data_labels: {}, data_order: 'desc', data_regions: {}, data_color: undefined, data_colors: {}, data_hide: false, data_filter: undefined, data_selection_enabled: false, data_selection_grouped: false, data_selection_isselectable: function() { return true }, data_selection_multiple: true, data_selection_draggable: false, data_stack_normalize: false, data_onclick: function() {}, data_onmouseover: function() {}, data_onmouseout: function() {}, data_onselected: function() {}, data_onunselected: function() {}, data_url: undefined, data_headers: undefined, data_json: undefined, data_rows: undefined, data_columns: undefined, data_mimeType: undefined, data_keys: undefined, // configuration for no plot-able data supplied. data_empty_label_text: '', // subchart subchart_show: false, subchart_size_height: 60, subchart_axis_x_show: true, subchart_onbrush: function() {}, // color color_pattern: [], color_threshold: {}, // legend legend_show: true, legend_hide: false, legend_position: 'bottom', legend_inset_anchor: 'top-left', legend_inset_x: 10, legend_inset_y: 0, legend_inset_step: undefined, legend_item_onclick: undefined, legend_item_onmouseover: undefined, legend_item_onmouseout: undefined, legend_equally: false, legend_padding: 0, legend_item_tile_width: 10, legend_item_tile_height: 10, // axis axis_rotated: false, axis_x_show: true, axis_x_type: 'indexed', axis_x_localtime: true, axis_x_categories: [], axis_x_tick_centered: false, axis_x_tick_format: undefined, axis_x_tick_culling: {}, axis_x_tick_culling_max: 10, axis_x_tick_count: undefined, axis_x_tick_fit: true, axis_x_tick_values: null, axis_x_tick_rotate: 0, axis_x_tick_outer: true, axis_x_tick_multiline: true, axis_x_tick_multilineMax: 0, axis_x_tick_width: null, axis_x_max: undefined, axis_x_min: undefined, axis_x_padding: {}, axis_x_height: undefined, axis_x_selection: undefined, axis_x_label: {}, axis_x_inner: undefined, axis_y_show: true, axis_y_type: 'linear', axis_y_max: undefined, axis_y_min: undefined, axis_y_inverted: false, axis_y_center: undefined, axis_y_inner: undefined, axis_y_label: {}, axis_y_tick_format: undefined, axis_y_tick_outer: true, axis_y_tick_values: null, axis_y_tick_rotate: 0, axis_y_tick_count: undefined, axis_y_tick_time_type: undefined, axis_y_tick_time_interval: undefined, axis_y_padding: {}, axis_y_default: undefined, axis_y2_show: false, axis_y2_type: 'linear', axis_y2_max: undefined, axis_y2_min: undefined, axis_y2_inverted: false, axis_y2_center: undefined, axis_y2_inner: undefined, axis_y2_label: {}, axis_y2_tick_format: undefined, axis_y2_tick_outer: true, axis_y2_tick_values: null, axis_y2_tick_count: undefined, axis_y2_padding: {}, axis_y2_default: undefined, // grid grid_x_show: false, grid_x_type: 'tick', grid_x_lines: [], grid_y_show: false, // not used // grid_y_type: 'tick', grid_y_lines: [], grid_y_ticks: 10, grid_focus_show: true, grid_lines_front: true, // point - point of each data point_show: true, point_r: 2.5, point_sensitivity: 10, point_focus_expand_enabled: true, point_focus_expand_r: undefined, point_select_r: undefined, // line line_connectNull: false, line_step_type: 'step', // bar bar_width: undefined, bar_width_ratio: 0.6, bar_width_max: undefined, bar_zerobased: true, bar_space: 0, // area area_zerobased: true, area_above: false, // pie pie_label_show: true, pie_label_format: undefined, pie_label_threshold: 0.05, pie_label_ratio: undefined, pie_expand: {}, pie_expand_duration: 50, pie_padAngle: 0, // gauge gauge_fullCircle: false, gauge_label_show: true, gauge_labelLine_show: true, gauge_label_format: undefined, gauge_min: 0, gauge_max: 100, gauge_startingAngle: (-1 * Math.PI) / 2, gauge_label_extents: undefined, gauge_units: undefined, gauge_width: undefined, gauge_arcs_minWidth: 5, gauge_expand: {}, gauge_expand_duration: 50, // donut donut_label_show: true, donut_label_format: undefined, donut_label_threshold: 0.05, donut_label_ratio: undefined, donut_width: undefined, donut_title: '', donut_expand: {}, donut_expand_duration: 50, donut_padAngle: 0, // spline spline_interpolation_type: 'cardinal', // stanford stanford_lines: [], stanford_regions: [], stanford_texts: [], stanford_scaleMin: undefined, stanford_scaleMax: undefined, stanford_scaleWidth: undefined, stanford_scaleFormat: undefined, stanford_scaleValues: undefined, stanford_colors: undefined, stanford_padding: { top: 0, right: 0, bottom: 0, left: 0 }, // region - region to change style regions: [], // tooltip - show when mouseover on each data tooltip_show: true, tooltip_grouped: true, tooltip_order: undefined, tooltip_format_title: undefined, tooltip_format_name: undefined, tooltip_format_value: undefined, tooltip_horizontal: undefined, tooltip_position: undefined, tooltip_contents: function( d, defaultTitleFormat, defaultValueFormat, color ) { return this.getTooltipContent ? this.getTooltipContent( d, defaultTitleFormat, defaultValueFormat, color ) : '' }, tooltip_init_show: false, tooltip_init_x: 0, tooltip_init_position: { top: '0px', left: '50px' }, tooltip_onshow: function() {}, tooltip_onhide: function() {}, // title title_text: undefined, title_padding: { top: 0, right: 0, bottom: 0, left: 0 }, title_position: 'top-center' } Object.keys(this.additionalConfig).forEach(function(key) { config[key] = this.additionalConfig[key] }, this) return config } ChartInternal.prototype.additionalConfig = {} ChartInternal.prototype.loadConfig = function(config) { var this_config = this.config, target, keys, read function find() { var key = keys.shift() // console.log("key =>", key, ", target =>", target); if (key && target && typeof target === 'object' && key in target) { target = target[key] return find() } else if (!key) { return target } else { return undefined } } Object.keys(this_config).forEach(function(key) { target = config keys = key.split('_') read = find() // console.log("CONFIG : ", key, read); if (isDefined(read)) { this_config[key] = read } }) } ================================================ FILE: src/core.ts ================================================ import { ChartInternal } from './chart-internal' import { Chart } from './chart' import { AxisInternal } from './axis-internal' import Axis from './axis' import CLASS from './class' import { asHalfPixel, getOption, getPathBox, isFunction, isValue, notEmpty } from './util' var c3 = { version: '0.7.20', chart: { fn: Chart.prototype, internal: { fn: ChartInternal.prototype, axis: { fn: Axis.prototype, internal: { fn: AxisInternal.prototype } } } }, generate: function(config) { return new Chart(config) } } export { c3 } ChartInternal.prototype.beforeInit = function() { // can do something } ChartInternal.prototype.afterInit = function() { // can do something } ChartInternal.prototype.init = function() { var $$ = this, config = $$.config $$.initParams() if (config.data_url) { $$.convertUrlToData( config.data_url, config.data_mimeType, config.data_headers, config.data_keys, $$.initWithData ) } else if (config.data_json) { $$.initWithData($$.convertJsonToData(config.data_json, config.data_keys)) } else if (config.data_rows) { $$.initWithData($$.convertRowsToData(config.data_rows)) } else if (config.data_columns) { $$.initWithData($$.convertColumnsToData(config.data_columns)) } else { throw Error('url or json or rows or columns is required.') } } ChartInternal.prototype.initParams = function() { var $$ = this, d3 = $$.d3, config = $$.config // MEMO: clipId needs to be unique because it conflicts when multiple charts exist $$.clipId = 'c3-' + new Date().valueOf() + '-clip' $$.clipIdForXAxis = $$.clipId + '-xaxis' $$.clipIdForYAxis = $$.clipId + '-yaxis' $$.clipIdForGrid = $$.clipId + '-grid' $$.clipIdForSubchart = $$.clipId + '-subchart' $$.clipPath = $$.getClipPath($$.clipId) $$.clipPathForXAxis = $$.getClipPath($$.clipIdForXAxis) $$.clipPathForYAxis = $$.getClipPath($$.clipIdForYAxis) $$.clipPathForGrid = $$.getClipPath($$.clipIdForGrid) $$.clipPathForSubchart = $$.getClipPath($$.clipIdForSubchart) $$.dragStart = null $$.dragging = false $$.flowing = false $$.cancelClick = false $$.mouseover = undefined $$.transiting = false $$.color = $$.generateColor() $$.levelColor = $$.generateLevelColor() $$.dataTimeParse = (config.data_xLocaltime ? d3.timeParse : d3.utcParse)( $$.config.data_xFormat ) $$.axisTimeFormat = config.axis_x_localtime ? d3.timeFormat : d3.utcFormat $$.defaultAxisTimeFormat = function(date) { if (date.getMilliseconds()) { return d3.timeFormat('.%L')(date) } if (date.getSeconds()) { return d3.timeFormat(':%S')(date) } if (date.getMinutes()) { return d3.timeFormat('%I:%M')(date) } if (date.getHours()) { return d3.timeFormat('%I %p')(date) } if (date.getDay() && date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getDate() !== 1) { return d3.timeFormat('%-m/%-d')(date) } if (date.getMonth()) { return d3.timeFormat('%-m/%-d')(date) } return d3.timeFormat('%Y/%-m/%-d')(date) } $$.hiddenTargetIds = [] $$.hiddenLegendIds = [] $$.focusedTargetIds = [] $$.defocusedTargetIds = [] $$.xOrient = config.axis_rotated ? config.axis_x_inner ? 'right' : 'left' : config.axis_x_inner ? 'top' : 'bottom' $$.yOrient = config.axis_rotated ? config.axis_y_inner ? 'top' : 'bottom' : config.axis_y_inner ? 'right' : 'left' $$.y2Orient = config.axis_rotated ? config.axis_y2_inner ? 'bottom' : 'top' : config.axis_y2_inner ? 'left' : 'right' $$.subXOrient = config.axis_rotated ? 'left' : 'bottom' $$.isLegendRight = config.legend_position === 'right' $$.isLegendInset = config.legend_position === 'inset' $$.isLegendTop = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'top-right' $$.isLegendLeft = config.legend_inset_anchor === 'top-left' || config.legend_inset_anchor === 'bottom-left' $$.legendStep = 0 $$.legendItemWidth = 0 $$.legendItemHeight = 0 $$.currentMaxTickWidths = { x: 0, y: 0, y2: 0 } $$.rotated_padding_left = 30 $$.rotated_padding_right = config.axis_rotated && !config.axis_x_show ? 0 : 30 $$.rotated_padding_top = 5 $$.withoutFadeIn = {} $$.intervalForObserveInserted = undefined $$.axes.subx = d3.selectAll([]) // needs when excluding subchart.js } ChartInternal.prototype.initChartElements = function() { if (this.initBar) { this.initBar() } if (this.initLine) { this.initLine() } if (this.initArc) { this.initArc() } if (this.initGauge) { this.initGauge() } if (this.initText) { this.initText() } } ChartInternal.prototype.initWithData = function(data) { var $$ = this, d3 = $$.d3, config = $$.config var defs, main, binding = true $$.axis = new Axis($$) if (!config.bindto) { $$.selectChart = d3.selectAll([]) } else if (typeof config.bindto.node === 'function') { $$.selectChart = config.bindto } else { $$.selectChart = d3.select(config.bindto) } if ($$.selectChart.empty()) { $$.selectChart = d3 .select(document.createElement('div')) .style('opacity', 0) $$.observeInserted($$.selectChart) binding = false } $$.selectChart.html('').classed('c3', true) // Init data as targets $$.data.xs = {} $$.data.targets = $$.convertDataToTargets(data) if (config.data_filter) { $$.data.targets = $$.data.targets.filter(config.data_filter) } // Set targets to hide if needed if (config.data_hide) { $$.addHiddenTargetIds( config.data_hide === true ? $$.mapToIds($$.data.targets) : config.data_hide ) } if (config.legend_hide) { $$.addHiddenLegendIds( config.legend_hide === true ? $$.mapToIds($$.data.targets) : config.legend_hide ) } if ($$.isStanfordGraphType()) { $$.initStanfordData() } // Init sizes and scales $$.updateSizes() $$.updateScales() // Set domains for each scale $$.x.domain(d3.extent($$.getXDomain($$.data.targets))) $$.y.domain($$.getYDomain($$.data.targets, 'y')) $$.y2.domain($$.getYDomain($$.data.targets, 'y2')) $$.subX.domain($$.x.domain()) $$.subY.domain($$.y.domain()) $$.subY2.domain($$.y2.domain()) // Save original x domain for zoom update $$.orgXDomain = $$.x.domain() /*-- Basic Elements --*/ // Define svgs $$.svg = $$.selectChart .append('svg') .style('overflow', 'hidden') .on('mouseenter', function() { return config.onmouseover.call($$) }) .on('mouseleave', function() { return config.onmouseout.call($$) }) if ($$.config.svg_classname) { $$.svg.attr('class', $$.config.svg_classname) } // Define defs defs = $$.svg.append('defs') $$.clipChart = $$.appendClip(defs, $$.clipId) $$.clipXAxis = $$.appendClip(defs, $$.clipIdForXAxis) $$.clipYAxis = $$.appendClip(defs, $$.clipIdForYAxis) $$.clipGrid = $$.appendClip(defs, $$.clipIdForGrid) $$.clipSubchart = $$.appendClip(defs, $$.clipIdForSubchart) $$.updateSvgSize() // Define regions main = $$.main = $$.svg.append('g').attr('transform', $$.getTranslate('main')) if ($$.initPie) { $$.initPie() } if ($$.initDragZoom) { $$.initDragZoom() } if (config.subchart_show && $$.initSubchart) { $$.initSubchart() } if ($$.initTooltip) { $$.initTooltip() } if ($$.initLegend) { $$.initLegend() } if ($$.initTitle) { $$.initTitle() } if ($$.initZoom) { $$.initZoom() } if ($$.isStanfordGraphType()) { $$.drawColorScale() } // Update selection based on size and scale // TODO: currently this must be called after initLegend because of update of sizes, but it should be done in initSubchart. if (config.subchart_show && $$.initSubchartBrush) { $$.initSubchartBrush() } /*-- Main Region --*/ // text when empty main .append('text') .attr('class', CLASS.text + ' ' + CLASS.empty) .attr('text-anchor', 'middle') // horizontal centering of text at x position in all browsers. .attr('dominant-baseline', 'middle') // vertical centering of text at y position in all browsers, except IE. // Regions $$.initRegion() // Grids $$.initGrid() // Define g for chart area main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.chart) // Grid lines if (config.grid_lines_front) { $$.initGridLines() } $$.initStanfordElements() // Cover whole with rects for events $$.initEventRect() // Define g for chart $$.initChartElements() // Add Axis $$.axis.init() // Set targets $$.updateTargets($$.data.targets) // Set default extent if defined if (config.axis_x_selection) { $$.brush.selectionAsValue($$.getDefaultSelection()) } // Draw with targets if (binding) { $$.updateDimension() $$.config.oninit.call($$) $$.redraw({ withTransition: false, withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransitionForAxis: false }) } // Bind to resize event $$.bindResize() // Bind to window focus event $$.bindWindowFocus() // export element of the chart $$.api.element = $$.selectChart.node() } ChartInternal.prototype.smoothLines = function(el, type) { var $$ = this if (type === 'grid') { el.each(function() { var g = $$.d3.select(this), x1 = g.attr('x1'), x2 = g.attr('x2'), y1 = g.attr('y1'), y2 = g.attr('y2') g.attr({ x1: Math.ceil(x1), x2: Math.ceil(x2), y1: Math.ceil(y1), y2: Math.ceil(y2) }) }) } } ChartInternal.prototype.updateSizes = function() { var $$ = this, config = $$.config var legendHeight = $$.legend ? $$.getLegendHeight() : 0, legendWidth = $$.legend ? $$.getLegendWidth() : 0, legendHeightForBottom = $$.isLegendRight || $$.isLegendInset ? 0 : legendHeight, hasArc = $$.hasArcType(), xAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x'), subchartXAxisHeight = config.axis_rotated || hasArc ? 0 : $$.getHorizontalAxisHeight('x',true), subchartHeight = config.subchart_show && !hasArc ? config.subchart_size_height + subchartXAxisHeight : 0 $$.currentWidth = $$.getCurrentWidth() $$.currentHeight = $$.getCurrentHeight() // for main $$.margin = config.axis_rotated ? { top: $$.getHorizontalAxisHeight('y2') + $$.getCurrentPaddingTop(), right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: $$.getHorizontalAxisHeight('y') + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: subchartHeight + (hasArc ? 0 : $$.getCurrentPaddingLeft()) } : { top: 4 + $$.getCurrentPaddingTop(), // for top tick text right: hasArc ? 0 : $$.getCurrentPaddingRight(), bottom: xAxisHeight + subchartHeight + legendHeightForBottom + $$.getCurrentPaddingBottom(), left: hasArc ? 0 : $$.getCurrentPaddingLeft() } // for subchart $$.margin2 = config.axis_rotated ? { top: $$.margin.top, right: NaN, bottom: 20 + legendHeightForBottom, left: $$.rotated_padding_left } : { top: $$.currentHeight - subchartHeight - legendHeightForBottom, right: NaN, bottom: subchartXAxisHeight + legendHeightForBottom, left: $$.margin.left } // for legend $$.margin3 = { top: 0, right: NaN, bottom: 0, left: 0 } if ($$.updateSizeForLegend) { $$.updateSizeForLegend(legendHeight, legendWidth) } $$.width = $$.currentWidth - $$.margin.left - $$.margin.right $$.height = $$.currentHeight - $$.margin.top - $$.margin.bottom if ($$.width < 0) { $$.width = 0 } if ($$.height < 0) { $$.height = 0 } $$.width2 = config.axis_rotated ? $$.margin.left - $$.rotated_padding_left - $$.rotated_padding_right : $$.width $$.height2 = config.axis_rotated ? $$.height : $$.currentHeight - $$.margin2.top - $$.margin2.bottom if ($$.width2 < 0) { $$.width2 = 0 } if ($$.height2 < 0) { $$.height2 = 0 } // for arc $$.arcWidth = $$.width - ($$.isLegendRight ? legendWidth + 10 : 0) $$.arcHeight = $$.height - ($$.isLegendRight ? 0 : 10) if ($$.hasType('gauge') && !config.gauge_fullCircle) { $$.arcHeight += $$.height - $$.getGaugeLabelHeight() } if ($$.updateRadius) { $$.updateRadius() } if ($$.isLegendRight && hasArc) { $$.margin3.left = $$.arcWidth / 2 + $$.radiusExpanded * 1.1 } } ChartInternal.prototype.updateTargets = function(targets) { var $$ = this, config = $$.config /*-- Main --*/ //-- Text --// $$.updateTargetsForText(targets) //-- Bar --// $$.updateTargetsForBar(targets) //-- Line --// $$.updateTargetsForLine(targets) //-- Arc --// if ($$.hasArcType() && $$.updateTargetsForArc) { $$.updateTargetsForArc(targets) } /*-- Sub --*/ if (config.subchart_show && $$.updateTargetsForSubchart) { $$.updateTargetsForSubchart(targets) } // Fade-in each chart $$.showTargets() } ChartInternal.prototype.showTargets = function() { var $$ = this $$.svg .selectAll('.' + CLASS.target) .filter(function(d) { return $$.isTargetToShow(d.id) }) .transition() .duration($$.config.transition_duration) .style('opacity', 1) } ChartInternal.prototype.redraw = function(options, transitions) { var $$ = this, main = $$.main, d3 = $$.d3, config = $$.config var areaIndices = $$.getShapeIndices($$.isAreaType), barIndices = $$.getShapeIndices($$.isBarType), lineIndices = $$.getShapeIndices($$.isLineType) var withY, withSubchart, withTransition, withTransitionForExit, withTransitionForAxis, withTransform, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain, withLegend, withEventRect, withDimension, withUpdateXAxis var hideAxis = $$.hasArcType() var drawArea, drawBar, drawLine, xForText, yForText var duration, durationForExit, durationForAxis var transitionsToWait, waitForDraw, flow, transition var targetsToShow = $$.filterTargetsToShow($$.data.targets), tickValues, i, intervalForCulling, xDomainForZoom var xv = $$.xv.bind($$), cx, cy options = options || {} withY = getOption(options, 'withY', true) withSubchart = getOption(options, 'withSubchart', true) withTransition = getOption(options, 'withTransition', true) withTransform = getOption(options, 'withTransform', false) withUpdateXDomain = getOption(options, 'withUpdateXDomain', false) withUpdateOrgXDomain = getOption(options, 'withUpdateOrgXDomain', false) withTrimXDomain = getOption(options, 'withTrimXDomain', true) withUpdateXAxis = getOption(options, 'withUpdateXAxis', withUpdateXDomain) withLegend = getOption(options, 'withLegend', false) withEventRect = getOption(options, 'withEventRect', true) withDimension = getOption(options, 'withDimension', true) withTransitionForExit = getOption( options, 'withTransitionForExit', withTransition ) withTransitionForAxis = getOption( options, 'withTransitionForAxis', withTransition ) duration = withTransition ? config.transition_duration : 0 durationForExit = withTransitionForExit ? duration : 0 durationForAxis = withTransitionForAxis ? duration : 0 transitions = transitions || $$.axis.generateTransitions(durationForAxis) // update legend and transform each g if (withLegend && config.legend_show) { $$.updateLegend($$.mapToIds($$.data.targets), options, transitions) } else if (withDimension) { // need to update dimension (e.g. axis.y.tick.values) because y tick values should change // no need to update axis in it because they will be updated in redraw() $$.updateDimension(true) } // MEMO: needed for grids calculation if ($$.isCategorized() && targetsToShow.length === 0) { $$.x.domain([0, $$.axes.x.selectAll('.tick').size()]) } if (targetsToShow.length) { $$.updateXDomain( targetsToShow, withUpdateXDomain, withUpdateOrgXDomain, withTrimXDomain ) if (!config.axis_x_tick_values) { tickValues = $$.axis.updateXAxisTickValues(targetsToShow) } } else { $$.xAxis.tickValues([]) $$.subXAxis.tickValues([]) } if (config.zoom_rescale && !options.flow) { xDomainForZoom = $$.x.orgDomain() } $$.y.domain($$.getYDomain(targetsToShow, 'y', xDomainForZoom)) $$.y2.domain($$.getYDomain(targetsToShow, 'y2', xDomainForZoom)) if (!config.axis_y_tick_values && config.axis_y_tick_count) { $$.yAxis.tickValues( $$.axis.generateTickValues($$.y.domain(), config.axis_y_tick_count) ) } if (!config.axis_y2_tick_values && config.axis_y2_tick_count) { $$.y2Axis.tickValues( $$.axis.generateTickValues($$.y2.domain(), config.axis_y2_tick_count) ) } // axes $$.axis.redraw(durationForAxis, hideAxis) // Update axis label $$.axis.updateLabels(withTransition) // show/hide if manual culling needed if ((withUpdateXDomain || withUpdateXAxis) && targetsToShow.length) { if (config.axis_x_tick_culling && tickValues) { for (i = 1; i < tickValues.length; i++) { if (tickValues.length / i < config.axis_x_tick_culling_max) { intervalForCulling = i break } } $$.svg.selectAll('.' + CLASS.axisX + ' .tick text').each(function(e) { var index = tickValues.indexOf(e) if (index >= 0) { d3.select(this).style( 'display', index % intervalForCulling ? 'none' : 'block' ) } }) } else { $$.svg .selectAll('.' + CLASS.axisX + ' .tick text') .style('display', 'block') } } // setup drawer - MEMO: these must be called after axis updated drawArea = $$.generateDrawArea ? $$.generateDrawArea(areaIndices, false) : undefined drawBar = $$.generateDrawBar ? $$.generateDrawBar(barIndices) : undefined drawLine = $$.generateDrawLine ? $$.generateDrawLine(lineIndices, false) : undefined xForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, true) yForText = $$.generateXYForText(areaIndices, barIndices, lineIndices, false) // update circleY based on updated parameters $$.updateCircleY() // generate circle x/y functions depending on updated params cx = ($$.config.axis_rotated ? $$.circleY : $$.circleX).bind($$) cy = ($$.config.axis_rotated ? $$.circleX : $$.circleY).bind($$) // Update sub domain if (withY) { $$.subY.domain($$.getYDomain(targetsToShow, 'y')) $$.subY2.domain($$.getYDomain(targetsToShow, 'y2')) } // xgrid focus $$.updateXgridFocus() // Data empty label positioning and text. main .select('text.' + CLASS.text + '.' + CLASS.empty) .attr('x', $$.width / 2) .attr('y', $$.height / 2) .text(config.data_empty_label_text) .transition() .style('opacity', targetsToShow.length ? 0 : 1) // event rect if (withEventRect) { $$.redrawEventRect() } // grid $$.updateGrid(duration) $$.updateStanfordElements(duration) // rect for regions $$.updateRegion(duration) // bars $$.updateBar(durationForExit) // lines, areas and circles $$.updateLine(durationForExit) $$.updateArea(durationForExit) $$.updateCircle(cx, cy) // text if ($$.hasDataLabel()) { $$.updateText(xForText, yForText, durationForExit) } // title if ($$.redrawTitle) { $$.redrawTitle() } // arc if ($$.redrawArc) { $$.redrawArc(duration, durationForExit, withTransform) } // subchart if (config.subchart_show && $$.redrawSubchart) { $$.redrawSubchart( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ) } if ($$.isStanfordGraphType()) { $$.drawColorScale() } // circles for select main .selectAll('.' + CLASS.selectedCircles) .filter($$.isBarType.bind($$)) .selectAll('circle') .remove() if (options.flow) { flow = $$.generateFlow({ targets: targetsToShow, flow: options.flow, duration: options.flow.duration, drawBar: drawBar, drawLine: drawLine, drawArea: drawArea, cx: cx, cy: cy, xv: xv, xForText: xForText, yForText: yForText }) } if (duration && $$.isTabVisible()) { // Only use transition if tab visible. See #938. // transition should be derived from one transition transition = d3.transition().duration(duration) transitionsToWait = [] ;[ $$.redrawBar(drawBar, true, transition), $$.redrawLine(drawLine, true, transition), $$.redrawArea(drawArea, true, transition), $$.redrawCircle(cx, cy, true, transition), $$.redrawText(xForText, yForText, options.flow, true, transition), $$.redrawRegion(true, transition), $$.redrawGrid(true, transition) ].forEach(function(transitions) { transitions.forEach(function(transition) { transitionsToWait.push(transition) }) }) // Wait for end of transitions to call flow and onrendered callback waitForDraw = $$.generateWait() transitionsToWait.forEach(function(t) { waitForDraw.add(t) }) waitForDraw(function() { if (flow) { flow() } if (config.onrendered) { config.onrendered.call($$) } }) } else { $$.redrawBar(drawBar) $$.redrawLine(drawLine) $$.redrawArea(drawArea) $$.redrawCircle(cx, cy) $$.redrawText(xForText, yForText, options.flow) $$.redrawRegion() $$.redrawGrid() if (flow) { flow() } if (config.onrendered) { config.onrendered.call($$) } } // update fadein condition $$.mapToIds($$.data.targets).forEach(function(id) { $$.withoutFadeIn[id] = true }) } ChartInternal.prototype.updateAndRedraw = function(options) { var $$ = this, config = $$.config, transitions options = options || {} // same with redraw options.withTransition = getOption(options, 'withTransition', true) options.withTransform = getOption(options, 'withTransform', false) options.withLegend = getOption(options, 'withLegend', false) // NOT same with redraw options.withUpdateXDomain = getOption(options, 'withUpdateXDomain', true) options.withUpdateOrgXDomain = getOption( options, 'withUpdateOrgXDomain', true ) options.withTransitionForExit = false options.withTransitionForTransform = getOption( options, 'withTransitionForTransform', options.withTransition ) // MEMO: this needs to be called before updateLegend and it means this ALWAYS needs to be called) $$.updateSizes() // MEMO: called in updateLegend in redraw if withLegend if (!(options.withLegend && config.legend_show)) { transitions = $$.axis.generateTransitions( options.withTransitionForAxis ? config.transition_duration : 0 ) // Update scales $$.updateScales() $$.updateSvgSize() // Update g positions $$.transformAll(options.withTransitionForTransform, transitions) } // Draw with new sizes & scales $$.redraw(options, transitions) } ChartInternal.prototype.redrawWithoutRescale = function() { this.redraw({ withY: false, withSubchart: false, withEventRect: false, withTransitionForAxis: false }) } ChartInternal.prototype.isTimeSeries = function() { return this.config.axis_x_type === 'timeseries' } ChartInternal.prototype.isCategorized = function() { return this.config.axis_x_type.indexOf('categor') >= 0 } ChartInternal.prototype.isCustomX = function() { var $$ = this, config = $$.config return !$$.isTimeSeries() && (config.data_x || notEmpty(config.data_xs)) } ChartInternal.prototype.isTimeSeriesY = function() { return this.config.axis_y_type === 'timeseries' } ChartInternal.prototype.getTranslate = function(target) { var $$ = this, config = $$.config, x, y if (target === 'main') { x = asHalfPixel($$.margin.left) y = asHalfPixel($$.margin.top) } else if (target === 'context') { x = asHalfPixel($$.margin2.left) y = asHalfPixel($$.margin2.top) } else if (target === 'legend') { x = $$.margin3.left y = $$.margin3.top } else if (target === 'x') { x = 0 y = config.axis_rotated ? 0 : $$.height } else if (target === 'y') { x = 0 y = config.axis_rotated ? $$.height : 0 } else if (target === 'y2') { x = config.axis_rotated ? 0 : $$.width y = config.axis_rotated ? 1 : 0 } else if (target === 'subx') { x = 0 y = config.axis_rotated ? 0 : $$.height2 } else if (target === 'arc') { x = $$.arcWidth / 2 y = $$.arcHeight / 2 - ($$.hasType('gauge') ? 6 : 0) // to prevent wrong display of min and max label } return 'translate(' + x + ',' + y + ')' } ChartInternal.prototype.initialOpacity = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? 1 : 0 } ChartInternal.prototype.initialOpacityForCircle = function(d) { return d.value !== null && this.withoutFadeIn[d.id] ? this.opacityForCircle(d) : 0 } ChartInternal.prototype.opacityForCircle = function(d) { var isPointShouldBeShown = isFunction(this.config.point_show) ? this.config.point_show(d) : this.config.point_show var opacity = isPointShouldBeShown || this.isStanfordType(d) ? 1 : 0 return isValue(d.value) ? (this.isScatterType(d) ? 0.5 : opacity) : 0 } ChartInternal.prototype.opacityForText = function() { return this.hasDataLabel() ? 1 : 0 } ChartInternal.prototype.xx = function(d) { return d ? this.x(d.x) : null } ChartInternal.prototype.xvCustom = function(d, xyValue) { var $$ = this, value = xyValue ? d[xyValue] : d.value if ($$.isTimeSeries()) { value = $$.parseDate(d.value) } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value) } return Math.ceil($$.x(value)) } ChartInternal.prototype.yvCustom = function(d, xyValue) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y, value = xyValue ? d[xyValue] : d.value return Math.ceil(yScale(value)) } ChartInternal.prototype.xv = function(d) { var $$ = this, value = d.value if ($$.isTimeSeries()) { value = $$.parseDate(d.value) } else if ($$.isCategorized() && typeof d.value === 'string') { value = $$.config.axis_x_categories.indexOf(d.value) } return Math.ceil($$.x(value)) } ChartInternal.prototype.yv = function(d) { var $$ = this, yScale = d.axis && d.axis === 'y2' ? $$.y2 : $$.y return Math.ceil(yScale(d.value)) } ChartInternal.prototype.subxx = function(d) { return d ? this.subX(d.x) : null } ChartInternal.prototype.transformMain = function(withTransition, transitions) { var $$ = this, xAxis, yAxis, y2Axis if (transitions && transitions.axisX) { xAxis = transitions.axisX } else { xAxis = $$.main.select('.' + CLASS.axisX) if (withTransition) { xAxis = xAxis.transition() } } if (transitions && transitions.axisY) { yAxis = transitions.axisY } else { yAxis = $$.main.select('.' + CLASS.axisY) if (withTransition) { yAxis = yAxis.transition() } } if (transitions && transitions.axisY2) { y2Axis = transitions.axisY2 } else { y2Axis = $$.main.select('.' + CLASS.axisY2) if (withTransition) { y2Axis = y2Axis.transition() } } ;(withTransition ? $$.main.transition() : $$.main).attr( 'transform', $$.getTranslate('main') ) xAxis.attr('transform', $$.getTranslate('x')) yAxis.attr('transform', $$.getTranslate('y')) y2Axis.attr('transform', $$.getTranslate('y2')) $$.main .select('.' + CLASS.chartArcs) .attr('transform', $$.getTranslate('arc')) } ChartInternal.prototype.transformAll = function(withTransition, transitions) { var $$ = this $$.transformMain(withTransition, transitions) if ($$.config.subchart_show) { $$.transformContext(withTransition, transitions) } if ($$.legend) { $$.transformLegend(withTransition) } } ChartInternal.prototype.updateSvgSize = function() { var $$ = this, brush = $$.svg.select(`.${CLASS.brush} .overlay`) $$.svg.attr('width', $$.currentWidth).attr('height', $$.currentHeight) $$.svg .selectAll(['#' + $$.clipId, '#' + $$.clipIdForGrid]) .select('rect') .attr('width', $$.width) .attr('height', $$.height) $$.svg .select('#' + $$.clipIdForXAxis) .select('rect') .attr('x', $$.getXAxisClipX.bind($$)) .attr('y', $$.getXAxisClipY.bind($$)) .attr('width', $$.getXAxisClipWidth.bind($$)) .attr('height', $$.getXAxisClipHeight.bind($$)) $$.svg .select('#' + $$.clipIdForYAxis) .select('rect') .attr('x', $$.getYAxisClipX.bind($$)) .attr('y', $$.getYAxisClipY.bind($$)) .attr('width', $$.getYAxisClipWidth.bind($$)) .attr('height', $$.getYAxisClipHeight.bind($$)) $$.svg .select('#' + $$.clipIdForSubchart) .select('rect') .attr('width', $$.width) .attr('height', (brush.size() && brush.attr('height')) || 0) // MEMO: parent div's height will be bigger than svg when $$.selectChart.style('max-height', $$.currentHeight + 'px') } ChartInternal.prototype.updateDimension = function(withoutAxis) { var $$ = this if (!withoutAxis) { if ($$.config.axis_rotated) { $$.axes.x.call($$.xAxis) $$.axes.subx.call($$.subXAxis) } else { $$.axes.y.call($$.yAxis) $$.axes.y2.call($$.y2Axis) } } $$.updateSizes() $$.updateScales() $$.updateSvgSize() $$.transformAll(false) } ChartInternal.prototype.observeInserted = function(selection) { var $$ = this, observer if (typeof MutationObserver === 'undefined') { window.console.error('MutationObserver not defined.') return } observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { if (mutation.type === 'childList' && mutation.previousSibling) { observer.disconnect() // need to wait for completion of load because size calculation requires the actual sizes determined after that completion $$.intervalForObserveInserted = window.setInterval(function() { // parentNode will NOT be null when completed if (selection.node().parentNode) { window.clearInterval($$.intervalForObserveInserted) $$.updateDimension() if ($$.brush) { $$.brush.update() } $$.config.oninit.call($$) $$.redraw({ withTransform: true, withUpdateXDomain: true, withUpdateOrgXDomain: true, withTransition: false, withTransitionForTransform: false, withLegend: true }) selection.transition().style('opacity', 1) } }, 10) } }) }) observer.observe(selection.node(), { attributes: true, childList: true, characterData: true }) } /** * Binds handlers to the window resize event. */ ChartInternal.prototype.bindResize = function() { var $$ = this, config = $$.config $$.resizeFunction = $$.generateResize() // need to call .remove $$.resizeFunction.add(function() { config.onresize.call($$) }) if (config.resize_auto) { $$.resizeFunction.add(function() { if ($$.resizeTimeout !== undefined) { window.clearTimeout($$.resizeTimeout) } $$.resizeTimeout = window.setTimeout(function() { delete $$.resizeTimeout $$.updateAndRedraw({ withUpdateXDomain: false, withUpdateOrgXDomain: false, withTransition: false, withTransitionForTransform: false, withLegend: true }) if ($$.brush) { $$.brush.update() } }, 100) }) } $$.resizeFunction.add(function() { config.onresized.call($$) }) $$.resizeIfElementDisplayed = function() { // if element not displayed skip it if ($$.api == null || !$$.api.element.offsetParent) { return } $$.resizeFunction() } window.addEventListener('resize', $$.resizeIfElementDisplayed, false) } /** * Binds handlers to the window focus event. */ ChartInternal.prototype.bindWindowFocus = function() { if (this.windowFocusHandler) { // The handler is already set return } this.windowFocusHandler = () => { this.redraw() } window.addEventListener('focus', this.windowFocusHandler) } /** * Unbinds from the window focus event. */ ChartInternal.prototype.unbindWindowFocus = function() { window.removeEventListener('focus', this.windowFocusHandler) delete this.windowFocusHandler } ChartInternal.prototype.generateResize = function() { var resizeFunctions = [] function callResizeFunctions() { resizeFunctions.forEach(function(f) { f() }) } callResizeFunctions.add = function(f) { resizeFunctions.push(f) } callResizeFunctions.remove = function(f) { for (var i = 0; i < resizeFunctions.length; i++) { if (resizeFunctions[i] === f) { resizeFunctions.splice(i, 1) break } } } return callResizeFunctions } ChartInternal.prototype.endall = function(transition, callback) { var n = 0 transition .each(function() { ++n }) .on('end', function() { if (!--n) { callback.apply(this, arguments) } }) } ChartInternal.prototype.generateWait = function() { var $$ = this var transitionsToWait = [], f = function(callback) { var timer = setInterval(function() { if (!$$.isTabVisible()) { return } var done = 0 transitionsToWait.forEach(function(t) { if (t.empty()) { done += 1 return } try { t.transition() } catch (e) { done += 1 } }) if (done === transitionsToWait.length) { clearInterval(timer) if (callback) { callback() } } }, 50) } ;(f as any).add = function(transition) { transitionsToWait.push(transition) } return f } ChartInternal.prototype.parseDate = function(date) { var $$ = this, parsedDate if (date instanceof Date) { parsedDate = date } else if (typeof date === 'string') { parsedDate = $$.dataTimeParse(date) } else if (typeof date === 'object') { parsedDate = new Date(+date) } else if (typeof date === 'number' && !isNaN(date)) { parsedDate = new Date(+date) } if (!parsedDate || isNaN(+parsedDate)) { window.console.error("Failed to parse x '" + date + "' to Date object") } return parsedDate } ChartInternal.prototype.isTabVisible = function() { return !document.hidden } ChartInternal.prototype.getPathBox = getPathBox ChartInternal.prototype.CLASS = CLASS export { Chart } export { ChartInternal } ================================================ FILE: src/data.convert.ts ================================================ import { ChartInternal } from './core' import { isValue, isUndefined, isDefined, notEmpty, isArray } from './util' ChartInternal.prototype.convertUrlToData = function( url, mimeType, headers, keys, done ) { var $$ = this, type = mimeType ? mimeType : 'csv', f, converter if (type === 'json') { f = $$.d3.json converter = $$.convertJsonToData } else if (type === 'tsv') { f = $$.d3.tsv converter = $$.convertXsvToData } else { f = $$.d3.csv converter = $$.convertXsvToData } f(url, headers) .then(function(data) { done.call($$, converter.call($$, data, keys)) }) .catch(function(error) { throw error }) } ChartInternal.prototype.convertXsvToData = function(xsv) { var keys = xsv.columns, rows = xsv if (rows.length === 0) { return { keys, rows: [keys.reduce((row, key) => Object.assign(row, { [key]: null }), {})] } } else { // [].concat() is to convert result into a plain array otherwise // test is not happy because rows have properties. return { keys, rows: [].concat(xsv) } } } ChartInternal.prototype.convertJsonToData = function(json, keys) { var $$ = this, new_rows = [], targetKeys, data if (keys) { // when keys specified, json would be an array that includes objects if (keys.x) { targetKeys = keys.value.concat(keys.x) $$.config.data_x = keys.x } else { targetKeys = keys.value } new_rows.push(targetKeys) json.forEach(function(o) { var new_row = [] targetKeys.forEach(function(key) { // convert undefined to null because undefined data will be removed in convertDataToTargets() var v = $$.findValueInJson(o, key) if (isUndefined(v)) { v = null } new_row.push(v) }) new_rows.push(new_row) }) data = $$.convertRowsToData(new_rows) } else { Object.keys(json).forEach(function(key) { new_rows.push([key].concat(json[key])) }) data = $$.convertColumnsToData(new_rows) } return data } /** * Finds value from the given nested object by the given path. * If it's not found, then this returns undefined. * @param {Object} object the object * @param {string} path the path */ ChartInternal.prototype.findValueInJson = function(object, path) { if (path in object) { // If object has a key that contains . or [], return the key's value // instead of searching for an inner object. // See https://github.com/c3js/c3/issues/1691 for details. return object[path] } path = path.replace(/\[(\w+)\]/g, '.$1') // convert indexes to properties (replace [] with .) path = path.replace(/^\./, '') // strip a leading dot var pathArray = path.split('.') for (var i = 0; i < pathArray.length; ++i) { var k = pathArray[i] if (k in object) { object = object[k] } else { return } } return object } /** * Converts the rows to normalized data. * @param {any[][]} rows The row data * @return {Object} */ ChartInternal.prototype.convertRowsToData = rows => { const newRows = [] const keys = rows[0] for (let i = 1; i < rows.length; i++) { const newRow = {} for (let j = 0; j < rows[i].length; j++) { if (isUndefined(rows[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRow[keys[j]] = rows[i][j] } newRows.push(newRow) } return { keys, rows: newRows } } /** * Converts the columns to normalized data. * @param {any[][]} columns The column data * @return {Object} */ ChartInternal.prototype.convertColumnsToData = columns => { const newRows = [] const keys = [] for (let i = 0; i < columns.length; i++) { const key = columns[i][0] for (let j = 1; j < columns[i].length; j++) { if (isUndefined(newRows[j - 1])) { newRows[j - 1] = {} } if (isUndefined(columns[i][j])) { throw new Error( 'Source data is missing a component at (' + i + ',' + j + ')!' ) } newRows[j - 1][key] = columns[i][j] } keys.push(key) } return { keys, rows: newRows } } /** * Converts the data format into the target format. * @param {!Object} data * @param {!Array} data.keys Ordered list of target IDs. * @param {!Array} data.rows Rows of data to convert. * @param {boolean} appendXs True to append to $$.data.xs, False to replace. * @return {!Array} */ ChartInternal.prototype.convertDataToTargets = function(data, appendXs) { var $$ = this, config = $$.config, targets, ids, xs, keys, epochs // handles format where keys are not orderly provided if (isArray(data)) { keys = Object.keys(data[0]) } else { keys = data.keys data = data.rows } xs = keys.filter($$.isX, $$) if (!$$.isStanfordGraphType()) { ids = keys.filter($$.isNotX, $$) } else { epochs = keys.filter($$.isEpochs, $$) ids = keys.filter($$.isNotXAndNotEpochs, $$) if (xs.length !== 1 || epochs.length !== 1 || ids.length !== 1) { throw new Error( "You must define the 'x' key name and the 'epochs' for Stanford Diagrams" ) } } // save x for update data by load when custom x and c3.x API ids.forEach(function(id) { var xKey = $$.getXKey(id) if ($$.isCustomX() || $$.isTimeSeries()) { // if included in input data if (xs.indexOf(xKey) >= 0) { $$.data.xs[id] = (appendXs && $$.data.xs[id] ? $$.data.xs[id] : [] ).concat( data .map(function(d) { return d[xKey] }) .filter(isValue) .map(function(rawX, i) { return $$.generateTargetX(rawX, id, i) }) ) } // if not included in input data, find from preloaded data of other id's x else if (config.data_x) { $$.data.xs[id] = $$.getOtherTargetXs() } // if not included in input data, find from preloaded data else if (notEmpty(config.data_xs)) { $$.data.xs[id] = $$.getXValuesOfXKey(xKey, $$.data.targets) } // MEMO: if no x included, use same x of current will be used } else { $$.data.xs[id] = data.map(function(d, i) { return i }) } }) // check x is defined ids.forEach(function(id) { if (!$$.data.xs[id]) { throw new Error('x is not defined for id = "' + id + '".') } }) // convert to target targets = ids.map(function(id, index) { var convertedId = config.data_idConverter(id) return { id: convertedId, id_org: id, values: data .map(function(d, i) { var xKey = $$.getXKey(id), rawX = d[xKey], value = d[id] !== null && !isNaN(d[id]) ? +d[id] : null, x, returnData // use x as categories if custom x and categorized if ($$.isCustomX() && $$.isCategorized() && !isUndefined(rawX)) { if (index === 0 && i === 0) { config.axis_x_categories = [] } x = config.axis_x_categories.indexOf(rawX) if (x === -1) { x = config.axis_x_categories.length config.axis_x_categories.push(rawX) } } else { x = $$.generateTargetX(rawX, id, i) } // mark as x = undefined if value is undefined and filter to remove after mapped if (isUndefined(d[id]) || $$.data.xs[id].length <= i) { x = undefined } returnData = { x: x, value: value, id: convertedId } if ($$.isStanfordGraphType()) { returnData.epochs = d[epochs] } return returnData }) .filter(function(v) { return isDefined(v.x) }) } }) // finish targets targets.forEach(function(t) { var i // sort values by its x if (config.data_xSort) { t.values = t.values.sort(function(v1, v2) { var x1 = v1.x || v1.x === 0 ? v1.x : Infinity, x2 = v2.x || v2.x === 0 ? v2.x : Infinity return x1 - x2 }) } // indexing each value i = 0 t.values.forEach(function(v) { v.index = i++ }) // this needs to be sorted because its index and value.index is identical $$.data.xs[t.id].sort(function(v1, v2) { return v1 - v2 }) }) // cache information about values $$.hasNegativeValue = $$.hasNegativeValueInTargets(targets) $$.hasPositiveValue = $$.hasPositiveValueInTargets(targets) // set target types if (config.data_type) { $$.setTargetType( $$.mapToIds(targets).filter(function(id) { return !(id in config.data_types) }), config.data_type ) } // cache as original id keyed targets.forEach(function(d) { $$.addCache(d.id_org, d) }) return targets } ================================================ FILE: src/data.load.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' ChartInternal.prototype.load = function(targets, args) { var $$ = this if (targets) { // filter loading targets if needed if (args.filter) { targets = targets.filter(args.filter) } // set type if args.types || args.type specified if (args.type || args.types) { targets.forEach(function(t) { var type = args.types && args.types[t.id] ? args.types[t.id] : args.type $$.setTargetType(t.id, type) }) } // Update/Add data $$.data.targets.forEach(function(d) { for (var i = 0; i < targets.length; i++) { if (d.id === targets[i].id) { d.values = targets[i].values targets.splice(i, 1) break } } }) $$.data.targets = $$.data.targets.concat(targets) // add remained } // Set targets $$.updateTargets($$.data.targets) // Redraw with new targets $$.redraw({ withUpdateOrgXDomain: true, withUpdateXDomain: true, withLegend: true }) if (args.done) { args.done() } } ChartInternal.prototype.loadFromArgs = function(args) { var $$ = this $$.resetCache() if (args.data) { $$.load($$.convertDataToTargets(args.data), args) } else if (args.url) { $$.convertUrlToData( args.url, args.mimeType, args.headers, args.keys, function(data) { $$.load($$.convertDataToTargets(data), args) } ) } else if (args.json) { $$.load( $$.convertDataToTargets($$.convertJsonToData(args.json, args.keys)), args ) } else if (args.rows) { $$.load($$.convertDataToTargets($$.convertRowsToData(args.rows)), args) } else if (args.columns) { $$.load( $$.convertDataToTargets($$.convertColumnsToData(args.columns)), args ) } else { $$.load(null, args) } } ChartInternal.prototype.unload = function(targetIds, done) { var $$ = this $$.resetCache() if (!done) { done = function() {} } // filter existing target targetIds = targetIds.filter(function(id) { return $$.hasTarget($$.data.targets, id) }) // If no target, call done and return if (!targetIds || targetIds.length === 0) { done() return } $$.svg .selectAll( targetIds.map(function(id) { return $$.selectorTarget(id) }) ) .transition() .style('opacity', 0) .remove() .call($$.endall, done) targetIds.forEach(function(id) { // Reset fadein for future load $$.withoutFadeIn[id] = false // Remove target's elements if ($$.legend) { $$.legend .selectAll('.' + CLASS.legendItem + $$.getTargetSelectorSuffix(id)) .remove() } // Remove target $$.data.targets = $$.data.targets.filter(function(t) { return t.id !== id }) }) } ================================================ FILE: src/data.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue, isFunction, isNumber, isArray, notEmpty, hasValue, flattenArray, getBBox } from './util' ChartInternal.prototype.isEpochs = function(key) { var $$ = this, config = $$.config return config.data_epochs && key === config.data_epochs } ChartInternal.prototype.isX = function(key) { var $$ = this, config = $$.config return ( (config.data_x && key === config.data_x) || (notEmpty(config.data_xs) && hasValue(config.data_xs, key)) ) } ChartInternal.prototype.isNotX = function(key) { return !this.isX(key) } ChartInternal.prototype.isNotXAndNotEpochs = function(key) { return !this.isX(key) && !this.isEpochs(key) } /** * Returns whether the normalized stack option is enabled or not. * * To be enabled it must also have data.groups defined. * * @return {boolean} */ ChartInternal.prototype.isStackNormalized = function() { return this.config.data_stack_normalize && this.config.data_groups.length > 0 } /** * Returns whether the axis is normalized or not. * * An axis is normalized as long as one of its associated target * is normalized. * * @param axisId Axis ID (y or y2) * @return {Boolean} */ ChartInternal.prototype.isAxisNormalized = function(axisId) { const $$ = this if (!$$.isStackNormalized()) { // shortcut return false } return $$.data.targets .filter(target => $$.axis.getId(target.id) === axisId) .some(target => $$.isTargetNormalized(target.id)) } /** * Returns whether the values for this target ID is normalized or not. * * To be normalized the option needs to be enabled and target needs * to be defined in `data.groups`. * * @param targetId ID of the target * @return {Boolean} True if the target is normalized, false otherwise. */ ChartInternal.prototype.isTargetNormalized = function(targetId) { const $$ = this return ( $$.isStackNormalized() && $$.config.data_groups.some(group => group.includes(targetId)) ) } ChartInternal.prototype.getXKey = function(id) { var $$ = this, config = $$.config return config.data_x ? config.data_x : notEmpty(config.data_xs) ? config.data_xs[id] : null } /** * Get sum of visible data per index for given axis. * * Expect axisId to be either 'y' or 'y2'. * * @private * @param axisId Compute sum for data associated to given axis. * @return {Array} */ ChartInternal.prototype.getTotalPerIndex = function(axisId) { const $$ = this if (!$$.isStackNormalized()) { return null } const cached = $$.getFromCache('getTotalPerIndex') if (cached !== undefined) { return cached[axisId] } const sum = { y: [], y2: [] } $$.data.targets // keep only target that are normalized .filter(target => $$.isTargetNormalized(target.id)) // keep only target that are visible .filter(target => $$.isTargetToShow(target.id)) // compute sum per axis .forEach(target => { const sumByAxis = sum[$$.axis.getId(target.id)] target.values.forEach((v, i) => { if (!sumByAxis[i]) { sumByAxis[i] = 0 } sumByAxis[i] += isNumber(v.value) ? v.value : 0 }) }) $$.addToCache('getTotalPerIndex', sum) return sum[axisId] } /** * Get sum of visible data. * * Should be used for normalised data only since all values * are expected to be positive. * * @private * @return {Number} */ ChartInternal.prototype.getTotalDataSum = function() { const $$ = this const cached = $$.getFromCache('getTotalDataSum') if (cached !== undefined) { return cached } const totalDataSum = flattenArray( $$.data.targets .filter(target => $$.isTargetToShow(target.id)) .map(target => target.values) ) .map(d => d.value) .reduce((p, c) => p + c, 0) $$.addToCache('getTotalDataSum', totalDataSum) return totalDataSum } ChartInternal.prototype.getXValuesOfXKey = function(key, targets) { var $$ = this, xValues, ids = targets && notEmpty(targets) ? $$.mapToIds(targets) : [] ids.forEach(function(id) { if ($$.getXKey(id) === key) { xValues = $$.data.xs[id] } }) return xValues } ChartInternal.prototype.getXValue = function(id, i) { var $$ = this return id in $$.data.xs && $$.data.xs[id] && isValue($$.data.xs[id][i]) ? $$.data.xs[id][i] : i } ChartInternal.prototype.getOtherTargetXs = function() { var $$ = this, idsForX = Object.keys($$.data.xs) return idsForX.length ? $$.data.xs[idsForX[0]] : null } ChartInternal.prototype.getOtherTargetX = function(index) { var xs = this.getOtherTargetXs() return xs && index < xs.length ? xs[index] : null } ChartInternal.prototype.addXs = function(xs) { var $$ = this Object.keys(xs).forEach(function(id) { $$.config.data_xs[id] = xs[id] }) } ChartInternal.prototype.addName = function(data) { var $$ = this, name if (data) { name = $$.config.data_names[data.id] data.name = name !== undefined ? name : data.id } return data } ChartInternal.prototype.getValueOnIndex = function(values, index) { var valueOnIndex = values.filter(function(v) { return v.index === index }) return valueOnIndex.length ? valueOnIndex[0] : null } ChartInternal.prototype.updateTargetX = function(targets, x) { var $$ = this targets.forEach(function(t) { t.values.forEach(function(v, i) { v.x = $$.generateTargetX(x[i], t.id, i) }) $$.data.xs[t.id] = x }) } ChartInternal.prototype.updateTargetXs = function(targets, xs) { var $$ = this targets.forEach(function(t) { if (xs[t.id]) { $$.updateTargetX([t], xs[t.id]) } }) } ChartInternal.prototype.generateTargetX = function(rawX, id, index) { var $$ = this, x if ($$.isTimeSeries()) { x = rawX ? $$.parseDate(rawX) : $$.parseDate($$.getXValue(id, index)) } else if ($$.isCustomX() && !$$.isCategorized()) { x = isValue(rawX) ? +rawX : $$.getXValue(id, index) } else { x = index } return x } ChartInternal.prototype.cloneTarget = function(target) { return { id: target.id, id_org: target.id_org, values: target.values.map(function(d) { return { x: d.x, value: d.value, id: d.id } }) } } ChartInternal.prototype.getMaxDataCount = function() { var $$ = this return $$.d3.max($$.data.targets, function(t) { return t.values.length }) } ChartInternal.prototype.mapToIds = function(targets) { return targets.map(function(d) { return d.id }) } ChartInternal.prototype.mapToTargetIds = function(ids) { var $$ = this return ids ? [].concat(ids) : $$.mapToIds($$.data.targets) } ChartInternal.prototype.hasTarget = function(targets, id) { var ids = this.mapToIds(targets), i for (i = 0; i < ids.length; i++) { if (ids[i] === id) { return true } } return false } ChartInternal.prototype.isTargetToShow = function(targetId) { return this.hiddenTargetIds.indexOf(targetId) < 0 } ChartInternal.prototype.isLegendToShow = function(targetId) { return this.hiddenLegendIds.indexOf(targetId) < 0 } /** * Returns only visible targets. * * This is the same as calling {@link filterTargetsToShow} on $$.data.targets. * * @return {Array} */ ChartInternal.prototype.getTargetsToShow = function() { const $$ = this return $$.filterTargetsToShow($$.data.targets) } ChartInternal.prototype.filterTargetsToShow = function(targets) { var $$ = this return targets.filter(function(t) { return $$.isTargetToShow(t.id) }) } /** * @return {Array} Returns all the targets attached to the chart, visible or not */ ChartInternal.prototype.getTargets = function() { const $$ = this return $$.data.targets } ChartInternal.prototype.mapTargetsToUniqueXs = function(targets) { var $$ = this var xs = $$.d3 .set( $$.d3.merge( targets.map(function(t) { return t.values.map(function(v) { return +v.x }) }) ) ) .values() xs = $$.isTimeSeries() ? xs.map(function(x) { return new Date(+x) }) : xs.map(function(x) { return +x }) return xs.sort(function(a, b) { return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN }) } ChartInternal.prototype.addHiddenTargetIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds) for (var i = 0; i < targetIds.length; i++) { if (this.hiddenTargetIds.indexOf(targetIds[i]) < 0) { this.hiddenTargetIds = this.hiddenTargetIds.concat(targetIds[i]) } } this.resetCache() } ChartInternal.prototype.removeHiddenTargetIds = function(targetIds) { this.hiddenTargetIds = this.hiddenTargetIds.filter(function(id) { return targetIds.indexOf(id) < 0 }) this.resetCache() } ChartInternal.prototype.addHiddenLegendIds = function(targetIds) { targetIds = targetIds instanceof Array ? targetIds : new Array(targetIds) for (var i = 0; i < targetIds.length; i++) { if (this.hiddenLegendIds.indexOf(targetIds[i]) < 0) { this.hiddenLegendIds = this.hiddenLegendIds.concat(targetIds[i]) } } } ChartInternal.prototype.removeHiddenLegendIds = function(targetIds) { this.hiddenLegendIds = this.hiddenLegendIds.filter(function(id) { return targetIds.indexOf(id) < 0 }) } ChartInternal.prototype.getValuesAsIdKeyed = function(targets) { var ys = {} targets.forEach(function(t) { ys[t.id] = [] t.values.forEach(function(v) { ys[t.id].push(v.value) }) }) return ys } ChartInternal.prototype.checkValueInTargets = function(targets, checker) { var ids = Object.keys(targets), i, j, values for (i = 0; i < ids.length; i++) { values = targets[ids[i]].values for (j = 0; j < values.length; j++) { if (checker(values[j].value)) { return true } } } return false } ChartInternal.prototype.hasNegativeValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v < 0 }) } ChartInternal.prototype.hasPositiveValueInTargets = function(targets) { return this.checkValueInTargets(targets, function(v) { return v > 0 }) } ChartInternal.prototype.isOrderDesc = function() { var config = this.config return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'desc' ) } ChartInternal.prototype.isOrderAsc = function() { var config = this.config return ( typeof config.data_order === 'string' && config.data_order.toLowerCase() === 'asc' ) } ChartInternal.prototype.getOrderFunction = function() { var $$ = this, config = $$.config, orderAsc = $$.isOrderAsc(), orderDesc = $$.isOrderDesc() if (orderAsc || orderDesc) { var reducer = function(p, c) { return p + Math.abs(c.value) } return function(t1, t2) { var t1Sum = t1.values.reduce(reducer, 0), t2Sum = t2.values.reduce(reducer, 0) return orderAsc ? t2Sum - t1Sum : t1Sum - t2Sum } } else if (isFunction(config.data_order)) { return config.data_order } else if (isArray(config.data_order)) { var order = config.data_order return function(t1, t2) { return order.indexOf(t1.id) - order.indexOf(t2.id) } } } ChartInternal.prototype.orderTargets = function(targets) { var fct = this.getOrderFunction() if (fct) { targets.sort(fct) } return targets } /** * Returns all the values from the given targets at the given index. * * @param {Array} targets * @param {Number} index * @return {Array} */ ChartInternal.prototype.filterByIndex = function(targets, index) { return this.d3.merge( targets.map(t => t.values.filter(v => v.index === index)) ) } ChartInternal.prototype.filterByX = function(targets, x) { return this.d3 .merge( targets.map(function(t) { return t.values }) ) .filter(function(v) { return v.x - x === 0 }) } ChartInternal.prototype.filterRemoveNull = function(data) { return data.filter(function(d) { return isValue(d.value) }) } ChartInternal.prototype.filterByXDomain = function(targets, xDomain) { return targets.map(function(t) { return { id: t.id, id_org: t.id_org, values: t.values.filter(function(v) { return xDomain[0] <= v.x && v.x <= xDomain[1] }) } }) } ChartInternal.prototype.hasDataLabel = function() { var config = this.config if (typeof config.data_labels === 'boolean' && config.data_labels) { return true } else if ( typeof config.data_labels === 'object' && notEmpty(config.data_labels) ) { return true } return false } ChartInternal.prototype.getDataLabelLength = function(min, max, key) { var $$ = this, lengths = [0, 0], paddingCoef = 1.3 $$.selectChart .select('svg') .selectAll('.dummy') .data([min, max]) .enter() .append('text') .text(function(d) { return $$.dataLabelFormat(d.id)(d) }) .each(function(d, i) { lengths[i] = getBBox(this)[key] * paddingCoef }) .remove() return lengths } /** * Returns true if the given data point is not arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isNoneArc = function(d) { return this.hasTarget(this.data.targets, d.id) } /** * Returns true if the given data point is arc type, otherwise false. * @param {Object} d The data point * @return {boolean} */ ChartInternal.prototype.isArc = function(d) { return 'data' in d && this.hasTarget(this.data.targets, d.data.id) } /** * Find the closest point from the given pos among the given targets or * undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} pos An [x,y] coordinate * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargets = function(targets, pos) { const $$ = this // for each target, find the closest point const candidates = targets .map(t => $$.findClosest( t.values, pos, $$.config.tooltip_horizontal ? $$.horizontalDistance.bind($$) : $$.dist.bind($$), $$.config.point_sensitivity ) ) .filter(v => v) // returns the closest of candidates if (candidates.length === 0) { return undefined } else if (candidates.length === 1) { return candidates[0] } else { return $$.findClosest(candidates, pos, $$.dist.bind($$)) } } /** * Find the closest point from the x value or undefined if none satisfies conditions. * * @param {Array} targets * @param {Array} x A value on X axis * @return {Object|undefined} */ ChartInternal.prototype.findClosestFromTargetsByX = function(targets, x) { let closest let diff targets.forEach(t => { t.values.forEach(d => { let newDiff = Math.abs(x - d.x) if (diff === undefined || newDiff < diff) { closest = d diff = newDiff } }) }) return closest } /** * Using given compute distance method, returns the closest data point from the * given position. * * Giving optionally a minimum distance to satisfy. * * @param {Array} dataPoints List of DataPoints * @param {Array} pos An [x,y] coordinate * @param {Function} computeDist Function to compute distance between 2 points * @param {Number} minDist Minimal distance to satisfy * @return {Object|undefined} Closest data point */ ChartInternal.prototype.findClosest = function( dataPoints, pos, computeDist, minDist = Infinity ) { const $$ = this let closest // find closest bar dataPoints .filter(v => v && $$.isBarType(v.id)) .forEach(function(v) { if (!closest) { const shape = $$.main .select( '.' + CLASS.bars + $$.getTargetSelectorSuffix(v.id) + ' .' + CLASS.bar + '-' + v.index ) .node() if ($$.isWithinBar(pos, shape)) { closest = v } } }) // find closest point from non-bar dataPoints .filter(v => v && !$$.isBarType(v.id)) .forEach(v => { let d = computeDist(v, pos) if (d < minDist) { minDist = d closest = v } }) return closest } ChartInternal.prototype.dist = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, yIndex = config.axis_rotated ? 0 : 1, y = $$.circleY(data, data.index), x = $$.x(data.x) return Math.sqrt(Math.pow(x - pos[xIndex], 2) + Math.pow(y - pos[yIndex], 2)) } ChartInternal.prototype.horizontalDistance = function(data, pos) { var $$ = this, config = $$.config, xIndex = config.axis_rotated ? 1 : 0, x = $$.x(data.x) return Math.abs(x - pos[xIndex]) } ChartInternal.prototype.convertValuesToStep = function(values) { var converted = [].concat(values), i if (!this.isCategorized()) { return values } for (i = values.length + 1; 0 < i; i--) { converted[i] = converted[i - 1] } converted[0] = { x: converted[0].x - 1, value: converted[0].value, id: converted[0].id } converted[values.length + 1] = { x: converted[values.length].x + 1, value: converted[values.length].value, id: converted[values.length].id } return converted } /** * Get ratio value * * @param {String} type Ratio for given type * @param {Object} d Data value object * @param {Boolean} asPercent Convert the return as percent or not * @return {Number} Ratio value * @private */ ChartInternal.prototype.getRatio = function(type, d, asPercent = false) { const $$ = this const api = $$.api let ratio = 0 if (d && api.data.shown.call(api).length) { ratio = d.ratio || d.value if (type === 'arc') { if ($$.hasType('gauge')) { ratio = (d.endAngle - d.startAngle) / (Math.PI * ($$.config.gauge_fullCircle ? 2 : 1)) } else { const total = $$.getTotalDataSum() ratio = d.value / total } } else if (type === 'index') { const total = $$.getTotalPerIndex($$.axis.getId(d.id)) d.ratio = isNumber(d.value) && total && total[d.index] > 0 ? d.value / total[d.index] : 0 ratio = d.ratio } } return asPercent && ratio ? ratio * 100 : ratio } ChartInternal.prototype.updateDataAttributes = function(name, attrs) { var $$ = this, config = $$.config, current = config['data_' + name] if (typeof attrs === 'undefined') { return current } Object.keys(attrs).forEach(function(id) { current[id] = attrs[id] }) $$.redraw({ withLegend: true }) return current } ================================================ FILE: src/domain.ts ================================================ import { ChartInternal } from './core' import { isValue, isDefined, diffDomain, notEmpty } from './util' ChartInternal.prototype.getYDomainMin = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasNegativeValue if (config.data_groups.length > 0) { hasNegativeValue = $$.hasNegativeValueInTargets(targets) for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }) if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0] // Consider negative values if (hasNegativeValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v < 0 ? v : 0 }) } // Compute min for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k] if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasNegativeValue && +v > 0) ) { ys[baseId][i] += +v } }) } } } return $$.d3.min( Object.keys(ys).map(function(key) { return $$.d3.min(ys[key]) }) ) } ChartInternal.prototype.getYDomainMax = function(targets) { var $$ = this, config = $$.config, ids = $$.mapToIds(targets), ys = $$.getValuesAsIdKeyed(targets), j, k, baseId, idsInGroup, id, hasPositiveValue if (config.data_groups.length > 0) { hasPositiveValue = $$.hasPositiveValueInTargets(targets) for (j = 0; j < config.data_groups.length; j++) { // Determine baseId idsInGroup = config.data_groups[j].filter(function(id) { return ids.indexOf(id) >= 0 }) if (idsInGroup.length === 0) { continue } baseId = idsInGroup[0] // Consider positive values if (hasPositiveValue && ys[baseId]) { ys[baseId].forEach(function(v, i) { ys[baseId][i] = v > 0 ? v : 0 }) } // Compute max for (k = 1; k < idsInGroup.length; k++) { id = idsInGroup[k] if (!ys[id]) { continue } ys[id].forEach(function(v, i) { if ( $$.axis.getId(id) === $$.axis.getId(baseId) && ys[baseId] && !(hasPositiveValue && +v < 0) ) { ys[baseId][i] += +v } }) } } } return $$.d3.max( Object.keys(ys).map(function(key) { return $$.d3.max(ys[key]) }) ) } ChartInternal.prototype.getYDomain = function(targets, axisId, xDomain) { var $$ = this, config = $$.config if ($$.isAxisNormalized(axisId)) { return [0, 100] } var targetsByAxisId = targets.filter(function(t) { return $$.axis.getId(t.id) === axisId }), yTargets = xDomain ? $$.filterByXDomain(targetsByAxisId, xDomain) : targetsByAxisId, yMin = axisId === 'y2' ? config.axis_y2_min : config.axis_y_min, yMax = axisId === 'y2' ? config.axis_y2_max : config.axis_y_max, yDomainMin = $$.getYDomainMin(yTargets), yDomainMax = $$.getYDomainMax(yTargets), domain, domainLength, padding_top, padding_bottom, center = axisId === 'y2' ? config.axis_y2_center : config.axis_y_center, yDomainAbs, lengths, diff, ratio, isAllPositive, isAllNegative, isZeroBased = ($$.hasType('bar', yTargets) && config.bar_zerobased) || ($$.hasType('area', yTargets) && config.area_zerobased), isInverted = axisId === 'y2' ? config.axis_y2_inverted : config.axis_y_inverted, showHorizontalDataLabel = $$.hasDataLabel() && config.axis_rotated, showVerticalDataLabel = $$.hasDataLabel() && !config.axis_rotated // MEMO: avoid inverting domain unexpectedly yDomainMin = isValue(yMin) ? yMin : isValue(yMax) ? yDomainMin < yMax ? yDomainMin : yMax - 10 : yDomainMin yDomainMax = isValue(yMax) ? yMax : isValue(yMin) ? yMin < yDomainMax ? yDomainMax : yMin + 10 : yDomainMax if (yTargets.length === 0) { // use current domain if target of axisId is none return axisId === 'y2' ? $$.y2.domain() : $$.y.domain() } if (isNaN(yDomainMin)) { // set minimum to zero when not number yDomainMin = 0 } if (isNaN(yDomainMax)) { // set maximum to have same value as yDomainMin yDomainMax = yDomainMin } if (yDomainMin === yDomainMax) { yDomainMin < 0 ? (yDomainMax = 0) : (yDomainMin = 0) } isAllPositive = yDomainMin >= 0 && yDomainMax >= 0 isAllNegative = yDomainMin <= 0 && yDomainMax <= 0 // Cancel zerobased if axis_*_min / axis_*_max specified if ((isValue(yMin) && isAllPositive) || (isValue(yMax) && isAllNegative)) { isZeroBased = false } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { yDomainMin = 0 } if (isAllNegative) { yDomainMax = 0 } } domainLength = Math.abs(yDomainMax - yDomainMin) padding_top = padding_bottom = domainLength * 0.1 if (typeof center !== 'undefined') { yDomainAbs = Math.max(Math.abs(yDomainMin), Math.abs(yDomainMax)) yDomainMax = center + yDomainAbs yDomainMin = center - yDomainAbs } // add padding for data label if (showHorizontalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'width') diff = diffDomain($$.y.range()) ratio = [lengths[0] / diff, lengths[1] / diff] padding_top += domainLength * (ratio[1] / (1 - ratio[0] - ratio[1])) padding_bottom += domainLength * (ratio[0] / (1 - ratio[0] - ratio[1])) } else if (showVerticalDataLabel) { lengths = $$.getDataLabelLength(yDomainMin, yDomainMax, 'height') const pixelsToAxisPadding = $$.getY( config[`axis_${axisId}_type`], // input domain as pixels [0, config.axis_rotated ? $$.width : $$.height], // output range as axis padding [0, domainLength] ) padding_top += pixelsToAxisPadding(lengths[1]) padding_bottom += pixelsToAxisPadding(lengths[0]) } if (axisId === 'y' && notEmpty(config.axis_y_padding)) { padding_top = $$.axis.getPadding( config.axis_y_padding, 'top', padding_top, domainLength ) padding_bottom = $$.axis.getPadding( config.axis_y_padding, 'bottom', padding_bottom, domainLength ) } if (axisId === 'y2' && notEmpty(config.axis_y2_padding)) { padding_top = $$.axis.getPadding( config.axis_y2_padding, 'top', padding_top, domainLength ) padding_bottom = $$.axis.getPadding( config.axis_y2_padding, 'bottom', padding_bottom, domainLength ) } // Bar/Area chart should be 0-based if all positive|negative if (isZeroBased) { if (isAllPositive) { padding_bottom = yDomainMin } if (isAllNegative) { padding_top = -yDomainMax } } domain = [yDomainMin - padding_bottom, yDomainMax + padding_top] return isInverted ? domain.reverse() : domain } ChartInternal.prototype.getXDomainMin = function(targets) { var $$ = this, config = $$.config return isDefined(config.axis_x_min) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_min) : config.axis_x_min : $$.d3.min(targets, function(t) { return $$.d3.min(t.values, function(v) { return v.x }) }) } ChartInternal.prototype.getXDomainMax = function(targets) { var $$ = this, config = $$.config return isDefined(config.axis_x_max) ? $$.isTimeSeries() ? this.parseDate(config.axis_x_max) : config.axis_x_max : $$.d3.max(targets, function(t) { return $$.d3.max(t.values, function(v) { return v.x }) }) } ChartInternal.prototype.getXDomainPadding = function(domain) { var $$ = this, config = $$.config, diff = domain[1] - domain[0], maxDataCount, padding, paddingLeft, paddingRight if ($$.isCategorized()) { padding = 0 } else if ($$.hasType('bar')) { maxDataCount = $$.getMaxDataCount() padding = maxDataCount > 1 ? diff / (maxDataCount - 1) / 2 : 0.5 } else { padding = diff * 0.01 } if ( typeof config.axis_x_padding === 'object' && notEmpty(config.axis_x_padding) ) { paddingLeft = isValue(config.axis_x_padding.left) ? config.axis_x_padding.left : padding paddingRight = isValue(config.axis_x_padding.right) ? config.axis_x_padding.right : padding } else if (typeof config.axis_x_padding === 'number') { paddingLeft = paddingRight = config.axis_x_padding } else { paddingLeft = paddingRight = padding } return { left: paddingLeft, right: paddingRight } } ChartInternal.prototype.getXDomain = function(targets) { var $$ = this, xDomain = [$$.getXDomainMin(targets), $$.getXDomainMax(targets)], firstX = xDomain[0], lastX = xDomain[1], padding = $$.getXDomainPadding(xDomain), min: Date | number = 0, max: Date | number = 0 // show center of x domain if min and max are the same if (firstX - lastX === 0 && !$$.isCategorized()) { if ($$.isTimeSeries()) { firstX = new Date(firstX.getTime() * 0.5) lastX = new Date(lastX.getTime() * 1.5) } else { firstX = firstX === 0 ? 1 : firstX * 0.5 lastX = lastX === 0 ? -1 : lastX * 1.5 } } if (firstX || firstX === 0) { min = $$.isTimeSeries() ? new Date(firstX.getTime() - padding.left) : firstX - padding.left } if (lastX || lastX === 0) { max = $$.isTimeSeries() ? new Date(lastX.getTime() + padding.right) : lastX + padding.right } return [min, max] } ChartInternal.prototype.updateXDomain = function( targets, withUpdateXDomain, withUpdateOrgXDomain, withTrim, domain ) { var $$ = this, config = $$.config if (withUpdateOrgXDomain) { $$.x.domain(domain ? domain : $$.d3.extent($$.getXDomain(targets))) $$.orgXDomain = $$.x.domain() if (config.zoom_enabled) { $$.zoom.update() } $$.subX.domain($$.x.domain()) if ($$.brush) { $$.brush.updateScale($$.subX) } } if (withUpdateXDomain) { $$.x.domain( domain ? domain : !$$.brush || $$.brush.empty() ? $$.orgXDomain : $$.brush.selectionAsValue() ) } // Trim domain when too big by zoom mousemove event if (withTrim) { $$.x.domain($$.trimXDomain($$.x.orgDomain())) } return $$.x.domain() } ChartInternal.prototype.trimXDomain = function(domain) { var zoomDomain = this.getZoomDomain(), min = zoomDomain[0], max = zoomDomain[1] if (domain[0] <= min) { domain[1] = +domain[1] + (min - domain[0]) domain[0] = min } if (max <= domain[1]) { domain[0] = +domain[0] - (domain[1] - max) domain[1] = max } return domain } ================================================ FILE: src/drag.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { getPathBox } from './util' ChartInternal.prototype.drag = function(mouse) { var $$ = this, config = $$.config, main = $$.main, d3 = $$.d3 var sx, sy, mx, my, minX, maxX, minY, maxY if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable if (!config.data_selection_multiple) { return } // skip when single selection because drag is used for multiple selection sx = $$.dragStart[0] sy = $$.dragStart[1] mx = mouse[0] my = mouse[1] minX = Math.min(sx, mx) maxX = Math.max(sx, mx) minY = config.data_selection_grouped ? $$.margin.top : Math.min(sy, my) maxY = config.data_selection_grouped ? $$.height : Math.max(sy, my) main .select('.' + CLASS.dragarea) .attr('x', minX) .attr('y', minY) .attr('width', maxX - minX) .attr('height', maxY - minY) // TODO: binary search when multiple xs main .selectAll('.' + CLASS.shapes) .selectAll('.' + CLASS.shape) .each(function(d, i) { if (!config.data_selection_isselectable(d)) { return } var shape = d3.select(this), isSelected = shape.classed(CLASS.SELECTED), isIncluded = shape.classed(CLASS.INCLUDED), _x, _y, _w, _h, toggle, isWithin = false, box if (shape.classed(CLASS.circle)) { _x = shape.attr('cx') * 1 _y = shape.attr('cy') * 1 toggle = $$.togglePoint isWithin = minX < _x && _x < maxX && minY < _y && _y < maxY } else if (shape.classed(CLASS.bar)) { box = getPathBox(this) _x = box.x _y = box.y _w = box.width _h = box.height toggle = $$.togglePath isWithin = !(maxX < _x || _x + _w < minX) && !(maxY < _y || _y + _h < minY) } else { // line/area selection not supported yet return } if ((isWithin as any) ^ isIncluded) { shape.classed(CLASS.INCLUDED, !isIncluded) // TODO: included/unincluded callback here shape.classed(CLASS.SELECTED, !isSelected) toggle.call($$, !isSelected, shape, d, i) } }) } ChartInternal.prototype.dragstart = function(mouse) { var $$ = this, config = $$.config if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.dragStart = mouse $$.main .select('.' + CLASS.chart) .append('rect') .attr('class', CLASS.dragarea) .style('opacity', 0.1) $$.dragging = true } ChartInternal.prototype.dragend = function() { var $$ = this, config = $$.config if ($$.hasArcType()) { return } if (!config.data_selection_enabled) { return } // do nothing if not selectable $$.main .select('.' + CLASS.dragarea) .transition() .duration(100) .style('opacity', 0) .remove() $$.main.selectAll('.' + CLASS.shape).classed(CLASS.INCLUDED, false) $$.dragging = false } ================================================ FILE: src/format.ts ================================================ import { ChartInternal } from './core' import { isValue } from './util' ChartInternal.prototype.getYFormat = function(forArc) { var $$ = this, formatForY = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.yFormat, formatForY2 = forArc && !$$.hasType('gauge') ? $$.defaultArcValueFormat : $$.y2Format return function(v, ratio, id) { var format = $$.axis.getId(id) === 'y2' ? formatForY2 : formatForY return format.call($$, v, ratio) } } ChartInternal.prototype.yFormat = function(v) { var $$ = this, config = $$.config, format = config.axis_y_tick_format ? config.axis_y_tick_format : $$.defaultValueFormat return format(v) } ChartInternal.prototype.y2Format = function(v) { var $$ = this, config = $$.config, format = config.axis_y2_tick_format ? config.axis_y2_tick_format : $$.defaultValueFormat return format(v) } ChartInternal.prototype.defaultValueFormat = function(v) { return isValue(v) ? +v : '' } ChartInternal.prototype.defaultArcValueFormat = function(v, ratio) { return (ratio * 100).toFixed(1) + '%' } ChartInternal.prototype.dataLabelFormat = function(targetId) { var $$ = this, data_labels = $$.config.data_labels, format, defaultFormat = function(v) { return isValue(v) ? +v : '' } // find format according to axis id if (typeof data_labels.format === 'function') { format = data_labels.format } else if (typeof data_labels.format === 'object') { if (data_labels.format[targetId]) { format = data_labels.format[targetId] === true ? defaultFormat : data_labels.format[targetId] } else { format = function() { return '' } } } else { format = defaultFormat } return format } ================================================ FILE: src/grid.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue } from './util' ChartInternal.prototype.initGrid = function() { var $$ = this, config = $$.config, d3 = $$.d3 $$.grid = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid) if (config.grid_x_show) { $$.grid.append('g').attr('class', CLASS.xgrids) } if (config.grid_y_show) { $$.grid.append('g').attr('class', CLASS.ygrids) } if (config.grid_focus_show) { $$.grid .append('g') .attr('class', CLASS.xgridFocus) .append('line') .attr('class', CLASS.xgridFocus) } $$.xgrid = d3.selectAll([]) if (!config.grid_lines_front) { $$.initGridLines() } } ChartInternal.prototype.initGridLines = function() { var $$ = this, d3 = $$.d3 $$.gridLines = $$.main .append('g') .attr('clip-path', $$.clipPathForGrid) .attr('class', CLASS.grid + ' ' + CLASS.gridLines) $$.gridLines.append('g').attr('class', CLASS.xgridLines) $$.gridLines.append('g').attr('class', CLASS.ygridLines) $$.xgridLines = d3.selectAll([]) } ChartInternal.prototype.updateXGrid = function(withoutUpdate) { var $$ = this, config = $$.config, d3 = $$.d3, xgridData = $$.generateGridData(config.grid_x_type, $$.x), tickOffset = $$.isCategorized() ? $$.xAxis.tickOffset() : 0 $$.xgridAttr = config.axis_rotated ? { x1: 0, x2: $$.width, y1: function(d) { return $$.x(d) - tickOffset }, y2: function(d) { return $$.x(d) - tickOffset } } : { x1: function(d) { return $$.x(d) + tickOffset }, x2: function(d) { return $$.x(d) + tickOffset }, y1: 0, y2: $$.height } $$.xgridAttr.opacity = function() { var pos = +d3.select(this).attr(config.axis_rotated ? 'y1' : 'x1') return pos === (config.axis_rotated ? $$.height : 0) ? 0 : 1 } var xgrid = $$.main .select('.' + CLASS.xgrids) .selectAll('.' + CLASS.xgrid) .data(xgridData) var xgridEnter = xgrid .enter() .append('line') .attr('class', CLASS.xgrid) .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', 0) $$.xgrid = xgridEnter.merge(xgrid) if (!withoutUpdate) { $$.xgrid .attr('x1', $$.xgridAttr.x1) .attr('x2', $$.xgridAttr.x2) .attr('y1', $$.xgridAttr.y1) .attr('y2', $$.xgridAttr.y2) .style('opacity', $$.xgridAttr.opacity) } xgrid.exit().remove() } ChartInternal.prototype.updateYGrid = function() { var $$ = this, config = $$.config, gridValues = $$.yAxis.tickValues() || $$.y.ticks(config.grid_y_ticks) var ygrid = $$.main .select('.' + CLASS.ygrids) .selectAll('.' + CLASS.ygrid) .data(gridValues) var ygridEnter = ygrid .enter() .append('line') // TODO: x1, x2, y1, y2, opacity need to be set here maybe .attr('class', CLASS.ygrid) $$.ygrid = ygridEnter.merge(ygrid) $$.ygrid .attr('x1', config.axis_rotated ? $$.y : 0) .attr('x2', config.axis_rotated ? $$.y : $$.width) .attr('y1', config.axis_rotated ? 0 : $$.y) .attr('y2', config.axis_rotated ? $$.height : $$.y) ygrid.exit().remove() $$.smoothLines($$.ygrid, 'grid') } ChartInternal.prototype.gridTextAnchor = function(d) { return d.position ? d.position : 'end' } ChartInternal.prototype.gridTextDx = function(d) { return d.position === 'start' ? 4 : d.position === 'middle' ? 0 : -4 } ChartInternal.prototype.xGridTextX = function(d) { return d.position === 'start' ? -this.height : d.position === 'middle' ? -this.height / 2 : 0 } ChartInternal.prototype.yGridTextX = function(d) { return d.position === 'start' ? 0 : d.position === 'middle' ? this.width / 2 : this.width } ChartInternal.prototype.updateGrid = function(duration) { var $$ = this, main = $$.main, config = $$.config, xgridLine, xgridLineEnter, ygridLine, ygridLineEnter, xv = $$.xv.bind($$), yv = $$.yv.bind($$), xGridTextX = $$.xGridTextX.bind($$), yGridTextX = $$.yGridTextX.bind($$) // hide if arc type $$.grid.style('visibility', $$.hasArcType() ? 'hidden' : 'visible') main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden') if (config.grid_x_show) { $$.updateXGrid() } xgridLine = main .select('.' + CLASS.xgridLines) .selectAll('.' + CLASS.xgridLine) .data(config.grid_x_lines) // enter xgridLineEnter = xgridLine .enter() .append('g') .attr('class', function(d) { return CLASS.xgridLine + (d['class'] ? ' ' + d['class'] : '') }) xgridLineEnter .append('line') .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 0) xgridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? '' : 'rotate(-90)') .attr('x', config.axis_rotated ? yGridTextX : xGridTextX) .attr('y', xv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0) // udpate $$.xgridLines = xgridLineEnter.merge(xgridLine) // done in d3.transition() of the end of this function // exit xgridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove() // Y-Grid if (config.grid_y_show) { $$.updateYGrid() } ygridLine = main .select('.' + CLASS.ygridLines) .selectAll('.' + CLASS.ygridLine) .data(config.grid_y_lines) // enter ygridLineEnter = ygridLine .enter() .append('g') .attr('class', function(d) { return CLASS.ygridLine + (d['class'] ? ' ' + d['class'] : '') }) ygridLineEnter .append('line') .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 0) ygridLineEnter .append('text') .attr('text-anchor', $$.gridTextAnchor) .attr('transform', config.axis_rotated ? 'rotate(-90)' : '') .attr('x', config.axis_rotated ? xGridTextX : yGridTextX) .attr('y', yv) .attr('dx', $$.gridTextDx) .attr('dy', -5) .style('opacity', 0) // update $$.ygridLines = ygridLineEnter.merge(ygridLine) $$.ygridLines .select('line') .transition() .duration(duration) .attr('x1', config.axis_rotated ? yv : 0) .attr('x2', config.axis_rotated ? yv : $$.width) .attr('y1', config.axis_rotated ? 0 : yv) .attr('y2', config.axis_rotated ? $$.height : yv) .style('opacity', 1) $$.ygridLines .select('text') .transition() .duration(duration) .attr( 'x', config.axis_rotated ? $$.xGridTextX.bind($$) : $$.yGridTextX.bind($$) ) .attr('y', yv) .text(function(d) { return d.text }) .style('opacity', 1) // exit ygridLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove() } ChartInternal.prototype.redrawGrid = function(withTransition, transition) { var $$ = this, config = $$.config, xv = $$.xv.bind($$), lines = $$.xgridLines.select('line'), texts = $$.xgridLines.select('text') return [ (withTransition ? lines.transition(transition) : lines) .attr('x1', config.axis_rotated ? 0 : xv) .attr('x2', config.axis_rotated ? $$.width : xv) .attr('y1', config.axis_rotated ? xv : 0) .attr('y2', config.axis_rotated ? xv : $$.height) .style('opacity', 1), (withTransition ? texts.transition(transition) : texts) .attr( 'x', config.axis_rotated ? $$.yGridTextX.bind($$) : $$.xGridTextX.bind($$) ) .attr('y', xv) .text(function(d) { return d.text }) .style('opacity', 1) ] } ChartInternal.prototype.showXGridFocus = function(selectedData) { var $$ = this, config = $$.config, dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), focusEl = $$.main.selectAll('line.' + CLASS.xgridFocus), xx = $$.xx.bind($$) if (!config.tooltip_show) { return } // Hide when stanford plot exists if ($$.hasType('stanford') || $$.hasArcType()) { return } focusEl .style('visibility', 'visible') .data([dataToShow[0]]) .attr(config.axis_rotated ? 'y1' : 'x1', xx) .attr(config.axis_rotated ? 'y2' : 'x2', xx) $$.smoothLines(focusEl, 'grid') } ChartInternal.prototype.hideXGridFocus = function() { this.main.select('line.' + CLASS.xgridFocus).style('visibility', 'hidden') } ChartInternal.prototype.updateXgridFocus = function() { var $$ = this, config = $$.config $$.main .select('line.' + CLASS.xgridFocus) .attr('x1', config.axis_rotated ? 0 : -10) .attr('x2', config.axis_rotated ? $$.width : -10) .attr('y1', config.axis_rotated ? -10 : 0) .attr('y2', config.axis_rotated ? -10 : $$.height) } ChartInternal.prototype.generateGridData = function(type, scale) { var $$ = this, gridData = [], xDomain, firstYear, lastYear, i, tickNum = $$.main .select('.' + CLASS.axisX) .selectAll('.tick') .size() if (type === 'year') { xDomain = $$.getXDomain() firstYear = xDomain[0].getFullYear() lastYear = xDomain[1].getFullYear() for (i = firstYear; i <= lastYear; i++) { gridData.push(new Date(i + '-01-01 00:00:00')) } } else { gridData = scale.ticks(10) if (gridData.length > tickNum) { // use only int gridData = gridData.filter(function(d) { return ('' + d).indexOf('.') < 0 }) } } return gridData } ChartInternal.prototype.getGridFilterToRemove = function(params) { return params ? function(line) { var found = false ;[].concat(params).forEach(function(param) { if ( ('value' in param && line.value === param.value) || ('class' in param && line['class'] === param['class']) ) { found = true } }) return found } : function() { return true } } ChartInternal.prototype.removeGridLines = function(params, forX) { var $$ = this, config = $$.config, toRemove = $$.getGridFilterToRemove(params), toShow = function(line) { return !toRemove(line) }, classLines = forX ? CLASS.xgridLines : CLASS.ygridLines, classLine = forX ? CLASS.xgridLine : CLASS.ygridLine $$.main .select('.' + classLines) .selectAll('.' + classLine) .filter(toRemove) .transition() .duration(config.transition_duration) .style('opacity', 0) .remove() if (forX) { config.grid_x_lines = config.grid_x_lines.filter(toShow) } else { config.grid_y_lines = config.grid_y_lines.filter(toShow) } } ================================================ FILE: src/index.ts ================================================ import { c3 } from './core' import './polyfill' import './api.axis' import './api.category' import './api.chart' import './api.color' import './api.data' import './api.donut' import './api.flow' import './api.focus' import './api.grid' import './api.group' import './api.legend' import './api.load' import './api.pie' import './api.region' import './api.selection' import './api.show' import './api.subchart' import './api.tooltip' import './api.transform' import './api.x' import './api.zoom' import './arc' import './axis' import './cache' import './category' import './class' import './class-utils' import './clip' import './color' import './config' import './data.convert' import './data' import './data.load' import './domain' import './drag' import './format' import './grid' import './interaction' import './legend' import './region' import './scale' import './selection' import './shape.bar' import './shape' import './shape.line' import './size' import './subchart' import './text' import './title' import './colorscale' import './stanford' import './stanfordelements' import './tooltip' import './type' import './ua' import './util' import './zoom' export default c3 ================================================ FILE: src/interaction.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' ChartInternal.prototype.initEventRect = function() { var $$ = this, config = $$.config $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0) $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect) // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null) if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)) } } } ChartInternal.prototype.redrawEventRect = function() { const $$ = this, d3 = $$.d3, config = $$.config function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null) $$.hideXGridFocus() $$.hideTooltip() $$.unexpandCircles() $$.unexpandBars() } const isHoveringDataPoint = (mouse, closest) => closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) const withName = d => (d ? $$.addName(Object.assign({}, d)) : null) // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style( 'cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null ) $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on( 'mouseout', config.interaction_enabled ? function() { if (!config) { return } // chart is destroyed if ($$.hasArcType()) { return } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover) $$.mouseover = undefined } mouseout() } : null ) .on( 'mousemove', config.interaction_enabled ? function() { // do nothing when dragging if ($$.dragging) { return } const targetsToShow = $$.getTargetsToShow() // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this) const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ) const isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest) // ensure onmouseout is always called if mousemove switch between 2 targets if ( $$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index) ) { config.data_onmouseout.call($$.api, $$.mouseover) $$.mouseover = undefined } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest) $$.mouseover = closest } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null) // if tooltip not grouped, we want to display only data from closest data point const showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow) // find data to highlight let selectedData if (showSingleDataPoint) { if (closest) { selectedData = [closest] } } else { let closestByX if (closest) { // reuse closest value closestByX = closest } else { // try to find the closest value by X values from the mouse position const mouseX = config.axis_rotated ? mouse[1] : mouse[0] closestByX = $$.findClosestFromTargetsByX( targetsToShow, $$.x.invert(mouseX) ) } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x) } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout() } // inject names for each point selectedData = selectedData.map(withName) // show tooltip $$.showTooltip(selectedData, this) // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles() selectedData.forEach(function(d) { $$.expandCircles(d.index, d.id, false) }) } // expand bars $$.unexpandBars() selectedData.forEach(function(d) { $$.expandBars(d.index, d.id, false) }) // Show xgrid focus line $$.showXGridFocus(selectedData) } : null ) .on( 'click', config.interaction_enabled ? function() { const targetsToShow = $$.getTargetsToShow() if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this) const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ) if (!isHoveringDataPoint(mouse, closest)) { return } // select if selection enabled let sameXData if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest] } else { sameXData = $$.filterByX(targetsToShow, closest.x) } // toggle selected state sameXData.forEach(function(d) { $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id) ) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function() { if ( config.data_selection_grouped || $$.isWithinShape(this, d) ) { $$.toggleShape(this, d, d.index) } }) }) // call data_onclick on the closest data point if (closest) { const shape = $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id) ) .select('.' + CLASS.shape + '-' + closest.index) config.data_onclick.call($$.api, closest, shape.node()) } } : null ) .call( config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function() { $$.drag(d3.mouse(this)) }) .on('start', function() { $$.dragstart(d3.mouse(this)) }) .on('end', function() { $$.dragend() }) : function() {} ) } ChartInternal.prototype.getMousePosition = function(data) { var $$ = this return [$$.x(data.x), $$.getYScale(data.id)(data.value)] } ChartInternal.prototype.dispatchEvent = function(type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents') event.initMouseEvent( type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null ) eventRect.dispatchEvent(event) } ================================================ FILE: src/legend.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isDefined, isEmpty, getOption } from './util' ChartInternal.prototype.initLegend = function() { var $$ = this $$.legendItemTextBox = {} $$.legendHasRendered = false $$.legend = $$.svg.append('g').attr('transform', $$.getTranslate('legend')) if (!$$.config.legend_show) { $$.legend.style('visibility', 'hidden') $$.hiddenLegendIds = $$.mapToIds($$.data.targets) return } // MEMO: call here to update legend box and tranlate for all // MEMO: translate will be updated by this, so transform not needed in updateLegend() $$.updateLegendWithDefaults() } ChartInternal.prototype.updateLegendWithDefaults = function() { var $$ = this $$.updateLegend($$.mapToIds($$.data.targets), { withTransform: false, withTransitionForTransform: false, withTransition: false }) } ChartInternal.prototype.updateSizeForLegend = function( legendHeight, legendWidth ) { var $$ = this, config = $$.config, insetLegendPosition = { top: $$.isLegendTop ? $$.getCurrentPaddingTop() + config.legend_inset_y + 5.5 : $$.currentHeight - legendHeight - $$.getCurrentPaddingBottom() - config.legend_inset_y, left: $$.isLegendLeft ? $$.getCurrentPaddingLeft() + config.legend_inset_x + 0.5 : $$.currentWidth - legendWidth - $$.getCurrentPaddingRight() - config.legend_inset_x + 0.5 } $$.margin3 = { top: $$.isLegendRight ? 0 : $$.isLegendInset ? insetLegendPosition.top : $$.currentHeight - legendHeight, right: NaN, bottom: 0, left: $$.isLegendRight ? $$.currentWidth - legendWidth : $$.isLegendInset ? insetLegendPosition.left : 0 } } ChartInternal.prototype.transformLegend = function(withTransition) { var $$ = this ;(withTransition ? $$.legend.transition() : $$.legend).attr( 'transform', $$.getTranslate('legend') ) } ChartInternal.prototype.updateLegendStep = function(step) { this.legendStep = step } ChartInternal.prototype.updateLegendItemWidth = function(w) { this.legendItemWidth = w } ChartInternal.prototype.updateLegendItemHeight = function(h) { this.legendItemHeight = h } ChartInternal.prototype.getLegendWidth = function() { var $$ = this return $$.config.legend_show ? $$.isLegendRight || $$.isLegendInset ? $$.legendItemWidth * ($$.legendStep + 1) : $$.currentWidth : 0 } ChartInternal.prototype.getLegendHeight = function() { var $$ = this, h = 0 if ($$.config.legend_show) { if ($$.isLegendRight) { h = $$.currentHeight } else { h = Math.max(20, $$.legendItemHeight) * ($$.legendStep + 1) } } return h } ChartInternal.prototype.opacityForLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 1 } ChartInternal.prototype.opacityForUnfocusedLegend = function(legendItem) { return legendItem.classed(CLASS.legendItemHidden) ? null : 0.3 } ChartInternal.prototype.toggleFocusLegend = function(targetIds, focus) { var $$ = this targetIds = $$.mapToTargetIds(targetIds) $$.legend .selectAll('.' + CLASS.legendItem) .filter(function(id) { return targetIds.indexOf(id) >= 0 }) .classed(CLASS.legendItemFocused, focus) .transition() .duration(100) .style('opacity', function() { var opacity = focus ? $$.opacityForLegend : $$.opacityForUnfocusedLegend return opacity.call($$, $$.d3.select(this)) }) } ChartInternal.prototype.revertLegend = function() { var $$ = this, d3 = $$.d3 $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemFocused, false) .transition() .duration(100) .style('opacity', function() { return $$.opacityForLegend(d3.select(this)) }) } ChartInternal.prototype.showLegend = function(targetIds) { var $$ = this, config = $$.config if (!config.legend_show) { config.legend_show = true $$.legend.style('visibility', 'visible') if (!$$.legendHasRendered) { $$.updateLegendWithDefaults() } } $$.removeHiddenLegendIds(targetIds) $$.legend .selectAll($$.selectorLegends(targetIds)) .style('visibility', 'visible') .transition() .style('opacity', function() { return $$.opacityForLegend($$.d3.select(this)) }) } ChartInternal.prototype.hideLegend = function(targetIds) { var $$ = this, config = $$.config if (config.legend_show && isEmpty(targetIds)) { config.legend_show = false $$.legend.style('visibility', 'hidden') } $$.addHiddenLegendIds(targetIds) $$.legend .selectAll($$.selectorLegends(targetIds)) .style('opacity', 0) .style('visibility', 'hidden') } ChartInternal.prototype.clearLegendItemTextBoxCache = function() { this.legendItemTextBox = {} } ChartInternal.prototype.updateLegend = function( targetIds, options, transitions ) { var $$ = this, config = $$.config var xForLegend, xForLegendText, xForLegendRect, yForLegend, yForLegendText, yForLegendRect, x1ForLegendTile, x2ForLegendTile, yForLegendTile var paddingTop = 4, paddingRight = 10, maxWidth = 0, maxHeight = 0, posMin = 10, tileWidth = config.legend_item_tile_width + 5 var l, totalLength = 0, offsets = {}, widths = {}, heights = {}, margins = [0], steps = {}, step = 0 var withTransition, withTransitionForTransform var texts, rects, tiles, background // Skip elements when their name is set to null targetIds = targetIds.filter(function(id) { return !isDefined(config.data_names[id]) || config.data_names[id] !== null }) options = options || {} withTransition = getOption(options, 'withTransition', true) withTransitionForTransform = getOption( options, 'withTransitionForTransform', true ) function getTextBox(textElement, id) { if (!$$.legendItemTextBox[id]) { $$.legendItemTextBox[id] = $$.getTextRect( textElement.textContent, CLASS.legendItem, textElement ) } return $$.legendItemTextBox[id] } function updatePositions(textElement, id, index) { var reset = index === 0, isLast = index === targetIds.length - 1, box = getTextBox(textElement, id), itemWidth = box.width + tileWidth + (isLast && !($$.isLegendRight || $$.isLegendInset) ? 0 : paddingRight) + config.legend_padding, itemHeight = box.height + paddingTop, itemLength = $$.isLegendRight || $$.isLegendInset ? itemHeight : itemWidth, areaLength = $$.isLegendRight || $$.isLegendInset ? $$.getLegendHeight() : $$.getLegendWidth(), margin, maxLength // MEMO: care about condifion of step, totalLength function updateValues(id, withoutStep?: boolean) { if (!withoutStep) { margin = (areaLength - totalLength - itemLength) / 2 if (margin < posMin) { margin = (areaLength - itemLength) / 2 totalLength = 0 step++ } } steps[id] = step margins[step] = $$.isLegendInset ? 10 : margin offsets[id] = totalLength totalLength += itemLength } if (reset) { totalLength = 0 step = 0 maxWidth = 0 maxHeight = 0 } if (config.legend_show && !$$.isLegendToShow(id)) { widths[id] = heights[id] = steps[id] = offsets[id] = 0 return } widths[id] = itemWidth heights[id] = itemHeight if (!maxWidth || itemWidth >= maxWidth) { maxWidth = itemWidth } if (!maxHeight || itemHeight >= maxHeight) { maxHeight = itemHeight } maxLength = $$.isLegendRight || $$.isLegendInset ? maxHeight : maxWidth if (config.legend_equally) { Object.keys(widths).forEach(function(id) { widths[id] = maxWidth }) Object.keys(heights).forEach(function(id) { heights[id] = maxHeight }) margin = (areaLength - maxLength * targetIds.length) / 2 if (margin < posMin) { totalLength = 0 step = 0 targetIds.forEach(function(id) { updateValues(id) }) } else { updateValues(id, true) } } else { updateValues(id) } } if ($$.isLegendInset) { step = config.legend_inset_step ? config.legend_inset_step : targetIds.length $$.updateLegendStep(step) } if ($$.isLegendRight) { xForLegend = function(id) { return maxWidth * steps[id] } yForLegend = function(id) { return margins[steps[id]] + offsets[id] } } else if ($$.isLegendInset) { xForLegend = function(id) { return maxWidth * steps[id] + 10 } yForLegend = function(id) { return margins[steps[id]] + offsets[id] } } else { xForLegend = function(id) { return margins[steps[id]] + offsets[id] } yForLegend = function(id) { return maxHeight * steps[id] } } xForLegendText = function(id, i) { return xForLegend(id, i) + 4 + config.legend_item_tile_width } yForLegendText = function(id, i) { return yForLegend(id, i) + 9 } xForLegendRect = function(id, i) { return xForLegend(id, i) } yForLegendRect = function(id, i) { return yForLegend(id, i) - 5 } x1ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 } x2ForLegendTile = function(id, i) { return xForLegend(id, i) - 2 + config.legend_item_tile_width } yForLegendTile = function(id, i) { return yForLegend(id, i) + 4 } // Define g for legend area l = $$.legend .selectAll('.' + CLASS.legendItem) .data(targetIds) .enter() .append('g') .attr('class', function(id) { return $$.generateClass(CLASS.legendItem, id) }) .style('visibility', function(id) { return $$.isLegendToShow(id) ? 'visible' : 'hidden' }) .style('cursor', function() { return config.interaction_enabled ? 'pointer' : 'auto' }) .on( 'click', config.interaction_enabled ? function(id) { if (config.legend_item_onclick) { config.legend_item_onclick.call($$, id) } else { if ($$.d3.event.altKey) { $$.api.hide() $$.api.show(id) } else { $$.api.toggle(id) $$.isTargetToShow(id) ? $$.api.focus(id) : $$.api.revert() } } } : null ) .on( 'mouseover', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseover) { config.legend_item_onmouseover.call($$, id) } else { $$.d3.select(this).classed(CLASS.legendItemFocused, true) if (!$$.transiting && $$.isTargetToShow(id)) { $$.api.focus(id) } } } : null ) .on( 'mouseout', config.interaction_enabled ? function(id) { if (config.legend_item_onmouseout) { config.legend_item_onmouseout.call($$, id) } else { $$.d3.select(this).classed(CLASS.legendItemFocused, false) $$.api.revert() } } : null ) l.append('text') .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) .each(function(id, i) { updatePositions(this, id, i) }) .style('pointer-events', 'none') .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendText : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendText) l.append('rect') .attr('class', CLASS.legendItemEvent) .style('fill-opacity', 0) .attr('x', $$.isLegendRight || $$.isLegendInset ? xForLegendRect : -200) .attr('y', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendRect) l.append('line') .attr('class', CLASS.legendItemTile) .style('stroke', $$.color) .style('pointer-events', 'none') .attr('x1', $$.isLegendRight || $$.isLegendInset ? x1ForLegendTile : -200) .attr('y1', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('x2', $$.isLegendRight || $$.isLegendInset ? x2ForLegendTile : -200) .attr('y2', $$.isLegendRight || $$.isLegendInset ? -200 : yForLegendTile) .attr('stroke-width', config.legend_item_tile_height) // Set background for inset legend background = $$.legend.select('.' + CLASS.legendBackground + ' rect') if ($$.isLegendInset && maxWidth > 0 && background.size() === 0) { background = $$.legend .insert('g', '.' + CLASS.legendItem) .attr('class', CLASS.legendBackground) .append('rect') } texts = $$.legend .selectAll('text') .data(targetIds) .text(function(id) { return isDefined(config.data_names[id]) ? config.data_names[id] : id }) // MEMO: needed for update .each(function(id, i) { updatePositions(this, id, i) }) ;(withTransition ? texts.transition() : texts) .attr('x', xForLegendText) .attr('y', yForLegendText) rects = $$.legend.selectAll('rect.' + CLASS.legendItemEvent).data(targetIds) ;(withTransition ? rects.transition() : rects) .attr('width', function(id) { return widths[id] }) .attr('height', function(id) { return heights[id] }) .attr('x', xForLegendRect) .attr('y', yForLegendRect) tiles = $$.legend.selectAll('line.' + CLASS.legendItemTile).data(targetIds) ;(withTransition ? tiles.transition() : tiles) .style( 'stroke', $$.levelColor ? function(id) { return $$.levelColor( $$.cache[id].values.reduce(function(total, item) { return total + item.value }, 0) ) } : $$.color ) .attr('x1', x1ForLegendTile) .attr('y1', yForLegendTile) .attr('x2', x2ForLegendTile) .attr('y2', yForLegendTile) if (background) { ;(withTransition ? background.transition() : background) .attr('height', $$.getLegendHeight() - 12) .attr('width', maxWidth * (step + 1) + 10) } // toggle legend state $$.legend .selectAll('.' + CLASS.legendItem) .classed(CLASS.legendItemHidden, function(id) { return !$$.isTargetToShow(id) }) // Update all to reflect change of legend $$.updateLegendItemWidth(maxWidth) $$.updateLegendItemHeight(maxHeight) $$.updateLegendStep(step) // Update size and scale $$.updateSizes() $$.updateScales() $$.updateSvgSize() // Update g positions $$.transformAll(withTransitionForTransform, transitions) $$.legendHasRendered = true } ================================================ FILE: src/polyfill.ts ================================================ /* jshint ignore:start */ // @ts-nocheck // SVGPathSeg API polyfill // https://github.com/progers/pathseg // // This is a drop-in replacement for the SVGPathSeg and SVGPathSegList APIs that were removed from // SVG2 (https://lists.w3.org/Archives/Public/www-svg/2015Jun/0044.html), including the latest spec // changes which were implemented in Firefox 43 and Chrome 46. ;(function() { 'use strict' if (!('SVGPathSeg' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSeg window.SVGPathSeg = function(type, typeAsLetter, owningPathSegList) { this.pathSegType = type this.pathSegTypeAsLetter = typeAsLetter this._owningPathSegList = owningPathSegList } window.SVGPathSeg.prototype.classname = 'SVGPathSeg' window.SVGPathSeg.PATHSEG_UNKNOWN = 0 window.SVGPathSeg.PATHSEG_CLOSEPATH = 1 window.SVGPathSeg.PATHSEG_MOVETO_ABS = 2 window.SVGPathSeg.PATHSEG_MOVETO_REL = 3 window.SVGPathSeg.PATHSEG_LINETO_ABS = 4 window.SVGPathSeg.PATHSEG_LINETO_REL = 5 window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS = 6 window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL = 7 window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS = 8 window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL = 9 window.SVGPathSeg.PATHSEG_ARC_ABS = 10 window.SVGPathSeg.PATHSEG_ARC_REL = 11 window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS = 12 window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL = 13 window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS = 14 window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL = 15 window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16 window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17 window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18 window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19 // Notify owning PathSegList on any changes so they can be synchronized back to the path element. window.SVGPathSeg.prototype._segmentChanged = function() { if (this._owningPathSegList) this._owningPathSegList.segmentChanged(this) } window.SVGPathSegClosePath = function(owningPathSegList) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CLOSEPATH, 'z', owningPathSegList ) } window.SVGPathSegClosePath.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegClosePath.prototype.toString = function() { return '[object SVGPathSegClosePath]' } window.SVGPathSegClosePath.prototype._asPathString = function() { return this.pathSegTypeAsLetter } window.SVGPathSegClosePath.prototype.clone = function() { return new window.SVGPathSegClosePath(undefined) } window.SVGPathSegMovetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_ABS, 'M', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegMovetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegMovetoAbs.prototype.toString = function() { return '[object SVGPathSegMovetoAbs]' } window.SVGPathSegMovetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegMovetoAbs.prototype.clone = function() { return new window.SVGPathSegMovetoAbs(undefined, this._x, this._y) } Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegMovetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegMovetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_MOVETO_REL, 'm', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegMovetoRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegMovetoRel.prototype.toString = function() { return '[object SVGPathSegMovetoRel]' } window.SVGPathSegMovetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegMovetoRel.prototype.clone = function() { return new window.SVGPathSegMovetoRel(undefined, this._x, this._y) } Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegMovetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoAbs = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_ABS, 'L', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegLinetoAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoAbs.prototype.toString = function() { return '[object SVGPathSegLinetoAbs]' } window.SVGPathSegLinetoAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegLinetoAbs.prototype.clone = function() { return new window.SVGPathSegLinetoAbs(undefined, this._x, this._y) } Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegLinetoAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoRel = function(owningPathSegList, x, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_REL, 'l', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegLinetoRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoRel.prototype.toString = function() { return '[object SVGPathSegLinetoRel]' } window.SVGPathSegLinetoRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegLinetoRel.prototype.clone = function() { return new window.SVGPathSegLinetoRel(undefined, this._x, this._y) } Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegLinetoRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegCurvetoCubicAbs = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS, 'C', owningPathSegList ) this._x = x this._y = y this._x1 = x1 this._y1 = y1 this._x2 = x2 this._y2 = y2 } window.SVGPathSegCurvetoCubicAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoCubicAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicAbs]' } window.SVGPathSegCurvetoCubicAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoCubicAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicAbs( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) } Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2 this._segmentChanged() }, enumerable: true }) window.SVGPathSegCurvetoCubicRel = function( owningPathSegList, x, y, x1, y1, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL, 'c', owningPathSegList ) this._x = x this._y = y this._x1 = x1 this._y1 = y1 this._x2 = x2 this._y2 = y2 } window.SVGPathSegCurvetoCubicRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoCubicRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicRel]' } window.SVGPathSegCurvetoCubicRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoCubicRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicRel( undefined, this._x, this._y, this._x1, this._y1, this._x2, this._y2 ) } Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoCubicRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2 this._segmentChanged() }, enumerable: true }) window.SVGPathSegCurvetoQuadraticAbs = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS, 'Q', owningPathSegList ) this._x = x this._y = y this._x1 = x1 this._y1 = y1 } window.SVGPathSegCurvetoQuadraticAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoQuadraticAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticAbs]' } window.SVGPathSegCurvetoQuadraticAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoQuadraticAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticAbs( undefined, this._x, this._y, this._x1, this._y1 ) } Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1 this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoQuadraticAbs.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1 this._segmentChanged() }, enumerable: true } ) window.SVGPathSegCurvetoQuadraticRel = function( owningPathSegList, x, y, x1, y1 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL, 'q', owningPathSegList ) this._x = x this._y = y this._x1 = x1 this._y1 = y1 } window.SVGPathSegCurvetoQuadraticRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoQuadraticRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticRel]' } window.SVGPathSegCurvetoQuadraticRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x1 + ' ' + this._y1 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoQuadraticRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticRel( undefined, this._x, this._y, this._x1, this._y1 ) } Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegCurvetoQuadraticRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'x1', { get: function() { return this._x1 }, set: function(x1) { this._x1 = x1 this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoQuadraticRel.prototype, 'y1', { get: function() { return this._y1 }, set: function(y1) { this._y1 = y1 this._segmentChanged() }, enumerable: true } ) window.SVGPathSegArcAbs = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_ABS, 'A', owningPathSegList ) this._x = x this._y = y this._r1 = r1 this._r2 = r2 this._angle = angle this._largeArcFlag = largeArcFlag this._sweepFlag = sweepFlag } window.SVGPathSegArcAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegArcAbs.prototype.toString = function() { return '[object SVGPathSegArcAbs]' } window.SVGPathSegArcAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegArcAbs.prototype.clone = function() { return new window.SVGPathSegArcAbs( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) } Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcAbs.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag this._segmentChanged() }, enumerable: true }) window.SVGPathSegArcRel = function( owningPathSegList, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_ARC_REL, 'a', owningPathSegList ) this._x = x this._y = y this._r1 = r1 this._r2 = r2 this._angle = angle this._largeArcFlag = largeArcFlag this._sweepFlag = sweepFlag } window.SVGPathSegArcRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegArcRel.prototype.toString = function() { return '[object SVGPathSegArcRel]' } window.SVGPathSegArcRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._r1 + ' ' + this._r2 + ' ' + this._angle + ' ' + (this._largeArcFlag ? '1' : '0') + ' ' + (this._sweepFlag ? '1' : '0') + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegArcRel.prototype.clone = function() { return new window.SVGPathSegArcRel( undefined, this._x, this._y, this._r1, this._r2, this._angle, this._largeArcFlag, this._sweepFlag ) } Object.defineProperty(window.SVGPathSegArcRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r1', { get: function() { return this._r1 }, set: function(r1) { this._r1 = r1 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'r2', { get: function() { return this._r2 }, set: function(r2) { this._r2 = r2 this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'angle', { get: function() { return this._angle }, set: function(angle) { this._angle = angle this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'largeArcFlag', { get: function() { return this._largeArcFlag }, set: function(largeArcFlag) { this._largeArcFlag = largeArcFlag this._segmentChanged() }, enumerable: true }) Object.defineProperty(window.SVGPathSegArcRel.prototype, 'sweepFlag', { get: function() { return this._sweepFlag }, set: function(sweepFlag) { this._sweepFlag = sweepFlag this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoHorizontalAbs = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS, 'H', owningPathSegList ) this._x = x } window.SVGPathSegLinetoHorizontalAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoHorizontalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalAbs]' } window.SVGPathSegLinetoHorizontalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x } window.SVGPathSegLinetoHorizontalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalAbs(undefined, this._x) } Object.defineProperty(window.SVGPathSegLinetoHorizontalAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoHorizontalRel = function(owningPathSegList, x) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL, 'h', owningPathSegList ) this._x = x } window.SVGPathSegLinetoHorizontalRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoHorizontalRel.prototype.toString = function() { return '[object SVGPathSegLinetoHorizontalRel]' } window.SVGPathSegLinetoHorizontalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x } window.SVGPathSegLinetoHorizontalRel.prototype.clone = function() { return new window.SVGPathSegLinetoHorizontalRel(undefined, this._x) } Object.defineProperty(window.SVGPathSegLinetoHorizontalRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoVerticalAbs = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS, 'V', owningPathSegList ) this._y = y } window.SVGPathSegLinetoVerticalAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoVerticalAbs.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalAbs]' } window.SVGPathSegLinetoVerticalAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y } window.SVGPathSegLinetoVerticalAbs.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalAbs(undefined, this._y) } Object.defineProperty(window.SVGPathSegLinetoVerticalAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegLinetoVerticalRel = function(owningPathSegList, y) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL, 'v', owningPathSegList ) this._y = y } window.SVGPathSegLinetoVerticalRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegLinetoVerticalRel.prototype.toString = function() { return '[object SVGPathSegLinetoVerticalRel]' } window.SVGPathSegLinetoVerticalRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._y } window.SVGPathSegLinetoVerticalRel.prototype.clone = function() { return new window.SVGPathSegLinetoVerticalRel(undefined, this._y) } Object.defineProperty(window.SVGPathSegLinetoVerticalRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true }) window.SVGPathSegCurvetoCubicSmoothAbs = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS, 'S', owningPathSegList ) this._x = x this._y = y this._x2 = x2 this._y2 = y2 } window.SVGPathSegCurvetoCubicSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoCubicSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothAbs]' } window.SVGPathSegCurvetoCubicSmoothAbs.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoCubicSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothAbs( undefined, this._x, this._y, this._x2, this._y2 ) } Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2 this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothAbs.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2 this._segmentChanged() }, enumerable: true } ) window.SVGPathSegCurvetoCubicSmoothRel = function( owningPathSegList, x, y, x2, y2 ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL, 's', owningPathSegList ) this._x = x this._y = y this._x2 = x2 this._y2 = y2 } window.SVGPathSegCurvetoCubicSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoCubicSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoCubicSmoothRel]' } window.SVGPathSegCurvetoCubicSmoothRel.prototype._asPathString = function() { return ( this.pathSegTypeAsLetter + ' ' + this._x2 + ' ' + this._y2 + ' ' + this._x + ' ' + this._y ) } window.SVGPathSegCurvetoCubicSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoCubicSmoothRel( undefined, this._x, this._y, this._x2, this._y2 ) } Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'x2', { get: function() { return this._x2 }, set: function(x2) { this._x2 = x2 this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoCubicSmoothRel.prototype, 'y2', { get: function() { return this._y2 }, set: function(y2) { this._y2 = y2 this._segmentChanged() }, enumerable: true } ) window.SVGPathSegCurvetoQuadraticSmoothAbs = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS, 'T', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothAbs]' } window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothAbs( undefined, this._x, this._y ) } Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothAbs.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true } ) window.SVGPathSegCurvetoQuadraticSmoothRel = function( owningPathSegList, x, y ) { window.SVGPathSeg.call( this, window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL, 't', owningPathSegList ) this._x = x this._y = y } window.SVGPathSegCurvetoQuadraticSmoothRel.prototype = Object.create( window.SVGPathSeg.prototype ) window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.toString = function() { return '[object SVGPathSegCurvetoQuadraticSmoothRel]' } window.SVGPathSegCurvetoQuadraticSmoothRel.prototype._asPathString = function() { return this.pathSegTypeAsLetter + ' ' + this._x + ' ' + this._y } window.SVGPathSegCurvetoQuadraticSmoothRel.prototype.clone = function() { return new window.SVGPathSegCurvetoQuadraticSmoothRel( undefined, this._x, this._y ) } Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'x', { get: function() { return this._x }, set: function(x) { this._x = x this._segmentChanged() }, enumerable: true } ) Object.defineProperty( window.SVGPathSegCurvetoQuadraticSmoothRel.prototype, 'y', { get: function() { return this._y }, set: function(y) { this._y = y this._segmentChanged() }, enumerable: true } ) // Add createSVGPathSeg* functions to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-Interfacewindow.SVGPathElement. window.SVGPathElement.prototype.createSVGPathSegClosePath = function() { return new window.SVGPathSegClosePath(undefined) } window.SVGPathElement.prototype.createSVGPathSegMovetoAbs = function(x, y) { return new window.SVGPathSegMovetoAbs(undefined, x, y) } window.SVGPathElement.prototype.createSVGPathSegMovetoRel = function(x, y) { return new window.SVGPathSegMovetoRel(undefined, x, y) } window.SVGPathElement.prototype.createSVGPathSegLinetoAbs = function(x, y) { return new window.SVGPathSegLinetoAbs(undefined, x, y) } window.SVGPathElement.prototype.createSVGPathSegLinetoRel = function(x, y) { return new window.SVGPathSegLinetoRel(undefined, x, y) } window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicAbs = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicAbs( undefined, x, y, x1, y1, x2, y2 ) } window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicRel = function( x, y, x1, y1, x2, y2 ) { return new window.SVGPathSegCurvetoCubicRel( undefined, x, y, x1, y1, x2, y2 ) } window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticAbs = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticAbs(undefined, x, y, x1, y1) } window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticRel = function( x, y, x1, y1 ) { return new window.SVGPathSegCurvetoQuadraticRel(undefined, x, y, x1, y1) } window.SVGPathElement.prototype.createSVGPathSegArcAbs = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcAbs( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) } window.SVGPathElement.prototype.createSVGPathSegArcRel = function( x, y, r1, r2, angle, largeArcFlag, sweepFlag ) { return new window.SVGPathSegArcRel( undefined, x, y, r1, r2, angle, largeArcFlag, sweepFlag ) } window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalAbs = function( x ) { return new window.SVGPathSegLinetoHorizontalAbs(undefined, x) } window.SVGPathElement.prototype.createSVGPathSegLinetoHorizontalRel = function( x ) { return new window.SVGPathSegLinetoHorizontalRel(undefined, x) } window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalAbs = function( y ) { return new window.SVGPathSegLinetoVerticalAbs(undefined, y) } window.SVGPathElement.prototype.createSVGPathSegLinetoVerticalRel = function( y ) { return new window.SVGPathSegLinetoVerticalRel(undefined, y) } window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothAbs = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothAbs(undefined, x, y, x2, y2) } window.SVGPathElement.prototype.createSVGPathSegCurvetoCubicSmoothRel = function( x, y, x2, y2 ) { return new window.SVGPathSegCurvetoCubicSmoothRel(undefined, x, y, x2, y2) } window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothAbs = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothAbs(undefined, x, y) } window.SVGPathElement.prototype.createSVGPathSegCurvetoQuadraticSmoothRel = function( x, y ) { return new window.SVGPathSegCurvetoQuadraticSmoothRel(undefined, x, y) } if (!('getPathSegAtLength' in window.SVGPathElement.prototype)) { // Add getPathSegAtLength to SVGPathElement. // Spec: https://www.w3.org/TR/SVG11/single-page.html#paths-__svg__SVGPathElement__getPathSegAtLength // This polyfill requires SVGPathElement.getTotalLength to implement the distance-along-a-path algorithm. window.SVGPathElement.prototype.getPathSegAtLength = function(distance) { if (distance === undefined || !isFinite(distance)) throw 'Invalid arguments.' var measurementElement = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ) measurementElement.setAttribute('d', this.getAttribute('d')) var lastPathSegment = measurementElement.pathSegList.numberOfItems - 1 // If the path is empty, return 0. if (lastPathSegment <= 0) return 0 do { measurementElement.pathSegList.removeItem(lastPathSegment) if (distance > measurementElement.getTotalLength()) break lastPathSegment-- } while (lastPathSegment > 0) return lastPathSegment } } } if (!('SVGPathSegList' in window)) { // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGPathSegList window.SVGPathSegList = function(pathElement) { this._pathElement = pathElement this._list = this._parsePath(this._pathElement.getAttribute('d')) // Use a MutationObserver to catch changes to the path's "d" attribute. this._mutationObserverConfig = { attributes: true, attributeFilter: ['d'] } this._pathElementMutationObserver = new MutationObserver( this._updateListFromPathMutations.bind(this) ) this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ) } window.SVGPathSegList.prototype.classname = 'SVGPathSegList' Object.defineProperty(window.SVGPathSegList.prototype, 'numberOfItems', { get: function() { this._checkPathSynchronizedToList() return this._list.length }, enumerable: true }) // Add the pathSegList accessors to window.SVGPathElement. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-InterfaceSVGAnimatedPathData Object.defineProperty(window.SVGPathElement.prototype, 'pathSegList', { get: function() { if (!this._pathSegList) this._pathSegList = new window.SVGPathSegList(this) return this._pathSegList }, enumerable: true }) // FIXME: The following are not implemented and simply return window.SVGPathElement.pathSegList. Object.defineProperty( window.SVGPathElement.prototype, 'normalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ) Object.defineProperty( window.SVGPathElement.prototype, 'animatedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ) Object.defineProperty( window.SVGPathElement.prototype, 'animatedNormalizedPathSegList', { get: function() { return this.pathSegList }, enumerable: true } ) // Process any pending mutations to the path element and update the list as needed. // This should be the first call of all public functions and is needed because // MutationObservers are not synchronous so we can have pending asynchronous mutations. window.SVGPathSegList.prototype._checkPathSynchronizedToList = function() { this._updateListFromPathMutations( this._pathElementMutationObserver.takeRecords() ) } window.SVGPathSegList.prototype._updateListFromPathMutations = function( mutationRecords ) { if (!this._pathElement) return var hasPathMutations = false mutationRecords.forEach(function(record) { if (record.attributeName == 'd') hasPathMutations = true }) if (hasPathMutations) this._list = this._parsePath(this._pathElement.getAttribute('d')) } // Serialize the list and update the path's 'd' attribute. window.SVGPathSegList.prototype._writeListToPath = function() { this._pathElementMutationObserver.disconnect() this._pathElement.setAttribute( 'd', window.SVGPathSegList._pathSegArrayAsString(this._list) ) this._pathElementMutationObserver.observe( this._pathElement, this._mutationObserverConfig ) } // When a path segment changes the list needs to be synchronized back to the path element. window.SVGPathSegList.prototype.segmentChanged = function(pathSeg) { this._writeListToPath() } window.SVGPathSegList.prototype.clear = function() { this._checkPathSynchronizedToList() this._list.forEach(function(pathSeg) { pathSeg._owningPathSegList = null }) this._list = [] this._writeListToPath() } window.SVGPathSegList.prototype.initialize = function(newItem) { this._checkPathSynchronizedToList() this._list = [newItem] newItem._owningPathSegList = this this._writeListToPath() return newItem } window.SVGPathSegList.prototype._checkValidIndex = function(index) { if (isNaN(index) || index < 0 || index >= this.numberOfItems) throw 'INDEX_SIZE_ERR' } window.SVGPathSegList.prototype.getItem = function(index) { this._checkPathSynchronizedToList() this._checkValidIndex(index) return this._list[index] } window.SVGPathSegList.prototype.insertItemBefore = function( newItem, index ) { this._checkPathSynchronizedToList() // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. if (index > this.numberOfItems) index = this.numberOfItems if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone() } this._list.splice(index, 0, newItem) newItem._owningPathSegList = this this._writeListToPath() return newItem } window.SVGPathSegList.prototype.replaceItem = function(newItem, index) { this._checkPathSynchronizedToList() if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone() } this._checkValidIndex(index) this._list[index] = newItem newItem._owningPathSegList = this this._writeListToPath() return newItem } window.SVGPathSegList.prototype.removeItem = function(index) { this._checkPathSynchronizedToList() this._checkValidIndex(index) var item = this._list[index] this._list.splice(index, 1) this._writeListToPath() return item } window.SVGPathSegList.prototype.appendItem = function(newItem) { this._checkPathSynchronizedToList() if (newItem._owningPathSegList) { // SVG2 spec says to make a copy. newItem = newItem.clone() } this._list.push(newItem) newItem._owningPathSegList = this // TODO: Optimize this to just append to the existing attribute. this._writeListToPath() return newItem } window.SVGPathSegList._pathSegArrayAsString = function(pathSegArray) { var string = '' var first = true pathSegArray.forEach(function(pathSeg) { if (first) { first = false string += pathSeg._asPathString() } else { string += ' ' + pathSeg._asPathString() } }) return string } // This closely follows SVGPathParser::parsePath from Source/core/svg/SVGPathParser.cpp. window.SVGPathSegList.prototype._parsePath = function(string) { if (!string || string.length == 0) return [] var owningPathSegList = this var Builder = function() { this.pathSegList = [] } Builder.prototype.appendSegment = function(pathSeg) { this.pathSegList.push(pathSeg) } var Source = function(string) { this._string = string this._currentIndex = 0 this._endIndex = this._string.length this._previousCommand = window.SVGPathSeg.PATHSEG_UNKNOWN this._skipOptionalSpaces() } Source.prototype._isCurrentSpace = function() { var character = this._string[this._currentIndex] return ( character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f') ) } Source.prototype._skipOptionalSpaces = function() { while (this._currentIndex < this._endIndex && this._isCurrentSpace()) this._currentIndex++ return this._currentIndex < this._endIndex } Source.prototype._skipOptionalSpacesOrDelimiter = function() { if ( this._currentIndex < this._endIndex && !this._isCurrentSpace() && this._string.charAt(this._currentIndex) != ',' ) return false if (this._skipOptionalSpaces()) { if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == ',' ) { this._currentIndex++ this._skipOptionalSpaces() } } return this._currentIndex < this._endIndex } Source.prototype.hasMoreData = function() { return this._currentIndex < this._endIndex } Source.prototype.peekSegmentType = function() { var lookahead = this._string[this._currentIndex] return this._pathSegTypeFromChar(lookahead) } Source.prototype._pathSegTypeFromChar = function(lookahead) { switch (lookahead) { case 'Z': case 'z': return window.SVGPathSeg.PATHSEG_CLOSEPATH case 'M': return window.SVGPathSeg.PATHSEG_MOVETO_ABS case 'm': return window.SVGPathSeg.PATHSEG_MOVETO_REL case 'L': return window.SVGPathSeg.PATHSEG_LINETO_ABS case 'l': return window.SVGPathSeg.PATHSEG_LINETO_REL case 'C': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS case 'c': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL case 'Q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS case 'q': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL case 'A': return window.SVGPathSeg.PATHSEG_ARC_ABS case 'a': return window.SVGPathSeg.PATHSEG_ARC_REL case 'H': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS case 'h': return window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL case 'V': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS case 'v': return window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL case 'S': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS case 's': return window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL case 'T': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS case 't': return window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL default: return window.SVGPathSeg.PATHSEG_UNKNOWN } } Source.prototype._nextCommandHelper = function( lookahead, previousCommand ) { // Check for remaining coordinates in the current command. if ( (lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) && previousCommand != window.SVGPathSeg.PATHSEG_CLOSEPATH ) { if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_ABS) return window.SVGPathSeg.PATHSEG_LINETO_ABS if (previousCommand == window.SVGPathSeg.PATHSEG_MOVETO_REL) return window.SVGPathSeg.PATHSEG_LINETO_REL return previousCommand } return window.SVGPathSeg.PATHSEG_UNKNOWN } Source.prototype.initialCommandIsMoveTo = function() { // If the path is empty it is still valid, so return true. if (!this.hasMoreData()) return true var command = this.peekSegmentType() // Path must start with moveTo. return ( command == window.SVGPathSeg.PATHSEG_MOVETO_ABS || command == window.SVGPathSeg.PATHSEG_MOVETO_REL ) } // Parse a number from an SVG path. This very closely follows genericParseNumber(...) from Source/core/svg/SVGParserUtilities.cpp. // Spec: http://www.w3.org/TR/SVG11/single-page.html#paths-PathDataBNF Source.prototype._parseNumber = function() { var exponent = 0 var integer = 0 var frac = 1 var decimal = 0 var sign = 1 var expsign = 1 var startIndex = this._currentIndex this._skipOptionalSpaces() // Read the sign. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '+' ) this._currentIndex++ else if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '-' ) { this._currentIndex++ sign = -1 } if ( this._currentIndex == this._endIndex || ((this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9') && this._string.charAt(this._currentIndex) != '.') ) // The first character of a number must be one of [0-9+-.]. return undefined // Read the integer part, build right-to-left. var startIntPartIndex = this._currentIndex while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) this._currentIndex++ // Advance to first non-digit. if (this._currentIndex != startIntPartIndex) { var scanIntPartIndex = this._currentIndex - 1 var multiplier = 1 while (scanIntPartIndex >= startIntPartIndex) { integer += multiplier * (this._string.charAt(scanIntPartIndex--) - '0') multiplier *= 10 } } // Read the decimals. if ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) == '.' ) { this._currentIndex++ // There must be a least one digit following the . if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { frac *= 10 decimal += (this._string.charAt(this._currentIndex) - '0') / frac this._currentIndex += 1 } } // Read the exponent part. if ( this._currentIndex != startIndex && this._currentIndex + 1 < this._endIndex && (this._string.charAt(this._currentIndex) == 'e' || this._string.charAt(this._currentIndex) == 'E') && this._string.charAt(this._currentIndex + 1) != 'x' && this._string.charAt(this._currentIndex + 1) != 'm' ) { this._currentIndex++ // Read the sign of the exponent. if (this._string.charAt(this._currentIndex) == '+') { this._currentIndex++ } else if (this._string.charAt(this._currentIndex) == '-') { this._currentIndex++ expsign = -1 } // There must be an exponent. if ( this._currentIndex >= this._endIndex || this._string.charAt(this._currentIndex) < '0' || this._string.charAt(this._currentIndex) > '9' ) return undefined while ( this._currentIndex < this._endIndex && this._string.charAt(this._currentIndex) >= '0' && this._string.charAt(this._currentIndex) <= '9' ) { exponent *= 10 exponent += this._string.charAt(this._currentIndex) - '0' this._currentIndex++ } } var number = integer + decimal number *= sign if (exponent) number *= Math.pow(10, expsign * exponent) if (startIndex == this._currentIndex) return undefined this._skipOptionalSpacesOrDelimiter() return number } Source.prototype._parseArcFlag = function() { if (this._currentIndex >= this._endIndex) return undefined var flag = false var flagChar = this._string.charAt(this._currentIndex++) if (flagChar == '0') flag = false else if (flagChar == '1') flag = true else return undefined this._skipOptionalSpacesOrDelimiter() return flag } Source.prototype.parseSegment = function() { var lookahead = this._string[this._currentIndex] var command = this._pathSegTypeFromChar(lookahead) if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) { // Possibly an implicit command. Not allowed if this is the first command. if (this._previousCommand == window.SVGPathSeg.PATHSEG_UNKNOWN) return null command = this._nextCommandHelper(lookahead, this._previousCommand) if (command == window.SVGPathSeg.PATHSEG_UNKNOWN) return null } else { this._currentIndex++ } this._previousCommand = command switch (command) { case window.SVGPathSeg.PATHSEG_MOVETO_REL: return new window.SVGPathSegMovetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_MOVETO_ABS: return new window.SVGPathSegMovetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_REL: return new window.SVGPathSegLinetoRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_ABS: return new window.SVGPathSegLinetoAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL: return new window.SVGPathSegLinetoHorizontalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS: return new window.SVGPathSegLinetoHorizontalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL: return new window.SVGPathSegLinetoVerticalRel( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS: return new window.SVGPathSegLinetoVerticalAbs( owningPathSegList, this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CLOSEPATH: this._skipOptionalSpaces() return new window.SVGPathSegClosePath(owningPathSegList) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoCubicRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoCubicAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoCubicSmoothRel( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: var points = { x2: this._parseNumber(), y2: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoCubicSmoothAbs( owningPathSegList, points.x, points.y, points.x2, points.y2 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoQuadraticRel( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegCurvetoQuadraticAbs( owningPathSegList, points.x, points.y, points.x1, points.y1 ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: return new window.SVGPathSegCurvetoQuadraticSmoothRel( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: return new window.SVGPathSegCurvetoQuadraticSmoothAbs( owningPathSegList, this._parseNumber(), this._parseNumber() ) case window.SVGPathSeg.PATHSEG_ARC_REL: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegArcRel( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) case window.SVGPathSeg.PATHSEG_ARC_ABS: var points = { x1: this._parseNumber(), y1: this._parseNumber(), arcAngle: this._parseNumber(), arcLarge: this._parseArcFlag(), arcSweep: this._parseArcFlag(), x: this._parseNumber(), y: this._parseNumber() } return new window.SVGPathSegArcAbs( owningPathSegList, points.x, points.y, points.x1, points.y1, points.arcAngle, points.arcLarge, points.arcSweep ) default: throw 'Unknown path seg type.' } } var builder = new Builder() var source = new Source(string) if (!source.initialCommandIsMoveTo()) return [] while (source.hasMoreData()) { var pathSeg = source.parseSegment() if (!pathSeg) return [] builder.appendSegment(pathSeg) } return builder.pathSegList } } })() // String.padEnd polyfill for IE11 // // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd if (!String.prototype.padEnd) { String.prototype.padEnd = function padEnd(targetLength, padString) { targetLength = targetLength >> 0 //floor if number or convert non-number to 0; padString = String(typeof padString !== 'undefined' ? padString : ' ') if (this.length > targetLength) { return String(this) } else { targetLength = targetLength - this.length if (targetLength > padString.length) { padString += padString.repeat(targetLength / padString.length) //append to original to ensure we are longer than needed } return String(this) + padString.slice(0, targetLength) } } } // Object.assign polyfill for IE11 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, 'assign', { value: function assign(target, varArgs) { // .length of function is 2 'use strict' if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object') } var to = Object(target) for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index] if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey] } } } } return to }, writable: true, configurable: true }) } /* jshint ignore:end */ ================================================ FILE: src/region.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue } from './util' ChartInternal.prototype.initRegion = function() { var $$ = this $$.region = $$.main .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.regions) } ChartInternal.prototype.updateRegion = function(duration) { var $$ = this, config = $$.config // hide if arc type $$.region.style('visibility', $$.hasArcType() ? 'hidden' : 'visible') var mainRegion = $$.main .select('.' + CLASS.regions) .selectAll('.' + CLASS.region) .data(config.regions) var g = mainRegion.enter().append('g') g.append('rect') .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }) g.append('text').text($$.labelRegion.bind($$)) $$.mainRegion = g.merge(mainRegion).attr('class', $$.classRegion.bind($$)) mainRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove() } ChartInternal.prototype.redrawRegion = function(withTransition, transition) { var $$ = this, regions = $$.mainRegion, regionLabels = $$.mainRegion.selectAll('text') return [ (withTransition ? regions.transition(transition) : regions) .attr('x', $$.regionX.bind($$)) .attr('y', $$.regionY.bind($$)) .attr('width', $$.regionWidth.bind($$)) .attr('height', $$.regionHeight.bind($$)) .style('fill-opacity', function(d) { return isValue(d.opacity) ? d.opacity : 0.1 }), (withTransition ? regionLabels.transition(transition) : regionLabels) .attr('x', $$.labelOffsetX.bind($$)) .attr('y', $$.labelOffsetY.bind($$)) .attr('transform', $$.labelTransform.bind($$)) .attr('style', 'text-anchor: left;') ] } ChartInternal.prototype.regionX = function(d) { var $$ = this, config = $$.config, xPos, yScale = d.axis === 'y' ? $$.y : $$.y2 if (d.axis === 'y' || d.axis === 'y2') { xPos = config.axis_rotated ? ('start' in d ? yScale(d.start) : 0) : 0 } else { xPos = config.axis_rotated ? 0 : 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 } return xPos } ChartInternal.prototype.regionY = function(d) { var $$ = this, config = $$.config, yPos, yScale = d.axis === 'y' ? $$.y : $$.y2 if (d.axis === 'y' || d.axis === 'y2') { yPos = config.axis_rotated ? 0 : 'end' in d ? yScale(d.end) : 0 } else { yPos = config.axis_rotated ? 'start' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.start) : d.start) : 0 : 0 } return yPos } ChartInternal.prototype.regionWidth = function(d) { var $$ = this, config = $$.config, start = $$.regionX(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2 if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? 'end' in d ? yScale(d.end) : $$.width : $$.width } else { end = config.axis_rotated ? $$.width : 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.width } return end < start ? 0 : end - start } ChartInternal.prototype.regionHeight = function(d) { var $$ = this, config = $$.config, start = this.regionY(d), end, yScale = d.axis === 'y' ? $$.y : $$.y2 if (d.axis === 'y' || d.axis === 'y2') { end = config.axis_rotated ? $$.height : 'start' in d ? yScale(d.start) : $$.height } else { end = config.axis_rotated ? 'end' in d ? $$.x($$.isTimeSeries() ? $$.parseDate(d.end) : d.end) : $$.height : $$.height } return end < start ? 0 : end - start } ChartInternal.prototype.isRegionOnX = function(d) { return !d.axis || d.axis === 'x' } ChartInternal.prototype.labelRegion = function(d) { return 'label' in d ? d.label : '' } ChartInternal.prototype.labelTransform = function(d) { return 'vertical' in d && d.vertical ? 'rotate(90)' : '' } ChartInternal.prototype.labelOffsetX = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3 var paddingY = 'paddingY' in d ? d.paddingY : 3 return 'vertical' in d && d.vertical ? this.regionY(d) + paddingY : this.regionX(d) + paddingX } ChartInternal.prototype.labelOffsetY = function(d) { var paddingX = 'paddingX' in d ? d.paddingX : 3 var paddingY = 'paddingY' in d ? d.paddingY : 3 return 'vertical' in d && d.vertical ? -(this.regionX(d) + paddingX) : this.regionY(d) + 10 + paddingY } ================================================ FILE: src/scale.ts ================================================ import { ChartInternal } from './core' function c3LogScale(d3, linearScale?, logScale?) { var PROJECTION = [0.01, 10] if (!linearScale) { linearScale = d3.scaleLinear() linearScale.range(PROJECTION) } if (!logScale) { logScale = d3.scaleLog() logScale.domain(PROJECTION) logScale.nice() } // copied from https://github.com/compute-io/logspace function logspace(a, b, len) { var arr, end, tmp, d if (arguments.length < 3) { len = 10 } else { if (len === 0) { return [] } } // Calculate the increment: end = len - 1 d = (b - a) / end // Build the output array... arr = new Array(len) tmp = a arr[0] = Math.pow(10, tmp) for (var i = 1; i < end; i++) { tmp += d arr[i] = Math.pow(10, tmp) } arr[end] = Math.pow(10, b) return arr } function scale(x) { return logScale(linearScale(x)) } scale.domain = function(x) { if (!arguments.length) { return linearScale.domain() } linearScale.domain(x) return scale } scale.range = function(x) { if (!arguments.length) { return logScale.range() } logScale.range(x) return scale } scale.ticks = function(m) { return logspace(-2, 1, m || 10).map(function(v) { return linearScale.invert(v) }) } scale.copy = function() { return c3LogScale(d3, linearScale.copy(), logScale.copy()) } return scale } ChartInternal.prototype.getScale = function(min, max, forTimeseries) { return (forTimeseries ? this.d3.scaleTime() : this.d3.scaleLinear()).range([ min, max ]) } ChartInternal.prototype.getX = function(min, max, domain, offset) { var $$ = this, scale = $$.getScale(min, max, $$.isTimeSeries()), _scale = domain ? scale.domain(domain) : scale, key // Define customized scale if categorized axis if ($$.isCategorized()) { offset = offset || function() { return 0 } scale = function(d, raw) { var v = _scale(d) + offset(d) return raw ? v : Math.ceil(v) } } else { scale = function(d, raw) { var v = _scale(d) return raw ? v : Math.ceil(v) } } // define functions for (key in _scale) { scale[key] = _scale[key] } scale.orgDomain = function() { return _scale.domain() } // define custom domain() for categorized axis if ($$.isCategorized()) { scale.domain = function(domain) { if (!arguments.length) { domain = this.orgDomain() return [domain[0], domain[1] + 1] } _scale.domain(domain) return scale } } return scale } /** * Creates and configures a D3 scale instance for the given type. * * By defaults it returns a Linear scale. * * @param {String} type Type of d3-scale to create. Type can be 'linear', 'time', 'timeseries' or 'log'. * @param {Array} domain The scale domain such as [from, to] * @param {Array} range The scale's range such as [from, to] * * @return A d3-scale instance */ ChartInternal.prototype.getY = function(type, domain, range) { let scale if (type === 'timeseries' || type === 'time') { scale = this.d3.scaleTime() } else if (type === 'log') { scale = c3LogScale(this.d3) } else if (type === 'linear' || type === undefined) { scale = this.d3.scaleLinear() } else { throw new Error(`Invalid Y axis type: "${type}"`) } if (domain) { scale.domain(domain) } if (range) { scale.range(range) } return scale } ChartInternal.prototype.getYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.y2 : this.y } ChartInternal.prototype.getSubYScale = function(id) { return this.axis.getId(id) === 'y2' ? this.subY2 : this.subY } ChartInternal.prototype.updateScales = function() { var $$ = this, config = $$.config, forInit = !$$.x // update edges $$.xMin = config.axis_rotated ? 1 : 0 $$.xMax = config.axis_rotated ? $$.height : $$.width $$.yMin = config.axis_rotated ? 0 : $$.height $$.yMax = config.axis_rotated ? $$.width : 1 $$.subXMin = $$.xMin $$.subXMax = $$.xMax $$.subYMin = config.axis_rotated ? 0 : $$.height2 $$.subYMax = config.axis_rotated ? $$.width2 : 1 // update scales $$.x = $$.getX( $$.xMin, $$.xMax, forInit ? undefined : $$.x.orgDomain(), function() { return $$.xAxis.tickOffset() } ) $$.y = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.y.domain(), [$$.yMin, $$.yMax] ) $$.y2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.y2.domain(), [$$.yMin, $$.yMax] ) $$.subX = $$.getX($$.xMin, $$.xMax, $$.orgXDomain, function(d) { return d % 1 ? 0 : $$.subXAxis.tickOffset() }) $$.subY = $$.getY( config.axis_y_type, forInit ? config.axis_y_default : $$.subY.domain(), [$$.subYMin, $$.subYMax] ) $$.subY2 = $$.getY( config.axis_y2_type, forInit ? config.axis_y2_default : $$.subY2.domain(), [$$.subYMin, $$.subYMax] ) // update axes $$.xAxisTickFormat = $$.axis.getXAxisTickFormat() $$.xAxisTickValues = $$.axis.getXAxisTickValues() $$.yAxisTickValues = $$.axis.getYAxisTickValues() $$.y2AxisTickValues = $$.axis.getY2AxisTickValues() $$.xAxis = $$.axis.getXAxis( $$.x, $$.xOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ) $$.subXAxis = $$.axis.getXAxis( $$.subX, $$.subXOrient, $$.xAxisTickFormat, $$.xAxisTickValues, config.axis_x_tick_outer ) $$.yAxis = $$.axis.getYAxis( 'y', $$.y, $$.yOrient, $$.yAxisTickValues, config.axis_y_tick_outer ) $$.y2Axis = $$.axis.getYAxis( 'y2', $$.y2, $$.y2Orient, $$.y2AxisTickValues, config.axis_y2_tick_outer ) // Set initialized scales to brush and zoom if (!forInit) { if ($$.brush) { $$.brush.updateScale($$.subX) } } // update for arc if ($$.updateArc) { $$.updateArc() } } ================================================ FILE: src/scss/arc.scss ================================================ .c3-chart-arcs-title { dominant-baseline: middle; font-size: 1.3em; } .c3-chart-arcs .c3-chart-arcs-background { fill: #e0e0e0; stroke: #FFF; } .c3-chart-arcs .c3-chart-arcs-gauge-unit { fill: #000; font-size: 16px; } .c3-chart-arcs .c3-chart-arcs-gauge-max { fill: #777; } .c3-chart-arcs .c3-chart-arcs-gauge-min { fill: #777; } .c3-chart-arc .c3-gauge-value { fill: #000; /* font-size: 28px !important;*/ } .c3-chart-arc.c3-target g path { opacity: 1; } .c3-chart-arc.c3-target.c3-focused g path { opacity: 1; } ================================================ FILE: src/scss/area.scss ================================================ .c3-area { stroke-width: 0; opacity: 0.2; } ================================================ FILE: src/scss/axis.scss ================================================ .c3-axis-x .tick { } .c3-axis-x-label { } .c3-axis-y .tick { } .c3-axis-y-label { } .c3-axis-y2 .tick { } .c3-axis-y2-label { } ================================================ FILE: src/scss/bar.scss ================================================ .c3-bar { stroke-width: 0; } .c3-bar._expanded_ { fill-opacity: 1; fill-opacity: 0.75; } ================================================ FILE: src/scss/brush.scss ================================================ .c3-brush .extent { fill-opacity: .1; } ================================================ FILE: src/scss/chart.scss ================================================ .c3 svg { font: 10px sans-serif; -webkit-tap-highlight-color: rgba(0,0,0,0); } .c3 path, .c3 line { fill: none; stroke: #000; } .c3 text { -webkit-user-select: none; -moz-user-select: none; user-select: none; } .c3-legend-item-tile, .c3-xgrid-focus, .c3-ygrid, .c3-event-rect, .c3-bars path { shape-rendering: crispEdges; } .c3-chart-arc path { stroke: #fff; } .c3-chart-arc rect { stroke: white; stroke-width: 1; } .c3-chart-arc text { fill: #fff; font-size: 13px; } ================================================ FILE: src/scss/focus.scss ================================================ .c3-target.c3-focused { opacity: 1; } .c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step { stroke-width: 2px; } .c3-target.c3-defocused { opacity: 0.3 !important; } ================================================ FILE: src/scss/grid.scss ================================================ .c3-grid line { stroke: #aaa; } .c3-grid text { fill: #aaa; } .c3-xgrid, .c3-ygrid { stroke-dasharray: 3 3; } .c3-xgrid-focus { } ================================================ FILE: src/scss/legend.scss ================================================ .c3-legend-item { font-size: 12px; } .c3-legend-item-hidden { opacity: 0.15; } .c3-legend-background { opacity: 0.75; fill: white; stroke: lightgray; stroke-width: 1 } ================================================ FILE: src/scss/line.scss ================================================ .c3-line { stroke-width: 1px; } ================================================ FILE: src/scss/main.scss ================================================ /*-- Chart --*/ @import 'chart'; /*-- Axis --*/ @import 'axis'; /*-- Grid --*/ @import 'grid'; /*-- Text on Chart --*/ @import 'text'; /*-- Line --*/ @import 'line'; /*-- Point --*/ @import 'point'; /*-- Bar --*/ @import 'bar'; /*-- Focus --*/ @import 'focus'; /*-- Region --*/ @import 'region'; /*-- Brush --*/ @import 'brush'; /*-- Select - Drag --*/ @import 'select_drag'; /*-- Legend --*/ @import 'legend'; /*-- Title --*/ @import 'title'; /*-- Tooltip --*/ @import 'tooltip'; /*-- Area --*/ @import 'area'; /*-- Arc --*/ @import 'arc'; /*-- Zoom --*/ @import 'zoom'; ================================================ FILE: src/scss/point.scss ================================================ .c3-circle { fill: currentColor; } .c3-circle._expanded_ { stroke-width: 1px; stroke: white; } .c3-selected-circle { fill: white; stroke-width: 2px; } ================================================ FILE: src/scss/region.scss ================================================ .c3-region { fill: steelblue; fill-opacity: .1; text { fill-opacity: 1; } } ================================================ FILE: src/scss/select_drag.scss ================================================ .c3-dragarea { } ================================================ FILE: src/scss/text.scss ================================================ .c3-text { } .c3-text.c3-empty { fill: #808080; font-size: 2em; } ================================================ FILE: src/scss/title.scss ================================================ .c3-title { font: 14px sans-serif; } ================================================ FILE: src/scss/tooltip.scss ================================================ .c3-tooltip-container { z-index: 10; } .c3-tooltip { border-collapse:collapse; border-spacing:0; background-color:#fff; empty-cells:show; -webkit-box-shadow: 7px 7px 12px -9px rgb(119,119,119); -moz-box-shadow: 7px 7px 12px -9px rgb(119,119,119); box-shadow: 7px 7px 12px -9px rgb(119,119,119); opacity: 0.9; } .c3-tooltip tr { border:1px solid #CCC; } .c3-tooltip th { background-color: #aaa; font-size:14px; padding:2px 5px; text-align:left; color:#FFF; } .c3-tooltip td { font-size:13px; padding: 3px 6px; background-color:#fff; border-left:1px dotted #999; } .c3-tooltip td > span { display: inline-block; width: 10px; height: 10px; margin-right: 6px; } .c3-tooltip .value { text-align: right; } ================================================ FILE: src/scss/zoom.scss ================================================ .c3-drag-zoom.enabled{ pointer-events: all!important; visibility: visible; } .c3-drag-zoom.disabled{ pointer-events: none!important; visibility: hidden; } .c3-drag-zoom .extent { fill-opacity: .1; } ================================================ FILE: src/selection.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' ChartInternal.prototype.selectPoint = function(target, d, i) { var $$ = this, config = $$.config, cx = (config.axis_rotated ? $$.circleY : $$.circleX).bind($$), cy = (config.axis_rotated ? $$.circleX : $$.circleY).bind($$), r = $$.pointSelectR.bind($$) config.data_onselected.call($$.api, d, target.node()) // add selected-circle on low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .data([d]) .enter() .append('circle') .attr('class', function() { return $$.generateClass(CLASS.selectedCircle, i) }) .attr('cx', cx) .attr('cy', cy) .attr('stroke', function() { return $$.color(d) }) .attr('r', function(d) { return $$.pointSelectR(d) * 1.4 }) .transition() .duration(100) .attr('r', r) } ChartInternal.prototype.unselectPoint = function(target, d, i) { var $$ = this $$.config.data_onunselected.call($$.api, d, target.node()) // remove selected-circle from low layer g $$.main .select('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(d.id)) .selectAll('.' + CLASS.selectedCircle + '-' + i) .transition() .duration(100) .attr('r', 0) .remove() } ChartInternal.prototype.togglePoint = function(selected, target, d, i) { selected ? this.selectPoint(target, d, i) : this.unselectPoint(target, d, i) } ChartInternal.prototype.selectPath = function(target, d) { var $$ = this $$.config.data_onselected.call($$, d, target.node()) if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.d3.rgb($$.color(d)).brighter(0.75) }) } } ChartInternal.prototype.unselectPath = function(target, d) { var $$ = this $$.config.data_onunselected.call($$, d, target.node()) if ($$.config.interaction_brighten) { target .transition() .duration(100) .style('fill', function() { return $$.color(d) }) } } ChartInternal.prototype.togglePath = function(selected, target, d, i) { selected ? this.selectPath(target, d, i) : this.unselectPath(target, d, i) } ChartInternal.prototype.getToggle = function(that, d) { var $$ = this, toggle if (that.nodeName === 'circle') { if ($$.isStepType(d)) { // circle is hidden in step chart, so treat as within the click area toggle = function() {} // TODO: how to select step chart? } else { toggle = $$.togglePoint } } else if (that.nodeName === 'path') { toggle = $$.togglePath } return toggle } ChartInternal.prototype.toggleShape = function(that, d, i) { var $$ = this, d3 = $$.d3, config = $$.config, shape = d3.select(that), isSelected = shape.classed(CLASS.SELECTED), toggle = $$.getToggle(that, d).bind($$) if (config.data_selection_enabled && config.data_selection_isselectable(d)) { if (!config.data_selection_multiple) { $$.main .selectAll( '.' + CLASS.shapes + (config.data_selection_grouped ? $$.getTargetSelectorSuffix(d.id) : '') ) .selectAll('.' + CLASS.shape) .each(function(d, i) { var shape = d3.select(this) if (shape.classed(CLASS.SELECTED)) { toggle(false, shape.classed(CLASS.SELECTED, false), d, i) } }) } shape.classed(CLASS.SELECTED, !isSelected) toggle(!isSelected, shape, d, i) } } ================================================ FILE: src/shape.bar.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { getBBox, isValue, isWithinBox } from './util' ChartInternal.prototype.initBar = function() { var $$ = this $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars) } ChartInternal.prototype.updateTargetsForBar = function(targets) { var $$ = this, config = $$.config, mainBars, mainBarEnter, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classFocus = $$.classFocus.bind($$) mainBars = $$.main .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) .attr('class', function(d) { return classChartBar(d) + classFocus(d) }) mainBarEnter = mainBars .enter() .append('g') .attr('class', classChartBar) .style('pointer-events', 'none') // Bars for each data mainBarEnter .append('g') .attr('class', classBars) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }) } ChartInternal.prototype.updateBar = function(durationForExit) { var $$ = this, barData = $$.barData.bind($$), classBar = $$.classBar.bind($$), initialOpacity = $$.initialOpacity.bind($$), color = function(d) { return $$.color(d.id) } var mainBar = $$.main .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data(barData) var mainBarEnter = mainBar .enter() .append('path') .attr('class', classBar) .style('stroke', color) .style('fill', color) $$.mainBar = mainBarEnter.merge(mainBar).style('opacity', initialOpacity) mainBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) } ChartInternal.prototype.redrawBar = function( drawBar, withTransition, transition ) { const $$ = this return [ (withTransition ? this.mainBar.transition(transition) : this.mainBar) .attr('d', drawBar) .style('stroke', this.color) .style('fill', this.color) .style('opacity', d => ($$.isTargetToShow(d.id) ? 1 : 0)) ] } ChartInternal.prototype.getBarW = function(axis, barTargetsNum) { var $$ = this, config = $$.config, w = typeof config.bar_width === 'number' ? config.bar_width : barTargetsNum ? (axis.tickInterval() * config.bar_width_ratio) / barTargetsNum : 0 return config.bar_width_max && w > config.bar_width_max ? config.bar_width_max : w } ChartInternal.prototype.getBars = function(i, id) { var $$ = this return (id ? $$.main.selectAll('.' + CLASS.bars + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.bar + (isValue(i) ? '-' + i : '')) } ChartInternal.prototype.expandBars = function(i, id, reset) { var $$ = this if (reset) { $$.unexpandBars() } $$.getBars(i, id).classed(CLASS.EXPANDED, true) } ChartInternal.prototype.unexpandBars = function(i) { var $$ = this $$.getBars(i).classed(CLASS.EXPANDED, false) } ChartInternal.prototype.generateDrawBar = function(barIndices, isSub) { var $$ = this, config = $$.config, getPoints = $$.generateGetBarPoints(barIndices, isSub) return function(d, i) { // 4 points that make a bar var points = getPoints(d, i) // switch points if axis is rotated, not applicable for sub chart var indexX = config.axis_rotated ? 1 : 0 var indexY = config.axis_rotated ? 0 : 1 var path = 'M ' + points[0][indexX] + ',' + points[0][indexY] + ' ' + 'L' + points[1][indexX] + ',' + points[1][indexY] + ' ' + 'L' + points[2][indexX] + ',' + points[2][indexY] + ' ' + 'L' + points[3][indexX] + ',' + points[3][indexY] + ' ' + 'z' return path } } ChartInternal.prototype.generateGetBarPoints = function(barIndices, isSub) { var $$ = this, axis = isSub ? $$.subXAxis : $$.xAxis, barTargetsNum = barIndices.__max__ + 1, barW = $$.getBarW(axis, barTargetsNum), barX = $$.getShapeX(barW, barTargetsNum, barIndices, !!isSub), barY = $$.getShapeY(!!isSub), barOffset = $$.getShapeOffset($$.isBarType, barIndices, !!isSub), barSpaceOffset = barW * ($$.config.bar_space / 2), yScale = isSub ? $$.getSubYScale : $$.getYScale return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = barOffset(d, i) || y0, // offset is for stacked bar chart posX = barX(d), posY = barY(d) // fix posY not to overflow opposite quadrant if ($$.config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0 } } posY -= y0 - offset // 4 points that make a bar return [ [posX + barSpaceOffset, offset], [posX + barSpaceOffset, posY], [posX + barW - barSpaceOffset, posY], [posX + barW - barSpaceOffset, offset] ] } } /** * Returns whether the data point is within the given bar shape. * * @param mouse * @param barShape * @return {boolean} */ ChartInternal.prototype.isWithinBar = function(mouse, barShape) { return isWithinBox(mouse, getBBox(barShape), 2) } ================================================ FILE: src/shape.line.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue, isFunction, isUndefined, isDefined } from './util' ChartInternal.prototype.initLine = function() { var $$ = this $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines) } ChartInternal.prototype.updateTargetsForLine = function(targets) { var $$ = this, config = $$.config, mainLines, mainLineEnter, classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$), classCircles = $$.classCircles.bind($$), classFocus = $$.classFocus.bind($$) mainLines = $$.main .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) .attr('class', function(d) { return classChartLine(d) + classFocus(d) }) mainLineEnter = mainLines .enter() .append('g') .attr('class', classChartLine) .style('opacity', 0) .style('pointer-events', 'none') // Lines for each data mainLineEnter.append('g').attr('class', classLines) // Areas mainLineEnter.append('g').attr('class', classAreas) // Circles for each data point on lines mainLineEnter.append('g').attr('class', function(d) { return $$.generateClass(CLASS.selectedCircles, d.id) }) mainLineEnter .append('g') .attr('class', classCircles) .style('cursor', function(d) { return config.data_selection_isselectable(d) ? 'pointer' : null }) // Update date for selected circles targets.forEach(function(t) { $$.main .selectAll('.' + CLASS.selectedCircles + $$.getTargetSelectorSuffix(t.id)) .selectAll('.' + CLASS.selectedCircle) .each(function(d) { d.value = t.values[d.index].value }) }) // MEMO: can not keep same color... //mainLineUpdate.exit().remove(); } ChartInternal.prototype.updateLine = function(durationForExit) { var $$ = this var mainLine = $$.main .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)) var mainLineEnter = mainLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color) $$.mainLine = mainLineEnter .merge(mainLine) .style('opacity', $$.initialOpacity.bind($$)) .style('shape-rendering', function(d) { return $$.isStepType(d) ? 'crispEdges' : '' }) .attr('transform', null) mainLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) } ChartInternal.prototype.redrawLine = function( drawLine, withTransition, transition ) { return [ (withTransition ? this.mainLine.transition(transition) : this.mainLine) .attr('d', drawLine) .style('stroke', this.color) .style('opacity', 1) ] } ChartInternal.prototype.generateDrawLine = function(lineIndices, isSub) { var $$ = this, config = $$.config, line = $$.d3.line(), getPoints = $$.generateGetLinePoints(lineIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, yValue = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)(d.value) } line = config.axis_rotated ? line.x(yValue).y(xValue) : line.x(xValue).y(yValue) if (!config.line_connectNull) { line = line.defined(function(d) { return d.value != null }) } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x = isSub ? $$.subX : $$.x, y = yScaleGetter.call($$, d.id), x0 = 0, y0 = 0, path if ($$.isLineType(d)) { if (config.data_regions[d.id]) { path = $$.lineWithRegions(values, x, y, config.data_regions[d.id]) } else { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values) } path = line.curve($$.getInterpolate(d))(values) } } else { if (values[0]) { x0 = x(values[0].x) y0 = y(values[0].value) } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0 } return path ? path : 'M 0 0' } } ChartInternal.prototype.generateGetLinePoints = function(lineIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, lineTargetsNum = lineIndices.__max__ + 1, x = $$.getShapeX(0, lineTargetsNum, lineIndices, !!isSub), y = $$.getShapeY(!!isSub), lineOffset = $$.getShapeOffset($$.isLineType, lineIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = lineOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d) // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0 } } // 1 point that marks the line position return [ [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)], // needed for compatibility [posX, posY - (y0 - offset)] // needed for compatibility ] } } ChartInternal.prototype.lineWithRegions = function(d, x, y, _regions) { var $$ = this, config = $$.config, prev = -1, i, j, s = 'M', sWithRegion, xp, yp, dx, dy, dd, diff, diffx2, xOffset = $$.isCategorized() ? 0.5 : 0, xValue, yValue, regions = [] function isWithinRegions(x, regions) { var i for (i = 0; i < regions.length; i++) { if (regions[i].start < x && x <= regions[i].end) { return true } } return false } // Check start/end of regions if (isDefined(_regions)) { for (i = 0; i < _regions.length; i++) { regions[i] = {} if (isUndefined(_regions[i].start)) { regions[i].start = d[0].x } else { regions[i].start = $$.isTimeSeries() ? $$.parseDate(_regions[i].start) : _regions[i].start } if (isUndefined(_regions[i].end)) { regions[i].end = d[d.length - 1].x } else { regions[i].end = $$.isTimeSeries() ? $$.parseDate(_regions[i].end) : _regions[i].end } } } // Set scales xValue = config.axis_rotated ? function(d) { return y(d.value) } : function(d) { return x(d.x) } yValue = config.axis_rotated ? function(d) { return x(d.x) } : function(d) { return y(d.value) } // Define svg generator function for region function generateM(points) { return ( 'M' + points[0][0] + ' ' + points[0][1] + ' ' + points[1][0] + ' ' + points[1][1] ) } if ($$.isTimeSeries()) { sWithRegion = function(d0, d1, j, diff) { var x0 = d0.x.getTime(), x_diff = d1.x - d0.x, xv0 = new Date(x0 + x_diff * j), xv1 = new Date(x0 + x_diff * (j + diff)), points if (config.axis_rotated) { points = [ [y(yp(j)), x(xv0)], [y(yp(j + diff)), x(xv1)] ] } else { points = [ [x(xv0), y(yp(j))], [x(xv1), y(yp(j + diff))] ] } return generateM(points) } } else { sWithRegion = function(d0, d1, j, diff) { var points if (config.axis_rotated) { points = [ [y(yp(j), true), x(xp(j))], [y(yp(j + diff), true), x(xp(j + diff))] ] } else { points = [ [x(xp(j), true), y(yp(j))], [x(xp(j + diff), true), y(yp(j + diff))] ] } return generateM(points) } } // Generate for (i = 0; i < d.length; i++) { // Draw as normal if (isUndefined(regions) || !isWithinRegions(d[i].x, regions)) { s += ' ' + xValue(d[i]) + ' ' + yValue(d[i]) } // Draw with region // TODO: Fix for horizotal charts else { xp = $$.getScale( d[i - 1].x + xOffset, d[i].x + xOffset, $$.isTimeSeries() ) yp = $$.getScale(d[i - 1].value, d[i].value) dx = x(d[i].x) - x(d[i - 1].x) dy = y(d[i].value) - y(d[i - 1].value) dd = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)) diff = 2 / dd diffx2 = diff * 2 for (j = diff; j <= 1; j += diffx2) { s += sWithRegion(d[i - 1], d[i], j, diff) } } prev = d[i].x } return s } ChartInternal.prototype.updateArea = function(durationForExit) { var $$ = this, d3 = $$.d3 var mainArea = $$.main .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)) var mainAreaEnter = mainArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity') return 0 }) $$.mainArea = mainAreaEnter .merge(mainArea) .style('opacity', $$.orgAreaOpacity) mainArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) } ChartInternal.prototype.redrawArea = function( drawArea, withTransition, transition ) { return [ (withTransition ? this.mainArea.transition(transition) : this.mainArea) .attr('d', drawArea) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) ] } ChartInternal.prototype.generateDrawArea = function(areaIndices, isSub) { var $$ = this, config = $$.config, area = $$.d3.area(), getPoints = $$.generateGetAreaPoints(areaIndices, isSub), yScaleGetter = isSub ? $$.getSubYScale : $$.getYScale, xValue = function(d) { return (isSub ? $$.subxx : $$.xx).call($$, d) }, value0 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[0][1] : yScaleGetter.call($$, d.id)($$.getAreaBaseValue(d.id)) }, value1 = function(d, i) { return config.data_groups.length > 0 ? getPoints(d, i)[1][1] : yScaleGetter.call($$, d.id)(d.value) } area = config.axis_rotated ? area .x0(value0) .x1(value1) .y(xValue) : area .x(xValue) .y0(config.area_above ? 0 : value0) .y1(value1) if (!config.line_connectNull) { area = area.defined(function(d) { return d.value !== null }) } return function(d) { var values = config.line_connectNull ? $$.filterRemoveNull(d.values) : d.values, x0 = 0, y0 = 0, path if ($$.isAreaType(d)) { if ($$.isStepType(d)) { values = $$.convertValuesToStep(values) } path = area.curve($$.getInterpolate(d))(values) } else { if (values[0]) { x0 = $$.x(values[0].x) y0 = $$.getYScale(d.id)(values[0].value) } path = config.axis_rotated ? 'M ' + y0 + ' ' + x0 : 'M ' + x0 + ' ' + y0 } return path ? path : 'M 0 0' } } ChartInternal.prototype.getAreaBaseValue = function() { return 0 } ChartInternal.prototype.generateGetAreaPoints = function(areaIndices, isSub) { // partial duplication of generateGetBarPoints var $$ = this, config = $$.config, areaTargetsNum = areaIndices.__max__ + 1, x = $$.getShapeX(0, areaTargetsNum, areaIndices, !!isSub), y = $$.getShapeY(!!isSub), areaOffset = $$.getShapeOffset($$.isAreaType, areaIndices, !!isSub), yScale = isSub ? $$.getSubYScale : $$.getYScale return function(d, i) { var y0 = yScale.call($$, d.id)(0), offset = areaOffset(d, i) || y0, // offset is for stacked area chart posX = x(d), posY = y(d) // fix posY not to overflow opposite quadrant if (config.axis_rotated) { if ((0 < d.value && posY < y0) || (d.value < 0 && y0 < posY)) { posY = y0 } } // 1 point that marks the area position return [ [posX, offset], [posX, posY - (y0 - offset)], [posX, posY - (y0 - offset)], // needed for compatibility [posX, offset] // needed for compatibility ] } } ChartInternal.prototype.updateCircle = function(cx, cy) { var $$ = this var mainCircle = $$.main .selectAll('.' + CLASS.circles) .selectAll('.' + CLASS.circle) .data($$.lineOrScatterOrStanfordData.bind($$)) var mainCircleEnter = mainCircle .enter() .append('circle') .attr('shape-rendering', $$.isStanfordGraphType() ? 'crispEdges' : '') .attr('class', $$.classCircle.bind($$)) .attr('cx', cx) .attr('cy', cy) .attr('r', $$.pointR.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ) $$.mainCircle = mainCircleEnter .merge(mainCircle) .style( 'opacity', $$.isStanfordGraphType() ? 1 : $$.initialOpacityForCircle.bind($$) ) mainCircle.exit().style('opacity', 0) } ChartInternal.prototype.redrawCircle = function( cx, cy, withTransition, transition ) { var $$ = this, selectedCircles = $$.main.selectAll('.' + CLASS.selectedCircle) return [ (withTransition ? $$.mainCircle.transition(transition) : $$.mainCircle) .style('opacity', this.opacityForCircle.bind($$)) .style( 'color', $$.isStanfordGraphType() ? $$.getStanfordPointColor.bind($$) : $$.color ) .attr('cx', cx) .attr('cy', cy), (withTransition ? selectedCircles.transition(transition) : selectedCircles) .attr('cx', cx) .attr('cy', cy) ] } ChartInternal.prototype.circleX = function(d) { return d.x || d.x === 0 ? this.x(d.x) : null } ChartInternal.prototype.updateCircleY = function() { var $$ = this, lineIndices, getPoints if ($$.config.data_groups.length > 0) { ;(lineIndices = $$.getShapeIndices($$.isLineType)), (getPoints = $$.generateGetLinePoints(lineIndices)) $$.circleY = function(d, i) { return getPoints(d, i)[0][1] } } else { $$.circleY = function(d) { return $$.getYScale(d.id)(d.value) } } } ChartInternal.prototype.getCircles = function(i, id) { var $$ = this return (id ? $$.main.selectAll('.' + CLASS.circles + $$.getTargetSelectorSuffix(id)) : $$.main ).selectAll('.' + CLASS.circle + (isValue(i) ? '-' + i : '')) } ChartInternal.prototype.expandCircles = function(i, id, reset) { var $$ = this, r = $$.pointExpandedR.bind($$) if (reset) { $$.unexpandCircles() } $$.getCircles(i, id) .classed(CLASS.EXPANDED, true) .attr('r', r) } ChartInternal.prototype.unexpandCircles = function(i) { var $$ = this, r = $$.pointR.bind($$) $$.getCircles(i) .filter(function() { return $$.d3.select(this).classed(CLASS.EXPANDED) }) .classed(CLASS.EXPANDED, false) .attr('r', r) } ChartInternal.prototype.pointR = function(d) { var $$ = this, config = $$.config return $$.isStepType(d) ? 0 : isFunction(config.point_r) ? config.point_r(d) : config.point_r } ChartInternal.prototype.pointExpandedR = function(d) { var $$ = this, config = $$.config if (config.point_focus_expand_enabled) { return isFunction(config.point_focus_expand_r) ? config.point_focus_expand_r(d) : config.point_focus_expand_r ? config.point_focus_expand_r : $$.pointR(d) * 1.75 } else { return $$.pointR(d) } } ChartInternal.prototype.pointSelectR = function(d) { var $$ = this, config = $$.config return isFunction(config.point_select_r) ? config.point_select_r(d) : config.point_select_r ? config.point_select_r : $$.pointR(d) * 4 } ChartInternal.prototype.isWithinCircle = function(that, r) { var d3 = this.d3, mouse = d3.mouse(that), d3_this = d3.select(that), cx = +d3_this.attr('cx'), cy = +d3_this.attr('cy') return Math.sqrt(Math.pow(cx - mouse[0], 2) + Math.pow(cy - mouse[1], 2)) < r } ChartInternal.prototype.isWithinStep = function(that, y) { return Math.abs(y - this.d3.mouse(that)[1]) < 30 } ================================================ FILE: src/shape.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isUndefined } from './util' ChartInternal.prototype.getShapeIndices = function(typeFilter) { var $$ = this, config = $$.config, indices = {}, i = 0, j, k $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)).forEach( function(d) { for (j = 0; j < config.data_groups.length; j++) { if (config.data_groups[j].indexOf(d.id) < 0) { continue } for (k = 0; k < config.data_groups[j].length; k++) { if (config.data_groups[j][k] in indices) { indices[d.id] = indices[config.data_groups[j][k]] break } } } if (isUndefined(indices[d.id])) { indices[d.id] = i++ } } ) ;(indices as any).__max__ = i - 1 return indices } ChartInternal.prototype.getShapeX = function( offset, targetsNum, indices, isSub ) { var $$ = this, scale = isSub ? $$.subX : $$.x return function(d) { var index = d.id in indices ? indices[d.id] : 0 return d.x || d.x === 0 ? scale(d.x) - offset * (targetsNum / 2 - index) : 0 } } ChartInternal.prototype.getShapeY = function(isSub) { const $$ = this return function(d) { const scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id) return scale( $$.isTargetNormalized(d.id) ? $$.getRatio('index', d, true) : d.value ) } } ChartInternal.prototype.getShapeOffset = function(typeFilter, indices, isSub) { var $$ = this, targets = $$.orderTargets( $$.filterTargetsToShow($$.data.targets.filter(typeFilter, $$)) ), targetIds = targets.map(function(t) { return t.id }) return function(d, i) { var scale = isSub ? $$.getSubYScale(d.id) : $$.getYScale(d.id), y0 = scale(0), offset = y0 targets.forEach(function(t) { const rowValues = $$.isStepType(d) ? $$.convertValuesToStep(t.values) : t.values const isTargetNormalized = $$.isTargetNormalized(d.id) const values = rowValues.map(v => isTargetNormalized ? $$.getRatio('index', v, true) : v.value ) if (t.id === d.id || indices[t.id] !== indices[d.id]) { return } if (targetIds.indexOf(t.id) < targetIds.indexOf(d.id)) { // check if the x values line up if (isUndefined(rowValues[i]) || +rowValues[i].x !== +d.x) { // "+" for timeseries // if not, try to find the value that does line up i = -1 rowValues.forEach(function(v, j) { const x1 = v.x.constructor === Date ? +v.x : v.x const x2 = d.x.constructor === Date ? +d.x : d.x if (x1 === x2) { i = j } }) } if (i in rowValues && rowValues[i].value * d.value >= 0) { offset += scale(values[i]) - y0 } } }) return offset } } ChartInternal.prototype.isWithinShape = function(that, d) { var $$ = this, shape = $$.d3.select(that), isWithin if (!$$.isTargetToShow(d.id)) { isWithin = false } else if (that.nodeName === 'circle') { isWithin = $$.isStepType(d) ? $$.isWithinStep(that, $$.getYScale(d.id)(d.value)) : $$.isWithinCircle(that, $$.pointSelectR(d) * 1.5) } else if (that.nodeName === 'path') { isWithin = shape.classed(CLASS.bar) ? $$.isWithinBar($$.d3.mouse(that), that) : true } return isWithin } ChartInternal.prototype.getInterpolate = function(d) { var $$ = this, d3 = $$.d3, types = { linear: d3.curveLinear, 'linear-closed': d3.curveLinearClosed, basis: d3.curveBasis, 'basis-open': d3.curveBasisOpen, 'basis-closed': d3.curveBasisClosed, bundle: d3.curveBundle, cardinal: d3.curveCardinal, 'cardinal-open': d3.curveCardinalOpen, 'cardinal-closed': d3.curveCardinalClosed, monotone: d3.curveMonotoneX, step: d3.curveStep, 'step-before': d3.curveStepBefore, 'step-after': d3.curveStepAfter }, type if ($$.isSplineType(d)) { type = types[$$.config.spline_interpolation_type] || types.cardinal } else if ($$.isStepType(d)) { type = types[$$.config.line_step_type] } else { type = types.linear } return type } ================================================ FILE: src/size.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue, ceil10, isDefined } from './util' ChartInternal.prototype.getCurrentWidth = function() { var $$ = this, config = $$.config return config.size_width ? config.size_width : $$.getParentWidth() } ChartInternal.prototype.getCurrentHeight = function() { var $$ = this, config = $$.config, h = config.size_height ? config.size_height : $$.getParentHeight() return h > 0 ? h : 320 / ($$.hasType('gauge') && !config.gauge_fullCircle ? 2 : 1) } ChartInternal.prototype.getCurrentPaddingTop = function() { var $$ = this, config = $$.config, padding = isValue(config.padding_top) ? config.padding_top : 0 if ($$.title && $$.title.node()) { padding += $$.getTitlePadding() } return padding } ChartInternal.prototype.getCurrentPaddingBottom = function() { var config = this.config return isValue(config.padding_bottom) ? config.padding_bottom : 0 } ChartInternal.prototype.getCurrentPaddingLeft = function(withoutRecompute) { var $$ = this, config = $$.config if (isValue(config.padding_left)) { return config.padding_left } else if (config.axis_rotated) { return !config.axis_x_show || config.axis_x_inner ? 1 : Math.max(ceil10($$.getAxisWidthByAxisId('x', withoutRecompute)), 40) } else if (!config.axis_y_show || config.axis_y_inner) { // && !config.axis_rotated return $$.axis.getYAxisLabelPosition().isOuter ? 30 : 1 } else { return ceil10($$.getAxisWidthByAxisId('y', withoutRecompute)) } } ChartInternal.prototype.getCurrentPaddingRight = function() { var $$ = this, config = $$.config, padding = 0, defaultPadding = 10, legendWidthOnRight = $$.isLegendRight ? $$.getLegendWidth() + 20 : 0 if (isValue(config.padding_right)) { padding = config.padding_right + 1 // 1 is needed not to hide tick line } else if (config.axis_rotated) { padding = defaultPadding + legendWidthOnRight } else if (!config.axis_y2_show || config.axis_y2_inner) { // && !config.axis_rotated padding = 2 + legendWidthOnRight + ($$.axis.getY2AxisLabelPosition().isOuter ? 20 : 0) } else { padding = ceil10($$.getAxisWidthByAxisId('y2')) + legendWidthOnRight } if ($$.colorScale && $$.colorScale.node()) { padding += $$.getColorScalePadding() } return padding } ChartInternal.prototype.getParentRectValue = function(key) { var parent = this.selectChart.node(), v while (parent && parent.tagName !== 'BODY') { try { v = parent.getBoundingClientRect()[key] } catch (e) { if (key === 'width') { // In IE in certain cases getBoundingClientRect // will cause an "unspecified error" v = parent.offsetWidth } } if (v) { break } parent = parent.parentNode } return v } ChartInternal.prototype.getParentWidth = function() { return this.getParentRectValue('width') } ChartInternal.prototype.getParentHeight = function() { var h = this.selectChart.style('height') return h.indexOf('px') > 0 ? +h.replace('px', '') : 0 } ChartInternal.prototype.getSvgLeft = function(withoutRecompute) { var $$ = this, config = $$.config, hasLeftAxisRect = config.axis_rotated || (!config.axis_rotated && !config.axis_y_inner), leftAxisClass = config.axis_rotated ? CLASS.axisX : CLASS.axisY, leftAxis = $$.main.select('.' + leftAxisClass).node(), svgRect = leftAxis && hasLeftAxisRect ? leftAxis.getBoundingClientRect() : { right: 0 }, chartRect = $$.selectChart.node().getBoundingClientRect(), hasArc = $$.hasArcType(), svgLeft = svgRect.right - chartRect.left - (hasArc ? 0 : $$.getCurrentPaddingLeft(withoutRecompute)) return svgLeft > 0 ? svgLeft : 0 } ChartInternal.prototype.getAxisWidthByAxisId = function(id, withoutRecompute) { var $$ = this, position = $$.axis.getLabelPositionById(id) return ( $$.axis.getMaxTickWidth(id, withoutRecompute) + (position.isInner ? 20 : 40) ) } ChartInternal.prototype.getHorizontalAxisHeight = function(axisId, isSubchart) { var $$ = this, config = $$.config, h = 30 if (axisId === 'x' && !(isDefined(isSubchart) && isSubchart ? config.subchart_axis_x_show : config.axis_x_show)) { return 8 } if (axisId === 'x' && config.axis_x_height) { return config.axis_x_height } if (axisId === 'y' && !config.axis_y_show) { return config.legend_show && !$$.isLegendRight && !$$.isLegendInset ? 10 : 1 } if (axisId === 'y2' && !config.axis_y2_show) { return $$.rotated_padding_top } // Calculate x axis height when tick rotated if (axisId === 'x' && !config.axis_rotated && config.axis_x_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_x_tick_rotate))) / 180) } // Calculate y axis height when tick rotated if (axisId === 'y' && config.axis_rotated && config.axis_y_tick_rotate) { h = 30 + $$.axis.getMaxTickWidth(axisId) * Math.cos((Math.PI * (90 - Math.abs(config.axis_y_tick_rotate))) / 180) } return ( h + ($$.axis.getLabelPositionById(axisId).isInner ? 0 : 10) + (axisId === 'y2' ? -10 : 0) ) } ================================================ FILE: src/stanford.ts ================================================ import { ChartInternal } from './core' import { isFunction, sanitise } from './util' ChartInternal.prototype.isStanfordGraphType = function() { var $$ = this return $$.config.data_type === 'stanford' } ChartInternal.prototype.initStanfordData = function() { var $$ = this, d3 = $$.d3, config = $$.config, target = $$.data.targets[0], epochs, maxEpochs, minEpochs // Make larger values appear on top target.values.sort(compareEpochs) // Get array of epochs epochs = target.values.map(a => a.epochs) minEpochs = !isNaN(config.stanford_scaleMin) ? config.stanford_scaleMin : d3.min(epochs) maxEpochs = !isNaN(config.stanford_scaleMax) ? config.stanford_scaleMax : d3.max(epochs) if (minEpochs > maxEpochs) { throw Error('Number of minEpochs has to be smaller than maxEpochs') } target.colors = isFunction(config.stanford_colors) ? config.stanford_colors : d3.interpolateHslLong(d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)) target.colorscale = d3 .scaleSequentialLog(target.colors) .domain([minEpochs, maxEpochs]) target.minEpochs = minEpochs target.maxEpochs = maxEpochs } ChartInternal.prototype.getStanfordPointColor = function(d) { var $$ = this, target = $$.data.targets[0] return target.colorscale(d.epochs) } // http://jsfiddle.net/Xotic750/KtzLq/ ChartInternal.prototype.getCentroid = function(points) { var area = getRegionArea(points) var x = 0, y = 0, i, j, f, point1, point2 for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i] point2 = points[j] f = point1.x * point2.y - point2.x * point1.y x += (point1.x + point2.x) * f y += (point1.y + point2.y) * f } f = area * 6 return { x: x / f, y: y / f } } ChartInternal.prototype.getStanfordTooltipTitle = function(d) { var $$ = this, labelX = $$.axis.getLabelText('x'), labelY = $$.axis.getLabelText('y') return ` ${labelX ? sanitise(labelX) : 'x'}${ d.x } ${labelY ? sanitise(labelY) : 'y'}${ d.value } ` } ChartInternal.prototype.countEpochsInRegion = function(region) { var $$ = this, target = $$.data.targets[0], total, count total = target.values.reduce( (accumulator, currentValue) => accumulator + Number(currentValue.epochs), 0 ) count = target.values.reduce((accumulator, currentValue) => { if (pointInRegion(currentValue, region)) { return accumulator + Number(currentValue.epochs) } return accumulator }, 0) return { value: count, percentage: count !== 0 ? ((count / total) * 100).toFixed(1) : 0 } } export var getRegionArea = function(points) { // thanks to: https://stackoverflow.com/questions/16282330/find-centerpoint-of-polygon-in-javascript var area = 0, i, j, point1, point2 for (i = 0, j = points.length - 1; i < points.length; j = i, i += 1) { point1 = points[i] point2 = points[j] area += point1.x * point2.y area -= point1.y * point2.x } area /= 2 return area } export var pointInRegion = function(point, region) { // thanks to: http://bl.ocks.org/bycoffe/5575904 // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html let xi, yi, yj, xj, intersect, x = point.x, y = point.value, inside = false for (let i = 0, j = region.length - 1; i < region.length; j = i++) { xi = region[i].x yi = region[i].y xj = region[j].x yj = region[j].y intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi if (intersect) { inside = !inside } } return inside } export var compareEpochs = function(a, b) { if (a.epochs < b.epochs) { return -1 } if (a.epochs > b.epochs) { return 1 } return 0 } ================================================ FILE: src/stanfordelements.ts ================================================ import { ChartInternal } from './chart-internal' import CLASS from './class' ChartInternal.prototype.initStanfordElements = function() { var $$ = this // Avoid blocking eventRect $$.stanfordElements = $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.stanfordElements) $$.stanfordElements.append('g').attr('class', CLASS.stanfordLines) $$.stanfordElements.append('g').attr('class', CLASS.stanfordTexts) $$.stanfordElements.append('g').attr('class', CLASS.stanfordRegions) } ChartInternal.prototype.updateStanfordElements = function(duration) { var $$ = this, main = $$.main, config = $$.config, stanfordLine, stanfordLineEnter, stanfordRegion, stanfordRegionEnter, stanfordText, stanfordTextEnter, xvCustom = $$.xvCustom.bind($$), yvCustom = $$.yvCustom.bind($$), countPointsInRegion = $$.countEpochsInRegion.bind($$) // Stanford-Lines stanfordLine = main .select('.' + CLASS.stanfordLines) .style('shape-rendering', 'geometricprecision') .selectAll('.' + CLASS.stanfordLine) .data(config.stanford_lines) // enter stanfordLineEnter = stanfordLine .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordLine + (d['class'] ? ' ' + d['class'] : '') }) stanfordLineEnter .append('line') .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 0) // update $$.stanfordLines = stanfordLineEnter.merge(stanfordLine) $$.stanfordLines .select('line') .transition() .duration(duration) .attr('x1', d => config.axis_rotated ? yvCustom(d, 'value_y1') : xvCustom(d, 'value_x1') ) .attr('x2', d => config.axis_rotated ? yvCustom(d, 'value_y2') : xvCustom(d, 'value_x2') ) .attr('y1', d => config.axis_rotated ? xvCustom(d, 'value_x1') : yvCustom(d, 'value_y1') ) .attr('y2', d => config.axis_rotated ? xvCustom(d, 'value_x2') : yvCustom(d, 'value_y2') ) .style('opacity', 1) // exit stanfordLine .exit() .transition() .duration(duration) .style('opacity', 0) .remove() // Stanford-Text stanfordText = main .select('.' + CLASS.stanfordTexts) .selectAll('.' + CLASS.stanfordText) .data(config.stanford_texts) // enter stanfordTextEnter = stanfordText .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordText + (d['class'] ? ' ' + d['class'] : '') }) stanfordTextEnter .append('text') .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .style('opacity', 0) // update $$.stanfordTexts = stanfordTextEnter.merge(stanfordText) $$.stanfordTexts .select('text') .transition() .duration(duration) .attr('x', d => (config.axis_rotated ? yvCustom(d, 'y') : xvCustom(d, 'x'))) .attr('y', d => (config.axis_rotated ? xvCustom(d, 'x') : yvCustom(d, 'y'))) .text(function(d) { return d.content }) .style('opacity', 1) // exit stanfordText .exit() .transition() .duration(duration) .style('opacity', 0) .remove() // Stanford-Regions stanfordRegion = main .select('.' + CLASS.stanfordRegions) .selectAll('.' + CLASS.stanfordRegion) .data(config.stanford_regions) // enter stanfordRegionEnter = stanfordRegion .enter() .append('g') .attr('class', function(d) { return CLASS.stanfordRegion + (d['class'] ? ' ' + d['class'] : '') }) stanfordRegionEnter .append('polygon') .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', 0) stanfordRegionEnter .append('text') .attr('x', d => $$.getCentroid(d.points).x) .attr('y', d => $$.getCentroid(d.points).y) .style('opacity', 0) // update $$.stanfordRegions = stanfordRegionEnter.merge(stanfordRegion) $$.stanfordRegions .select('polygon') .transition() .duration(duration) .attr('points', d => { return d.points .map(value => { return [ config.axis_rotated ? yvCustom(value, 'y') : xvCustom(value, 'x'), config.axis_rotated ? xvCustom(value, 'x') : yvCustom(value, 'y') ].join(',') }) .join(' ') }) .style('opacity', d => { return d.opacity ? d.opacity : 0.2 }) $$.stanfordRegions .select('text') .transition() .duration(duration) .attr('x', d => config.axis_rotated ? yvCustom($$.getCentroid(d.points), 'y') : xvCustom($$.getCentroid(d.points), 'x') ) .attr('y', d => config.axis_rotated ? xvCustom($$.getCentroid(d.points), 'x') : yvCustom($$.getCentroid(d.points), 'y') ) .text(function(d) { if (d.text) { var value, percentage, temp if ($$.isStanfordGraphType()) { temp = countPointsInRegion(d.points) value = temp.value percentage = temp.percentage } return d.text(value, percentage) } return '' }) .attr('text-anchor', 'middle') .attr('dominant-baseline', 'middle') .style('opacity', 1) // exit stanfordRegion .exit() .transition() .duration(duration) .style('opacity', 0) .remove() } ================================================ FILE: src/subchart.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isFunction } from './util' ChartInternal.prototype.initBrush = function(scale) { var $$ = this, d3 = $$.d3 // TODO: dynamically change brushY/brushX according to axis_rotated. $$.brush = ($$.config.axis_rotated ? d3.brushY() : d3.brushX()) .on('brush', function() { var event = d3.event.sourceEvent if (event && event.type === 'zoom') { return } $$.redrawForBrush() }) .on('end', function() { var event = d3.event.sourceEvent if (event && event.type === 'zoom') { return } if ($$.brush.empty() && event && event.type !== 'end') { $$.brush.clear() } }) $$.brush.updateExtent = function() { var range = this.scale.range(), extent if ($$.config.axis_rotated) { extent = [ [0, range[0]], [$$.width2, range[1]] ] } else { extent = [ [range[0], 0], [range[1], $$.height2] ] } this.extent(extent) return this } $$.brush.updateScale = function(scale) { this.scale = scale return this } $$.brush.update = function(scale) { this.updateScale(scale || $$.subX).updateExtent() $$.context.select('.' + CLASS.brush).call(this) } $$.brush.clear = function() { $$.context.select('.' + CLASS.brush).call($$.brush.move, null) } $$.brush.selection = function() { return d3.brushSelection($$.context.select('.' + CLASS.brush).node()) } $$.brush.selectionAsValue = function(selectionAsValue, withTransition) { var selection, brush if (selectionAsValue) { if ($$.context) { selection = [ this.scale(selectionAsValue[0]), this.scale(selectionAsValue[1]) ] brush = $$.context.select('.' + CLASS.brush) if (withTransition) { brush = brush.transition() } $$.brush.move(brush, selection) } return [] } selection = $$.brush.selection() || [0, 0] return [this.scale.invert(selection[0]), this.scale.invert(selection[1])] } $$.brush.empty = function() { var selection = $$.brush.selection() return !selection || selection[0] === selection[1] } return $$.brush.updateScale(scale) } ChartInternal.prototype.initSubchart = function() { var $$ = this, config = $$.config, context = ($$.context = $$.svg .append('g') .attr('transform', $$.getTranslate('context'))) // set style context.style('visibility', 'visible') // Define g for chart area context .append('g') .attr('clip-path', $$.clipPathForSubchart) .attr('class', CLASS.chart) // Define g for bar chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartBars) // Define g for line chart area context .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartLines) // Add extent rect for Brush context .append('g') .attr('clip-path', $$.clipPath) .attr('class', CLASS.brush) // ATTENTION: This must be called AFTER chart added // Add Axis $$.axes.subx = context .append('g') .attr('class', CLASS.axisX) .attr('transform', $$.getTranslate('subx')) .attr('clip-path', config.axis_rotated ? '' : $$.clipPathForXAxis) .style('visibility', config.subchart_axis_x_show ? 'visible' : 'hidden'); } ChartInternal.prototype.initSubchartBrush = function() { var $$ = this // Add extent rect for Brush $$.initBrush($$.subX).updateExtent() $$.context.select('.' + CLASS.brush).call($$.brush) } ChartInternal.prototype.updateTargetsForSubchart = function(targets) { var $$ = this, context = $$.context, config = $$.config, contextLineEnter, contextLine, contextBarEnter, contextBar, classChartBar = $$.classChartBar.bind($$), classBars = $$.classBars.bind($$), classChartLine = $$.classChartLine.bind($$), classLines = $$.classLines.bind($$), classAreas = $$.classAreas.bind($$) //-- Bar --// contextBar = context .select('.' + CLASS.chartBars) .selectAll('.' + CLASS.chartBar) .data(targets) contextBarEnter = contextBar .enter() .append('g') .style('opacity', 0) contextBarEnter.merge(contextBar).attr('class', classChartBar) // Bars for each data contextBarEnter.append('g').attr('class', classBars) //-- Line --// contextLine = context .select('.' + CLASS.chartLines) .selectAll('.' + CLASS.chartLine) .data(targets) contextLineEnter = contextLine .enter() .append('g') .style('opacity', 0) contextLineEnter.merge(contextLine).attr('class', classChartLine) // Lines for each data contextLineEnter.append('g').attr('class', classLines) // Area contextLineEnter.append('g').attr('class', classAreas) //-- Brush --// context .selectAll('.' + CLASS.brush + ' rect') .attr( config.axis_rotated ? 'width' : 'height', config.axis_rotated ? $$.width2 : $$.height2 ) } ChartInternal.prototype.updateBarForSubchart = function(durationForExit) { var $$ = this var contextBar = $$.context .selectAll('.' + CLASS.bars) .selectAll('.' + CLASS.bar) .data($$.barData.bind($$)) var contextBarEnter = contextBar .enter() .append('path') .attr('class', $$.classBar.bind($$)) .style('stroke', 'none') .style('fill', $$.color) contextBar .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextBar = contextBarEnter .merge(contextBar) .style('opacity', $$.initialOpacity.bind($$)) } ChartInternal.prototype.redrawBarForSubchart = function( drawBarOnSub, withTransition, duration ) { ;(withTransition ? this.contextBar.transition(Math.random().toString()).duration(duration) : this.contextBar ) .attr('d', drawBarOnSub) .style('opacity', 1) } ChartInternal.prototype.updateLineForSubchart = function(durationForExit) { var $$ = this var contextLine = $$.context .selectAll('.' + CLASS.lines) .selectAll('.' + CLASS.line) .data($$.lineData.bind($$)) var contextLineEnter = contextLine .enter() .append('path') .attr('class', $$.classLine.bind($$)) .style('stroke', $$.color) contextLine .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextLine = contextLineEnter .merge(contextLine) .style('opacity', $$.initialOpacity.bind($$)) } ChartInternal.prototype.redrawLineForSubchart = function( drawLineOnSub, withTransition, duration ) { ;(withTransition ? this.contextLine.transition(Math.random().toString()).duration(duration) : this.contextLine ) .attr('d', drawLineOnSub) .style('opacity', 1) } ChartInternal.prototype.updateAreaForSubchart = function(durationForExit) { var $$ = this, d3 = $$.d3 var contextArea = $$.context .selectAll('.' + CLASS.areas) .selectAll('.' + CLASS.area) .data($$.lineData.bind($$)) var contextAreaEnter = contextArea .enter() .append('path') .attr('class', $$.classArea.bind($$)) .style('fill', $$.color) .style('opacity', function() { $$.orgAreaOpacity = +d3.select(this).style('opacity') return 0 }) contextArea .exit() .transition() .duration(durationForExit) .style('opacity', 0) .remove() $$.contextArea = contextAreaEnter.merge(contextArea).style('opacity', 0) } ChartInternal.prototype.redrawAreaForSubchart = function( drawAreaOnSub, withTransition, duration ) { ;(withTransition ? this.contextArea.transition(Math.random().toString()).duration(duration) : this.contextArea ) .attr('d', drawAreaOnSub) .style('fill', this.color) .style('opacity', this.orgAreaOpacity) } ChartInternal.prototype.redrawSubchart = function( withSubchart, transitions, duration, durationForExit, areaIndices, barIndices, lineIndices ) { var $$ = this, d3 = $$.d3, drawAreaOnSub, drawBarOnSub, drawLineOnSub // reflect main chart to extent on subchart if zoomed if (d3.event && d3.event.type === 'zoom') { $$.brush.selectionAsValue($$.x.orgDomain()) } // update subchart elements if needed if (withSubchart) { // extent rect if (!$$.brush.empty()) { $$.brush.selectionAsValue($$.x.orgDomain()) } // setup drawer - MEMO: this must be called after axis updated drawAreaOnSub = $$.generateDrawArea(areaIndices, true) drawBarOnSub = $$.generateDrawBar(barIndices, true) drawLineOnSub = $$.generateDrawLine(lineIndices, true) $$.updateBarForSubchart(duration) $$.updateLineForSubchart(duration) $$.updateAreaForSubchart(duration) $$.redrawBarForSubchart(drawBarOnSub, duration, duration) $$.redrawLineForSubchart(drawLineOnSub, duration, duration) $$.redrawAreaForSubchart(drawAreaOnSub, duration, duration) } } ChartInternal.prototype.redrawForBrush = function() { var $$ = this, x = $$.x, d3 = $$.d3, s $$.redraw({ withTransition: false, withY: $$.config.zoom_rescale, withSubchart: false, withUpdateXDomain: true, withEventRect: false, withDimension: false }) // update zoom transation binded to event rect s = d3.event.selection || $$.brush.scale.range() $$.main .select('.' + CLASS.eventRect) .call( $$.zoom.transform, d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) ) $$.config.subchart_onbrush.call($$.api, x.orgDomain()) } ChartInternal.prototype.transformContext = function( withTransition, transitions ) { var $$ = this, subXAxis if (transitions && transitions.axisSubX) { subXAxis = transitions.axisSubX } else { subXAxis = $$.context.select('.' + CLASS.axisX) if (withTransition) { subXAxis = subXAxis.transition() } } $$.context.attr('transform', $$.getTranslate('context')) subXAxis.attr('transform', $$.getTranslate('subx')) } ChartInternal.prototype.getDefaultSelection = function() { var $$ = this, config = $$.config, selection = isFunction(config.axis_x_selection) ? config.axis_x_selection($$.getXDomain($$.data.targets)) : config.axis_x_selection if ($$.isTimeSeries()) { selection = [$$.parseDate(selection[0]), $$.parseDate(selection[1])] } return selection } ChartInternal.prototype.removeSubchart = function() { const $$ = this $$.brush = null $$.context.remove() $$.context = null } ================================================ FILE: src/text.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { getBBox } from './util' ChartInternal.prototype.initText = function() { var $$ = this $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.chartTexts) $$.mainText = $$.d3.selectAll([]) } ChartInternal.prototype.updateTargetsForText = function(targets) { var $$ = this, classChartText = $$.classChartText.bind($$), classTexts = $$.classTexts.bind($$), classFocus = $$.classFocus.bind($$) var mainText = $$.main .select('.' + CLASS.chartTexts) .selectAll('.' + CLASS.chartText) .data(targets) var mainTextEnter = mainText .enter() .append('g') .attr('class', classChartText) .style('opacity', 0) .style('pointer-events', 'none') mainTextEnter.append('g').attr('class', classTexts) mainTextEnter.merge(mainText).attr('class', function(d) { return classChartText(d) + classFocus(d) }) } ChartInternal.prototype.updateText = function( xForText, yForText, durationForExit ) { var $$ = this, config = $$.config, barOrLineData = $$.barOrLineData.bind($$), classText = $$.classText.bind($$) var mainText = $$.main .selectAll('.' + CLASS.texts) .selectAll('.' + CLASS.text) .data(barOrLineData) var mainTextEnter = mainText .enter() .append('text') .attr('class', classText) .attr('text-anchor', function(d) { return config.axis_rotated ? (d.value < 0 ? 'end' : 'start') : 'middle' }) .style('stroke', 'none') .attr('x', xForText) .attr('y', yForText) .style('fill', function(d) { return $$.color(d) }) .style('fill-opacity', 0) $$.mainText = mainTextEnter.merge(mainText).text(function(d, i, j) { return $$.dataLabelFormat(d.id)(d.value, d.id, i, j) }) mainText .exit() .transition() .duration(durationForExit) .style('fill-opacity', 0) .remove() } ChartInternal.prototype.redrawText = function( xForText, yForText, forFlow, withTransition, transition ) { return [ (withTransition ? this.mainText.transition(transition) : this.mainText) .attr('x', xForText) .attr('y', yForText) .style('fill', this.color) .style('fill-opacity', forFlow ? 0 : this.opacityForText.bind(this)) ] } ChartInternal.prototype.getTextRect = function(text, cls, element) { var dummy = this.d3 .select('body') .append('div') .classed('c3', true), svg = dummy .append('svg') .style('visibility', 'hidden') .style('position', 'fixed') .style('top', 0) .style('left', 0), font = this.d3.select(element).style('font'), rect svg .selectAll('.dummy') .data([text]) .enter() .append('text') .classed(cls ? cls : '', true) .style('font', font) .text(text) .each(function() { rect = getBBox(this) }) dummy.remove() return rect } ChartInternal.prototype.generateXYForText = function( areaIndices, barIndices, lineIndices, forX ) { var $$ = this, getAreaPoints = $$.generateGetAreaPoints(areaIndices, false), getBarPoints = $$.generateGetBarPoints(barIndices, false), getLinePoints = $$.generateGetLinePoints(lineIndices, false), getter = forX ? $$.getXForText : $$.getYForText return function(d, i) { var getPoints = $$.isAreaType(d) ? getAreaPoints : $$.isBarType(d) ? getBarPoints : getLinePoints return getter.call($$, getPoints(d, i), d, this) } } ChartInternal.prototype.getXForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), xPos, padding if ($$.config.axis_rotated) { padding = $$.isBarType(d) ? 4 : 6 xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1) } else { xPos = $$.hasType('bar') ? (points[2][0] + points[0][0]) / 2 : points[0][0] } // show labels regardless of the domain if value is null if (d.value === null) { if (xPos > $$.width) { xPos = $$.width - box.width } else if (xPos < 0) { xPos = 4 } } return xPos } ChartInternal.prototype.getYForText = function(points, d, textElement) { var $$ = this, box = getBBox(textElement), yPos if ($$.config.axis_rotated) { yPos = (points[0][0] + points[2][0] + box.height * 0.6) / 2 } else { yPos = points[2][1] if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { yPos += box.height if ($$.isBarType(d) && $$.isSafari()) { yPos -= 3 } else if (!$$.isBarType(d) && $$.isChrome()) { yPos += 3 } } else { yPos += $$.isBarType(d) ? -3 : -6 } } // show labels regardless of the domain if value is null if (d.value === null && !$$.config.axis_rotated) { if (yPos < box.height) { yPos = box.height } else if (yPos > this.height) { yPos = this.height - 4 } } return yPos } ================================================ FILE: src/title.ts ================================================ import { ChartInternal } from './core' ChartInternal.prototype.initTitle = function() { var $$ = this $$.title = $$.svg .append('text') .text($$.config.title_text) .attr('class', $$.CLASS.title) } ChartInternal.prototype.redrawTitle = function() { var $$ = this $$.title.attr('x', $$.xForTitle.bind($$)).attr('y', $$.yForTitle.bind($$)) } ChartInternal.prototype.xForTitle = function() { var $$ = this, config = $$.config, position = config.title_position || 'left', x if (position.indexOf('right') >= 0) { x = $$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width - config.title_padding.right } else if (position.indexOf('center') >= 0) { x = Math.max( ($$.currentWidth - $$.getTextRect( $$.title.node().textContent, $$.CLASS.title, $$.title.node() ).width) / 2, 0 ) } else { // left x = config.title_padding.left } return x } ChartInternal.prototype.yForTitle = function() { var $$ = this return ( $$.config.title_padding.top + $$.getTextRect($$.title.node().textContent, $$.CLASS.title, $$.title.node()) .height ) } ChartInternal.prototype.getTitlePadding = function() { var $$ = this return $$.yForTitle() + $$.config.title_padding.bottom } ================================================ FILE: src/tooltip.ts ================================================ import CLASS from './class' import { ChartInternal } from './core' import { isValue, isFunction, isArray, isString, sanitise } from './util' ChartInternal.prototype.initTooltip = function() { var $$ = this, config = $$.config, i $$.tooltip = $$.selectChart .style('position', 'relative') .append('div') .attr('class', CLASS.tooltipContainer) .style('position', 'absolute') .style('pointer-events', 'none') .style('display', 'none') // Show tooltip if needed if (config.tooltip_init_show) { if ($$.isTimeSeries() && isString(config.tooltip_init_x)) { config.tooltip_init_x = $$.parseDate(config.tooltip_init_x) for (i = 0; i < $$.data.targets[0].values.length; i++) { if ($$.data.targets[0].values[i].x - config.tooltip_init_x === 0) { break } } config.tooltip_init_x = i } $$.tooltip.html( config.tooltip_contents.call( $$, $$.data.targets.map(function(d) { return $$.addName(d.values[config.tooltip_init_x]) }), $$.axis.getXAxisTickFormat(), $$.getYFormat($$.hasArcType()), $$.color ) ) $$.tooltip .style('top', config.tooltip_init_position.top) .style('left', config.tooltip_init_position.left) .style('display', 'block') } } ChartInternal.prototype.getTooltipSortFunction = function() { var $$ = this, config = $$.config if (config.data_groups.length === 0 || config.tooltip_order !== undefined) { // if data are not grouped or if an order is specified // for the tooltip values we sort them by their values var order = config.tooltip_order if (order === undefined) { order = config.data_order } var valueOf = function(obj) { return obj ? obj.value : null } // if data are not grouped, we sort them by their value if (isString(order) && order.toLowerCase() === 'asc') { return function(a, b) { return valueOf(a) - valueOf(b) } } else if (isString(order) && order.toLowerCase() === 'desc') { return function(a, b) { return valueOf(b) - valueOf(a) } } else if (isFunction(order)) { // if the function is from data_order we need // to wrap the returned function in order to format // the sorted value to the expected format var sortFunction = order if (config.tooltip_order === undefined) { sortFunction = function(a, b) { return order( a ? { id: a.id, values: [a] } : null, b ? { id: b.id, values: [b] } : null ) } } return sortFunction } else if (isArray(order)) { return function(a, b) { return order.indexOf(a.id) - order.indexOf(b.id) } } } else { // if data are grouped, we follow the order of grouped targets var ids = $$.orderTargets($$.data.targets).map(function(i) { return i.id }) // if it was either asc or desc we need to invert the order // returned by orderTargets if ($$.isOrderAsc() || $$.isOrderDesc()) { ids = ids.reverse() } return function(a, b) { return ids.indexOf(a.id) - ids.indexOf(b.id) } } } ChartInternal.prototype.getTooltipContent = function( d, defaultTitleFormat, defaultValueFormat, color ) { var $$ = this, config = $$.config, titleFormat = config.tooltip_format_title || defaultTitleFormat, nameFormat = config.tooltip_format_name || function(name) { return name }, text, i, title, value, name, bgcolor var valueFormat = config.tooltip_format_value if (!valueFormat) { valueFormat = $$.isTargetNormalized(d.id) ? (v, ratio) => `${(ratio * 100).toFixed(2)}%` : defaultValueFormat } var tooltipSortFunction = this.getTooltipSortFunction() if (tooltipSortFunction) { d.sort(tooltipSortFunction) } for (i = 0; i < d.length; i++) { if (!(d[i] && (d[i].value || d[i].value === 0))) { continue } if ($$.isStanfordGraphType()) { // Custom tooltip for stanford plots if (!text) { title = $$.getStanfordTooltipTitle(d[i]) text = "" + title } bgcolor = $$.getStanfordPointColor(d[i]) name = sanitise(config.data_epochs) // Epochs key name value = d[i].epochs } else { // Regular tooltip if (!text) { title = sanitise(titleFormat ? titleFormat(d[i].x, d[i].index) : d[i].x) text = "
" + (title || title === 0 ? "' : '') } value = sanitise( valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index, d) ) if (value !== undefined) { // Skip elements when their name is set to null if (d[i].name === null) { continue } name = sanitise(nameFormat(d[i].name, d[i].ratio, d[i].id, d[i].index)) bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id) } } if (value !== undefined) { text += "" text += "' text += "' text += '' } } return text + '
" + title + '
" + name + '" + value + '
' } ChartInternal.prototype.tooltipPosition = function( dataToShow, tWidth, tHeight, element ) { var $$ = this, config = $$.config, d3 = $$.d3 var svgLeft, tooltipLeft, tooltipRight, tooltipTop, chartRight var forArc = $$.hasArcType(), mouse = d3.mouse(element) // Determin tooltip position if (forArc) { tooltipLeft = ($$.width - ($$.isLegendRight ? $$.getLegendWidth() : 0)) / 2 + mouse[0] tooltipTop = ($$.hasType('gauge') ? $$.height : $$.height / 2) + mouse[1] + 20 } else { svgLeft = $$.getSvgLeft(true) if (config.axis_rotated) { tooltipLeft = svgLeft + mouse[0] + 100 tooltipRight = tooltipLeft + tWidth chartRight = $$.currentWidth - $$.getCurrentPaddingRight() tooltipTop = $$.x(dataToShow[0].x) + 20 } else { tooltipLeft = svgLeft + $$.getCurrentPaddingLeft(true) + $$.x(dataToShow[0].x) + 20 tooltipRight = tooltipLeft + tWidth chartRight = svgLeft + $$.currentWidth - $$.getCurrentPaddingRight() tooltipTop = mouse[1] + 15 } if (tooltipRight > chartRight) { // 20 is needed for Firefox to keep tooltip width tooltipLeft -= tooltipRight - chartRight + 20 } if (tooltipTop + tHeight > $$.currentHeight) { tooltipTop -= tHeight + 30 } } if (tooltipTop < 0) { tooltipTop = 0 } return { top: tooltipTop, left: tooltipLeft } } ChartInternal.prototype.showTooltip = function(selectedData, element) { var $$ = this, config = $$.config var tWidth, tHeight, position var forArc = $$.hasArcType(), dataToShow = selectedData.filter(function(d) { return d && isValue(d.value) }), positionFunction = config.tooltip_position || ChartInternal.prototype.tooltipPosition if (dataToShow.length === 0 || !config.tooltip_show) { $$.hideTooltip() return } $$.tooltip .html( config.tooltip_contents.call( $$, selectedData, $$.axis.getXAxisTickFormat(), $$.getYFormat(forArc), $$.color ) ) .style('display', 'block') // Get tooltip dimensions tWidth = $$.tooltip.property('offsetWidth') tHeight = $$.tooltip.property('offsetHeight') position = positionFunction.call(this, dataToShow, tWidth, tHeight, element) // Set tooltip $$.tooltip .style('top', position.top + 'px') .style('left', position.left + 'px') } ChartInternal.prototype.hideTooltip = function() { this.tooltip.style('display', 'none') } ================================================ FILE: src/type.ts ================================================ import { ChartInternal } from './core' import { isString } from './util' ChartInternal.prototype.setTargetType = function(targetIds, type) { var $$ = this, config = $$.config $$.mapToTargetIds(targetIds).forEach(function(id) { $$.withoutFadeIn[id] = type === config.data_types[id] config.data_types[id] = type }) if (!targetIds) { config.data_type = type } } ChartInternal.prototype.hasType = function(type, targets) { var $$ = this, types = $$.config.data_types, has = false targets = targets || $$.data.targets if (targets && targets.length) { targets.forEach(function(target) { var t = types[target.id] if ((t && t.indexOf(type) >= 0) || (!t && type === 'line')) { has = true } }) } else if (Object.keys(types).length) { Object.keys(types).forEach(function(id) { if (types[id] === type) { has = true } }) } else { has = $$.config.data_type === type } return has } ChartInternal.prototype.hasArcType = function(targets) { return ( this.hasType('pie', targets) || this.hasType('donut', targets) || this.hasType('gauge', targets) ) } ChartInternal.prototype.isLineType = function(d) { var config = this.config, id = isString(d) ? d : d.id return ( !config.data_types[id] || ['line', 'spline', 'area', 'area-spline', 'step', 'area-step'].indexOf( config.data_types[id] ) >= 0 ) } ChartInternal.prototype.isStepType = function(d) { var id = isString(d) ? d : d.id return ['step', 'area-step'].indexOf(this.config.data_types[id]) >= 0 } ChartInternal.prototype.isSplineType = function(d) { var id = isString(d) ? d : d.id return ['spline', 'area-spline'].indexOf(this.config.data_types[id]) >= 0 } ChartInternal.prototype.isAreaType = function(d) { var id = isString(d) ? d : d.id return ( ['area', 'area-spline', 'area-step'].indexOf(this.config.data_types[id]) >= 0 ) } ChartInternal.prototype.isBarType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'bar' } ChartInternal.prototype.isScatterType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'scatter' } ChartInternal.prototype.isStanfordType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'stanford' } ChartInternal.prototype.isPieType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'pie' } ChartInternal.prototype.isGaugeType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'gauge' } ChartInternal.prototype.isDonutType = function(d) { var id = isString(d) ? d : d.id return this.config.data_types[id] === 'donut' } ChartInternal.prototype.isArcType = function(d) { return this.isPieType(d) || this.isDonutType(d) || this.isGaugeType(d) } ChartInternal.prototype.lineData = function(d) { return this.isLineType(d) ? [d] : [] } ChartInternal.prototype.arcData = function(d) { return this.isArcType(d.data) ? [d] : [] } /* not used function scatterData(d) { return isScatterType(d) ? d.values : []; } */ ChartInternal.prototype.barData = function(d) { return this.isBarType(d) ? d.values : [] } ChartInternal.prototype.lineOrScatterOrStanfordData = function(d) { return this.isLineType(d) || this.isScatterType(d) || this.isStanfordType(d) ? d.values : [] } ChartInternal.prototype.barOrLineData = function(d) { return this.isBarType(d) || this.isLineType(d) ? d.values : [] } ================================================ FILE: src/ua.ts ================================================ import { ChartInternal } from './core' ChartInternal.prototype.isSafari = function() { var ua = window.navigator.userAgent return ua.indexOf('Safari') >= 0 && ua.indexOf('Chrome') < 0 } ChartInternal.prototype.isChrome = function() { var ua = window.navigator.userAgent return ua.indexOf('Chrome') >= 0 } ================================================ FILE: src/util.ts ================================================ export var asHalfPixel = function(n) { return Math.ceil(n) + 0.5 } export var ceil10 = function(v) { return Math.ceil(v / 10) * 10 } export var diffDomain = function(d) { return d[1] - d[0] } export var getOption = function(options, key, defaultValue) { return isDefined(options[key]) ? options[key] : defaultValue } export var getPathBox = function(path) { var box = getBBox(path), items = [path.pathSegList.getItem(0), path.pathSegList.getItem(1)], minX = items[0].x, minY = Math.min(items[0].y, items[1].y) return { x: minX, y: minY, width: box.width, height: box.height } } export var getBBox = function(element) { try { return element.getBBox() } catch (ignore) { // Firefox will throw an exception if getBBox() is called whereas the // element is rendered with display:none // See https://github.com/c3js/c3/issues/2692 // The previous code was using `getBoundingClientRect` which was returning // everything at 0 in this case so let's reproduce this behavior here. return { x: 0, y: 0, width: 0, height: 0 } } } export var hasValue = function(dict, value) { var found = false Object.keys(dict).forEach(function(key) { if (dict[key] === value) { found = true } }) return found } export var isArray = function(o) { return Array.isArray(o) } export var isDefined = function(v) { return typeof v !== 'undefined' } export var isEmpty = function(o) { return ( typeof o === 'undefined' || o === null || (isString(o) && o.length === 0) || (typeof o === 'object' && Object.keys(o).length === 0) ) } export var isFunction = function(o) { return typeof o === 'function' } export var isNumber = function(o) { return typeof o === 'number' } export var isString = function(o) { return typeof o === 'string' } export var isUndefined = function(v) { return typeof v === 'undefined' } export var isValue = function(v) { return v || v === 0 } export var notEmpty = function(o) { return !isEmpty(o) } export var sanitise = function(str) { return typeof str === 'string' ? str.replace(//g, '>') : str } export var flattenArray = function(arr) { return Array.isArray(arr) ? [].concat(...arr) : [] } /** * Returns whether the point is within the given box. * * @param {Array} point An [x,y] coordinate * @param {Object} box An object with {x, y, width, height} keys * @param {Number} sensitivity An offset to ease check on very small boxes */ export var isWithinBox = function(point, box, sensitivity = 0) { const xStart = box.x - sensitivity const xEnd = box.x + box.width + sensitivity const yStart = box.y + box.height + sensitivity const yEnd = box.y - sensitivity return ( xStart < point[0] && point[0] < xEnd && yEnd < point[1] && point[1] < yStart ) } /** * Returns Internet Explorer version number (or false if no Internet Explorer used). * * @param string agent Optional parameter to specify user agent */ export var getIEVersion = function(agent?: string) { // https://stackoverflow.com/questions/19999388/check-if-user-is-using-ie if (typeof agent === 'undefined') { agent = window.navigator.userAgent } let pos = agent.indexOf('MSIE ') // up to IE10 if (pos > 0) { return parseInt(agent.substring(pos + 5, agent.indexOf('.', pos)), 10) } pos = agent.indexOf('Trident/') // IE11 if (pos > 0) { pos = agent.indexOf('rv:') return parseInt(agent.substring(pos + 3, agent.indexOf('.', pos)), 10) } return false } /** * Returns whether the used browser is Internet Explorer. * * @param version Optional parameter to specify IE version */ export var isIE = function(version?: number) { const ver = getIEVersion() if (typeof version === 'undefined') { return !!ver } return version === ver } ================================================ FILE: src/zoom.ts ================================================ import { ChartInternal } from './core' import CLASS from './class' ChartInternal.prototype.initZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, startEvent $$.zoom = d3 .zoom() .on('start', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent if (e && e.type === 'brush') { return } startEvent = e config.zoom_onzoomstart.call($$.api, e) }) .on('zoom', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent if (e && e.type === 'brush') { return } $$.redrawForZoom() config.zoom_onzoom.call($$.api, $$.x.orgDomain()) }) .on('end', function() { if (config.zoom_type !== 'scroll') { return } var e = d3.event.sourceEvent if (e && e.type === 'brush') { return } // if click, do nothing. otherwise, click interaction will be canceled. if ( e && startEvent.clientX === e.clientX && startEvent.clientY === e.clientY ) { return } config.zoom_onzoomend.call($$.api, $$.x.orgDomain()) }) $$.zoom.updateDomain = function() { if (d3.event && d3.event.transform) { if (config.axis_rotated && config.zoom_type === 'scroll' && d3.event.sourceEvent.type === 'mousemove') { // we're moving the mouse in a rotated chart with zoom = "scroll", so we need rescaleY (i.e. vertical) $$.x.domain(d3.event.transform.rescaleY($$.subX).domain()); } else { $$.x.domain(d3.event.transform.rescaleX($$.subX).domain()); } } return this } $$.zoom.updateExtent = function() { this.scaleExtent([1, Infinity]) .translateExtent([ [0, 0], [$$.width, $$.height] ]) .extent([ [0, 0], [$$.width, $$.height] ]) return this } $$.zoom.update = function() { return this.updateExtent().updateDomain() } return $$.zoom.updateExtent() } ChartInternal.prototype.zoomTransform = function(range) { var $$ = this, s = [$$.x(range[0]), $$.x(range[1])] return $$.d3.zoomIdentity.scale($$.width / (s[1] - s[0])).translate(-s[0], 0) } ChartInternal.prototype.initDragZoom = function() { const $$ = this const d3 = $$.d3 const config = $$.config const context = ($$.context = $$.svg) const brushXPos = $$.margin.left + 20.5 const brushYPos = $$.margin.top + 0.5 if (!(config.zoom_type === 'drag' && config.zoom_enabled)) { return } const getZoomedDomain = selection => selection && selection.map(x => $$.x.invert(x)) const brush = ($$.dragZoomBrush = d3 .brushX() .on('start', () => { $$.api.unzoom() $$.svg.select('.' + CLASS.dragZoom).classed('disabled', false) config.zoom_onzoomstart.call($$.api, d3.event.sourceEvent) }) .on('brush', () => { config.zoom_onzoom.call($$.api, getZoomedDomain(d3.event.selection)) }) .on('end', () => { if (d3.event.selection == null) { return } const zoomedDomain = getZoomedDomain(d3.event.selection) if (!config.zoom_disableDefaultBehavior) { $$.api.zoom(zoomedDomain) } $$.svg.select('.' + CLASS.dragZoom).classed('disabled', true) config.zoom_onzoomend.call($$.api, zoomedDomain) })) context .append('g') .classed(CLASS.dragZoom, true) .attr('clip-path', $$.clipPath) .attr('transform', 'translate(' + brushXPos + ',' + brushYPos + ')') .call(brush) } ChartInternal.prototype.getZoomDomain = function() { var $$ = this, config = $$.config, d3 = $$.d3, min = d3.min([$$.orgXDomain[0], config.zoom_x_min]), max = d3.max([$$.orgXDomain[1], config.zoom_x_max]) return [min, max] } ChartInternal.prototype.redrawForZoom = function() { var $$ = this, d3 = $$.d3, config = $$.config, zoom = $$.zoom, x = $$.x if (!config.zoom_enabled) { return } if ($$.filterTargetsToShow($$.data.targets).length === 0) { return } zoom.update() if (config.zoom_disableDefaultBehavior) { return } if ($$.isCategorized() && x.orgDomain()[0] === $$.orgXDomain[0]) { x.domain([$$.orgXDomain[0] - 1e-10, x.orgDomain()[1]]) } $$.redraw({ withTransition: false, withY: config.zoom_rescale, withSubchart: false, withEventRect: false, withDimension: false }) if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'mousemove') { $$.cancelClick = true } } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "module": "ESNext", "strict": false, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src", "spec"] }