diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:26:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 15:26:01 +0000 |
commit | ef03469fec14f1f0358b690934fc173d744f4e7d (patch) | |
tree | 8be439d7b2f1d7c8283b745919b9e66481a950e7 /debian/missing-sources/epoch/src/core/chart.coffee | |
parent | Adding upstream version 5.6.0. (diff) | |
download | knot-resolver-debian/5.6.0-1.tar.xz knot-resolver-debian/5.6.0-1.zip |
Adding debian version 5.6.0-1.debian/5.6.0-1debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | debian/missing-sources/epoch/src/core/chart.coffee | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/debian/missing-sources/epoch/src/core/chart.coffee b/debian/missing-sources/epoch/src/core/chart.coffee new file mode 100644 index 0000000..068335a --- /dev/null +++ b/debian/missing-sources/epoch/src/core/chart.coffee @@ -0,0 +1,361 @@ +# The base class for all charts in Epoch. Defines chart dimensions, keeps a reference +# of the chart's containing elements. And defines core method for handling data and +# drawing. +class Epoch.Chart.Base extends Epoch.Events + defaults = + width: 320 + height: 240 + dataFormat: null + + optionListeners = + 'option:width': 'dimensionsChanged' + 'option:height': 'dimensionsChanged' + 'layer:shown': 'layerChanged' + 'layer:hidden': 'layerChanged' + + # Creates a new base chart. + # @param [Object] options Options to set for this chart. + # @option options [Integer] width Sets an explicit width for the visualization. + # @option options [Integer] height Sets an explicit height for the visualization. + # @option options [Object, String] dataFormat Specific data format for the chart. + # @option options [Object] model Data model for the chart. + constructor: (@options={}) -> + super() + + if @options.model + if @options.model.hasData()? + @setData(@options.model.getData(@options.type, @options.dataFormat)) + else + @setData(@options.data or []) + @options.model.on 'data:updated', => @setDataFromModel() + else + @setData(@options.data or []) + + if @options.el? + @el = d3.select(@options.el) + + @width = @options.width + @height = @options.height + + if @el? + @width = @el.width() unless @width? + @height = @el.height() unless @height? + else + @width = defaults.width unless @width? + @height = defaults.height unless @height? + @el = d3.select(document.createElement('DIV')) + .attr('width', @width) + .attr('height', @height) + + @onAll optionListeners + + # @return [Object] A copy of this charts options. + _getAllOptions: -> + Epoch.Util.defaults({}, @options) + + # Chart option accessor. + # @param key Name of the option to fetch. Can be hierarchical, e.g. 'margins.left' + # @return The requested option if found, undefined otherwise. + _getOption: (key) -> + parts = key.split('.') + scope = @options + while parts.length and scope? + subkey = parts.shift() + scope = scope[subkey] + scope + + # Chart option mutator. + # @param key Name of the option to fetch. Can be hierarchical, e.g. 'margins.top' + # @param value Value to set for the option. + # @event option:`key` Triggers an option event with the given key being set. + _setOption: (key, value) -> + parts = key.split('.') + scope = @options + while parts.length + subkey = parts.shift() + if parts.length == 0 + scope[subkey] = arguments[1] + @trigger "option:#{arguments[0]}" + return + unless scope[subkey]? + scope[subkey] = {} + scope = scope[subkey] + + # Sets all options given an object of mixed hierarchical keys and nested objects. + # @param [Object] options Options to set. + # @event option:* Triggers an option event for each key that was set + _setManyOptions: (options, prefix='') -> + for own key, value of options + if Epoch.isObject(value) + @_setManyOptions value, "#{prefix + key}." + else + @_setOption prefix + key, value + + # General accessor / mutator for chart options. + # + # @overload option() + # Fetches chart options. + # @return a copy of this chart's options. + # + # @overload option(name) + # Fetches the value the option with the given name. + # @param [String] name Name of the option to fetch. Can be hierarchical, e.g. <code>'margins.left'</code> + # @return The requested option if found, <code>undefined</code> otherwise. + # + # @overload option(name, value) + # Sets an option and triggers the associated event. + # @param [String] name Name of the option to fetch. Can be hierarchical, e.g. 'margins.top' + # @param value Value to set for the option. + # @event option:`name` Triggers an option event with the given key being set. + # + # @overload option(options) + # Sets multiple options at once. + # @param [Object] options Options to set for the chart. + # @event option:* Triggers an option event for each key that was set. + option: -> + if arguments.length == 0 + @_getAllOptions() + else if arguments.length == 1 and Epoch.isString(arguments[0]) + @_getOption arguments[0] + else if arguments.length == 2 and Epoch.isString(arguments[0]) + @_setOption arguments[0], arguments[1] + else if arguments.length == 1 and Epoch.isObject(arguments[0]) + @_setManyOptions arguments[0] + + # Retrieves and sets data from the chart's model + setDataFromModel: -> + prepared = @_prepareData @options.model.getData(@options.type, @options.dataFormat) + @data = @_annotateLayers(prepared) + @draw() + + # Set the initial data for the chart. + # @param data Data to initially set for the given chart. The data format can vary + # from chart to chart. The base class assumes that the data provided will be an + # array of layers. + setData: (data, options={}) -> + prepared = @_prepareData (@rawData = @_formatData(data)) + @data = @_annotateLayers(prepared) + + # Performs post formatted data preparation. + # @param data Data to prepare before setting. + # @return The prepared data. + _prepareData: (data) -> data + + # Performs data formatting before setting the charts data + # @param data Data to be formatted. + # @return The chart specific formatted data. + _formatData: (data) -> + Epoch.Data.formatData(data, @options.type, @options.dataFormat) + + # Annotates data to add class names, categories, and initial visibility states + _annotateLayers: (data) -> + category = 1 + for layer in data + classes = ['layer'] + classes.push "category#{category}" + layer.category = category + layer.visible = true + classes.push(Epoch.Util.dasherize layer.label) if layer.label? + layer.className = classes.join(' ') + category++ + return data + + # Finds a layer in the chart's current data that has the given label or index. + # @param [String, Number] labelOrIndex The label or index of the layer to find. + _findLayer: (labelOrIndex) -> + layer = null + if Epoch.isString(labelOrIndex) + for l in @data + if l.label == labelOrIndex + layer = l + break + else if Epoch.isNumber(labelOrIndex) + index = parseInt(labelOrIndex) + layer = @data[index] unless index < 0 or index >= @data.length + return layer + + # Instructs the chart that a data layer should be displayed. + # @param [String, Number] labelOrIndex The label or index of the layer to show. + # @event 'layer:shown' If a layer that was previously hidden now became visible. + showLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + return if layer.visible + layer.visible = true + @trigger 'layer:shown' + + # Instructs the chart that a data layer should not be displayed. + # @param [String, Number] labelOrIndex The label or index of the layer to hide. + # @event 'layer:hidden' If a layer that was visible was made hidden. + hideLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + return unless layer.visible + layer.visible = false + @trigger 'layer:hidden' + + # Instructs the chart that a data layer's visibility should be toggled. + # @param [String, Number] labelOrIndex The label or index of the layer to toggle. + # @event 'layer:shown' If the layer was made visible + # @event 'layer:hidden' If the layer was made invisible + toggleLayer: (labelOrIndex) -> + return unless (layer = @_findLayer labelOrIndex) + layer.visible = !layer.visible + if layer.visible + @trigger 'layer:shown' + else + @trigger 'layer:hidden' + + # Determines whether or not a data layer is visible. + # @param [String, Number] labelOrIndex The label or index of the layer to toggle. + # @return <code>true</code> if the layer is visible, <code>false</code> otherwise. + isLayerVisible: (labelOrIndex) -> + return null unless (layer = @_findLayer labelOrIndex) + layer.visible + + # Calculates an array of layers in the charts data that are flagged as visible. + # @return [Array] The chart's visible layers. + getVisibleLayers: -> + return @data.filter((layer) -> layer.visible) + + # Updates the chart with new data. + # @param data Data to replace the current data for the chart. + # @param [Boolean] draw Whether or not to redraw the chart after the data has been set. + # Default: true. + update: (data, draw=true) -> + @setData data + @draw() if draw + + # Draws the chart. Triggers the 'draw' event, subclasses should call super() after drawing to + # ensure that the event is triggered. + # @abstract Must be overriden in child classes to perform chart specific drawing. + draw: -> @trigger 'draw' + + # Determines a resulting scale domain for the y axis given a domain + # @param [Mixed] givenDomain A set domain, a label associated with mutliple + # layers, or null. + # @return The domain for the y scale + _getScaleDomain: (givenDomain) -> + # Explicitly set given domain + if Array.isArray(givenDomain) + return givenDomain + + # Check for "labeled" layer ranges + if Epoch.isString(givenDomain) + layers = @getVisibleLayers() + .filter((l) -> l.range == givenDomain) + .map((l) -> l.values) + if layers? && layers.length + values = Epoch.Util.flatten(layers).map((d) -> d.y) + minFn = (memo, curr) -> if curr < memo then curr else memo + maxFn = (memo, curr) -> if curr > memo then curr else memo + return [values.reduce(minFn, values[0]), values.reduce(maxFn, values[0])] + + # Find the domain based on chart options + if Array.isArray(@options.range) + @options.range + else if @options.range && Array.isArray(@options.range.left) + @options.range.left + else if @options.range && Array.isArray(@options.range.right) + @options.range.right + else + @extent((d) -> d.y) + + # Calculates an extent throughout the layers based on the given comparator. + # @param [Function] cmp Comparator to use for performing the min and max for the extent + # calculation. + # @return [Array] an extent array with the first element as the minimum value in the + # chart's data set and the second element as the maximum. + extent: (cmp) -> + [ + d3.min(@getVisibleLayers(), (layer) -> d3.min(layer.values, cmp)), + d3.max(@getVisibleLayers(), (layer) -> d3.max(layer.values, cmp)) + ] + + # Updates the width and height members and container dimensions in response to an + # 'option:width' or 'option:height' event. + dimensionsChanged: -> + @width = @option('width') or @width + @height = @option('height') or @height + @el.width(@width) + @el.height(@height) + + # Updates the chart in response to a layer being shown or hidden + layerChanged: -> + @draw() + +# Base class for all SVG charts (via d3). +class Epoch.Chart.SVG extends Epoch.Chart.Base + # Initializes the chart and places the rendering SVG in the specified HTML + # containing element. + # @param [Object] options Options for the SVG chart. + # @option options [HTMLElement] el Container element for the chart. + # @option options [Array] data Layered data used to render the chart. + constructor: (@options={}) -> + super(@options) + if @el? + @svg = @el.append('svg') + else + @svg = d3.select(document.createElement('svg')) + @svg.attr + xmlns: 'http://www.w3.org/2000/svg', + width: @width, + height: @height + + # Resizes the svg element in response to a 'option:width' or 'option:height' event. + dimensionsChanged: -> + super() + @svg.attr('width', @width).attr('height', @height) + +# Base Class for all Canvas based charts. +class Epoch.Chart.Canvas extends Epoch.Chart.Base + # Initializes the chart and places the rendering canvas in the specified + # HTML container element. + # @param [Object] options Options for the SVG chart. + # @option options [HTMLElement] el Container element for the chart. + # @option options [Array] data Layered data used to render the chart. + constructor: (@options={}) -> + super(@options) + + if @options.pixelRatio? + @pixelRatio = @options.pixelRatio + else if window.devicePixelRatio? + @pixelRatio = window.devicePixelRatio + else + @pixelRatio = 1 + + @canvas = d3.select( document.createElement('CANVAS') ) + @canvas.style + 'width': "#{@width}px" + 'height': "#{@height}px" + + @canvas.attr + width: @getWidth() + height: @getHeight() + + @el.node().appendChild @canvas.node() if @el? + @ctx = Epoch.Util.getContext @canvas.node() + + # @return [Number] width of the canvas with respect to the pixel ratio of the display + getWidth: -> @width * @pixelRatio + + # @return [Number] height of the canvas with respect to the pixel ratio of the display + getHeight: -> @height * @pixelRatio + + # Clears the render canvas. + clear: -> + @ctx.clearRect(0, 0, @getWidth(), @getHeight()) + + # @return [Object] computed styles for the given selector in the context of this chart. + # @param [String] selector The selector used to compute the styles. + getStyles: (selector) -> + Epoch.QueryCSS.getStyles(selector, @el) + + # Resizes the canvas element when the dimensions of the container change + dimensionsChanged: -> + super() + @canvas.style {'width': "#{@width}px", 'height': "#{@height}px"} + @canvas.attr { width: @getWidth(), height: @getHeight() } + + # Purges QueryCSS cache and redraws the Canvas based chart. + redraw: -> + Epoch.QueryCSS.purge() + @draw() |