If global configuration is used, labels are drawn from one of the label arrays included in the chart data. If only data.labels is defined, this will be used. If data.xLabels is defined and the axis is horizontal, this will be used. Similarly, if data.yLabels is defined and the axis is vertical, this property will be used. Using both xLabels and yLabels together can create a chart that uses strings for both the X and Y axes.
-
Specifying any of the settings above defines the x axis as type: category if not defined otherwise. For more fine-grained control of category labels it is also possible to add labels as part of the category axis definition. Doing so does not apply the global defaults.
The category scale provides the following options for configuring tick marks. They are nested in the ticks sub object. These options extend the common tick configuration.
For both the min and max properties, the value must be in the labels array. In the example below, the x axis would only display "March" through "June".
Axes that follow a cartesian grid are known as 'Cartesian Axes'. Cartesian axes are used for line, bar, and bubble charts. Four cartesian axes are included in Chart.js by default.
The following options are common to all cartesian axes but do not apply to other axes.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
autoSkip
-
Boolean
-
true
-
If true, automatically calculates how many labels that can be shown and hides labels accordingly. Turn it off to show all labels no matter what
-
-
-
autoSkipPadding
-
Number
-
0
-
Padding between the ticks on the horizontal axis when autoSkip is enabled. Note: Only applicable to horizontal scales.
-
-
-
labelOffset
-
Number
-
0
-
Distance in pixels to offset the label from the centre point of the tick (in the y direction for the x axis, and the x direction for the y axis). Note: this can cause labels at the edges to be cropped by the edge of the canvas
-
-
-
maxRotation
-
Number
-
90
-
Maximum rotation for tick labels when rotating to condense labels. Note: Rotation doesn't occur until necessary. Note: Only applicable to horizontal scales.
-
-
-
minRotation
-
Number
-
0
-
Minimum rotation for tick labels. Note: Only applicable to horizontal scales.
-
-
-
mirror
-
Boolean
-
false
-
Flips tick labels around axis, displaying the labels inside the chart instead of outside. Note: Only applicable to vertical scales.
-
-
-
padding
-
Number
-
10
-
Padding between the tick label and the axis. When set on a vertical axis, this applies in the horizontal (X) direction. When set on a horizontal axis, this applies in the vertical (Y) direction.
-
-
-
-
Axis ID
-
The properties dataset.xAxisID or dataset.yAxisID have to match the scale properties scales.xAxes.id or scales.yAxes.id. This is especially needed if multi-axes charts are used.
-
var myChart = new Chart(ctx, {
- type: 'line',
- data: {
- datasets: [{
- // This dataset appears on the first axis
- yAxisID: 'first-y-axis'
- }, {
- // This dataset appears on the second axis
- yAxisID: 'second-y-axis'
- }]
- },
- options: {
- scales: {
- yAxes: [{
- id: 'first-y-axis',
- type: 'linear'
- }, {
- id: 'second-y-axis',
- type: 'linear'
- }]
- }
- }
-});
-
-
Creating Multiple Axes
-
With cartesian axes, it is possible to create multiple X and Y axes. To do so, you can add multiple configuration objects to the xAxes and yAxes properties. When adding new axes, it is important to ensure that you specify the type of the new axes as default types are not used in this case.
-
In the example below, we are creating two Y axes. We then use the yAxisID property to map the datasets to their correct axes.
-
var myChart = new Chart(ctx, {
- type: 'line',
- data: {
- datasets: [{
- data: [20, 50, 100, 75, 25, 0],
- label: 'Left dataset',
-
- // This binds the dataset to the left y axis
- yAxisID: 'left-y-axis'
- }, {
- data: [0.1, 0.5, 1.0, 2.0, 1.5, 0],
- label: 'Right dataset',
-
- // This binds the dataset to the right y axis
- yAxisID: 'right-y-axis',
- }],
- labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
- },
- options: {
- scales: {
- yAxes: [{
- id: 'left-y-axis',
- type: 'linear',
- position: 'left'
- }, {
- id: 'right-y-axis',
- type: 'linear',
- position: 'right'
- }]
- }
- }
-});
-
The linear scale is use to chart numerical data. It can be placed on either the x or y axis. The scatter chart type automatically configures a line chart to use one of these scales for the x axis. As the name suggests, linear interpolation is used to determine where a value lies on the axis.
-
Tick Configuration Options
-
The following options are provided by the linear scale. They are all located in the ticks sub options. These options extend the common tick configuration.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
beginAtZero
-
Boolean
-
-
if true, scale will include 0 if it is not already included.
-
-
-
min
-
Number
-
-
User defined minimum number for the scale, overrides minimum value from data. more...
-
-
-
max
-
Number
-
-
User defined maximum number for the scale, overrides maximum value from data. more...
-
-
-
maxTicksLimit
-
Number
-
11
-
Maximum number of ticks and gridlines to show.
-
-
-
stepSize
-
Number
-
-
User defined fixed step size for the scale. more...
-
-
-
suggestedMax
-
Number
-
-
Adjustment used when calculating the maximum data value. more...
-
-
-
suggestedMin
-
Number
-
-
Adjustment used when calculating the minimum data value. more...
-
-
-
-
Axis Range Settings
-
Given the number of axis range settings, it is important to understand how they all interact with each other.
-
The suggestedMax and suggestedMin settings only change the data values that are used to scale the axis. These are useful for extending the range of the axis while maintaining the auto fit behaviour.
In this example, the largest positive value is 50, but the data maximum is expanded out to 100. However, because the lowest data value is below the suggestedMin setting, it is ignored.
In contrast to the suggested* settings, the min and max settings set explicit ends to the axes. When these are set, some data points may not be visible.
-
Step Size
-
If set, the scale ticks will be enumerated by multiple of stepSize, having one tick per increment. If not set, the ticks are labeled automatically using the nice numbers algorithm.
-
This example sets up a chart with a y axis that creates ticks at 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5.
The logarithmic scale is use to chart numerical data. It can be placed on either the x or y axis. As the name suggests, logarithmic interpolation is used to determine where a value lies on the axis.
-
Tick Configuration Options
-
The following options are provided by the logarithmic scale. They are all located in the ticks sub options. These options extend the common tick configuration.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
min
-
Number
-
-
User defined minimum number for the scale, overrides minimum value from data.
-
-
-
max
-
Number
-
-
User defined maximum number for the scale, overrides maximum value from data.
The time scale is used to display times and dates. When building its ticks, it will automatically calculate the most comfortable unit base on the size of the scale.
-
Data Sets
-
Input Data
-
The x-axis data points may additionally be specified via the t attribute when using the time scale.
-
data: [{
- x: new Date(),
- y: 1
-}, {
- t: new Date(),
- y: 10
-}]
-
Date Formats
-
When providing data for the time scale, Chart.js supports all of the formats that Moment.js accepts. See Moment.js docs for details.
-
Configuration Options
-
The following options are provided by the time scale. You may also set options provided by the common tick configuration.
The following display formats are used to configure how different time units are formed into strings for the axis tick marks. See moment.js for the allowable format strings.
-
-
-
-
Name
-
Default
-
Example
-
-
-
-
-
millisecond
-
'h:mm:ss.SSS a'
-
11:20:01.123 AM
-
-
-
second
-
'h:mm:ss a'
-
11:20:01 AM
-
-
-
minute
-
'h:mm a'
-
11:20 AM
-
-
-
hour
-
'hA'
-
11AM
-
-
-
day
-
'MMM D'
-
Sep 4
-
-
-
week
-
'll'
-
Sep 4 2015
-
-
-
month
-
'MMM YYYY'
-
Sep 2015
-
-
-
quarter
-
'[Q]Q - YYYY'
-
Q3 - 2015
-
-
-
year
-
'YYYY'
-
2015
-
-
-
-
For example, to set the display format for the 'quarter' unit to show the month and year, the following config would be passed to the chart constructor.
Axes are an integral part of a chart. They are used to determine how data maps to a pixel value on the chart. In a cartesian chart, there is 1 or more X axis and 1 or more Y axis to map points onto the 2 dimensional canvas. These axes are know as 'cartesian axes'.
-
In a radial chart, such as a radar chart or a polar area chart, there is a single axis that maps points in the angular and radial directions. These are known as 'radial axes'.
-
Scales in Chart.js >V2.0 are significantly more powerful, but also different than those of v1.0.
-
-
Multiple X & Y axes are supported.
-
A built-in label auto-skip feature detects would-be overlapping ticks and labels and removes every nth label to keep things displaying normally.
-
Scale titles are supported
-
New scale types can be extended without writing an entirely new chart type
-
-
Common Configuration
-
The following properties are common to all axes provided by Chart.js
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
display
-
Boolean
-
true
-
If set to false the axis is hidden from view. Overrides gridLines.display, scaleLabel.display, and ticks.display.
-
-
-
callbacks
-
Object
-
-
Callback functions to hook into the axis lifecycle. more...
-
-
-
weight
-
Number
-
0
-
The weight used to sort the axis. Higher weights are further away from the chart area.
-
-
-
-
Callbacks
-
There are a number of config callbacks that can be used to change parameters in the scale at different points in the update process.
-
-
-
-
Name
-
Arguments
-
Description
-
-
-
-
-
beforeUpdate
-
axis
-
Callback called before the update process starts.
-
-
-
beforeSetDimensions
-
axis
-
Callback that runs before dimensions are set.
-
-
-
afterSetDimensions
-
axis
-
Callback that runs after dimensions are set.
-
-
-
beforeDataLimits
-
axis
-
Callback that runs before data limits are determined.
-
-
-
afterDataLimits
-
axis
-
Callback that runs after data limits are determined.
-
-
-
beforeBuildTicks
-
axis
-
Callback that runs before ticks are created.
-
-
-
afterBuildTicks
-
axis
-
Callback that runs after ticks are created. Useful for filtering ticks.
-
-
-
beforeTickToLabelConversion
-
axis
-
Callback that runs before ticks are converted into strings.
-
-
-
afterTickToLabelConversion
-
axis
-
Callback that runs after ticks are converted into strings.
-
-
-
beforeCalculateTickRotation
-
axis
-
Callback that runs before tick rotation is determined.
-
-
-
afterCalculateTickRotation
-
axis
-
Callback that runs after tick rotation is determined.
-
-
-
beforeFit
-
axis
-
Callback that runs before the scale fits to the canvas.
-
-
-
afterFit
-
axis
-
Callback that runs after the scale fits to the canvas.
-
-
-
afterUpdate
-
axis
-
Callback that runs at the end of the update process.
-
-
-
-
Updating Axis Defaults
-
The default configuration for a scale can be easily changed using the scale service. All you need to do is to pass in a partial configuration that will be merged with the current scale default configuration to form the new default.
-
For example, to set the minimum value of 0 for all linear scales, you would do the following. Any linear scales created after this time would now have a minimum of 0.
When creating a chart, you want to tell the viewer what data they are viewing. To do this, you need to label the axis.
-
Scale Title Configuration
-
The scale label configuration is nested under the scale configuration in the scaleLabel key. It defines options for the scale title. Note that this only applies to cartesian axes.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
display
-
Boolean
-
false
-
If true, display the axis title.
-
-
-
labelString
-
String
-
''
-
The text for the title. (i.e. "# of People" or "Response Choices").
Font family for the scale title, follows CSS font-family options.
-
-
-
fontSize
-
Number
-
12
-
Font size for scale title.
-
-
-
fontStyle
-
String
-
'normal'
-
Font style for the scale title, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).
-
-
-
padding
-
Number/Object
-
4
-
Padding to apply around scale labels. Only top and bottom are implemented.
-
-
-
-
Creating Custom Tick Formats
-
It is also common to want to change the tick marks to include information about the data type. For example, adding a dollar sign ('$'). To do this, you need to override the ticks.callback method in the axis configuration.
-In the following example, every label of the Y axis would be displayed with a dollar sign at the front..
-
If the callback returns null or undefined the associated grid line will be hidden.
-
var chart = new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- scales: {
- yAxes: [{
- ticks: {
- // Include a dollar sign in the ticks
- callback: function(value, index, values) {
- return'$' + value;
- }
- }
- }]
- }
- }
-});
-
Radial axes are used specifically for the radar and polar area chart types. These axes overlay the chart area, rather than being positioned on one of the edges. One radial axis is included by default in Chart.js.
The linear scale is use to chart numerical data. As the name suggests, linear interpolation is used to determine where a value lies in relation the center of the axis.
-
The following additional configuration options are provided by the radial linear scale.
-
Configuration Options
-
The axis has configuration properties for ticks, angle lines (line that appear in a radar chart outward from the center), pointLabels (labels around the edge in a radar chart). The following sections define each of the properties in those sections.
The following options are provided by the linear scale. They are all located in the ticks sub options. The common tick configuration options are supported by this axis.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
backdropColor
-
Color
-
'rgba(255, 255, 255, 0.75)'
-
Color of label backdrops
-
-
-
backdropPaddingX
-
Number
-
2
-
Horizontal padding of label backdrop.
-
-
-
backdropPaddingY
-
Number
-
2
-
Vertical padding of label backdrop.
-
-
-
beginAtZero
-
Boolean
-
false
-
if true, scale will include 0 if it is not already included.
-
-
-
min
-
Number
-
-
User defined minimum number for the scale, overrides minimum value from data. more...
-
-
-
max
-
Number
-
-
User defined maximum number for the scale, overrides maximum value from data. more...
-
-
-
maxTicksLimit
-
Number
-
11
-
Maximum number of ticks and gridlines to show.
-
-
-
stepSize
-
Number
-
-
User defined fixed step size for the scale. more...
-
-
-
suggestedMax
-
Number
-
-
Adjustment used when calculating the maximum data value. more...
-
-
-
suggestedMin
-
Number
-
-
Adjustment used when calculating the minimum data value. more...
-
-
-
showLabelBackdrop
-
Boolean
-
true
-
If true, draw a background behind the tick labels
-
-
-
-
Axis Range Settings
-
Given the number of axis range settings, it is important to understand how they all interact with each other.
-
The suggestedMax and suggestedMin settings only change the data values that are used to scale the axis. These are useful for extending the range of the axis while maintaining the auto fit behaviour.
In this example, the largest positive value is 50, but the data maximum is expanded out to 100. However, because the lowest data value is below the suggestedMin setting, it is ignored.
In contrast to the suggested* settings, the min and max settings set explicit ends to the axes. When these are set, some data points may not be visible.
-
Step Size
-
If set, the scale ticks will be enumerated by multiple of stepSize, having one tick per increment. If not set, the ticks are labeled automatically using the nice numbers algorithm.
-
This example sets up a chart with a y axis that creates ticks at 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5.
The following options are used to configure angled lines that radiate from the center of the chart to the point labels. They can be found in the angleLines sub options. Note that these options only apply if angleLines.display is true.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
display
-
Boolean
-
true
-
if true, angle lines are shown
-
-
-
color
-
Color
-
rgba(0, 0, 0, 0.1)
-
Color of angled lines
-
-
-
lineWidth
-
Number
-
1
-
Width of angled lines
-
-
-
-
Point Label Options
-
The following options are used to configure the point labels that are shown on the perimeter of the scale. They can be found in the pointLabels sub options. Note that these options only apply if pointLabels.display is true.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
callback
-
Function
-
-
Callback function to transform data labels to point labels. The default implementation simply returns the current string.
There are a number of options to allow styling an axis. There are settings to control grid lines and ticks.
-
Grid Line Configuration
-
The grid line configuration is nested under the scale configuration in the gridLines key. It defines options for the grid lines that run perpendicular to the axis.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
display
-
Boolean
-
true
-
If false, do not display grid lines for this axis.
-
-
-
color
-
Color/Color[]
-
'rgba(0, 0, 0, 0.1)'
-
The color of the grid lines. If specified as an array, the first color applies to the first grid line, the second to the second grid line and so on.
-
-
-
borderDash
-
Number[]
-
[]
-
Length and spacing of dashes on grid lines. See MDN
If true, draw border at the edge between the axis and the chart area.
-
-
-
drawOnChartArea
-
Boolean
-
true
-
If true, draw lines on the chart area inside the axis lines. This is useful when there are multiple axes and you need to control which grid lines are drawn.
-
-
-
drawTicks
-
Boolean
-
true
-
If true, draw lines beside the ticks in the axis area beside the chart.
-
-
-
tickMarkLength
-
Number
-
10
-
Length in pixels that the grid lines will draw into the axis area.
-
-
-
zeroLineWidth
-
Number
-
1
-
Stroke width of the grid line for the first index (index 0).
-
-
-
zeroLineColor
-
Color
-
'rgba(0, 0, 0, 0.25)'
-
Stroke color of the grid line for the first index (index 0).
-
-
-
zeroLineBorderDash
-
Number[]
-
[]
-
Length and spacing of dashes of the grid line for the first index (index 0). See MDN
-
-
-
zeroLineBorderDashOffset
-
Number
-
0
-
Offset for line dashes of the grid line for the first index (index 0). See MDN
-
-
-
offsetGridLines
-
Boolean
-
false
-
If true, grid lines will be shifted to be between labels. This is set to true in the bar chart by default.
-
-
-
-
Tick Configuration
-
The tick configuration is nested under the scale configuration in the ticks key. It defines options for the tick marks that are generated by the axis.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
callback
-
Function
-
-
Returns the string representation of the tick value as it should be displayed on the chart. See callback.
Font family for the tick labels, follows CSS font-family options.
-
-
-
fontSize
-
Number
-
12
-
Font size for the tick labels.
-
-
-
fontStyle
-
String
-
'normal'
-
Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).
-
-
-
reverse
-
Boolean
-
false
-
Reverses order of tick labels.
-
-
-
minor
-
object
-
{}
-
Minor ticks configuration. Omitted options are inherited from options above.
-
-
-
major
-
object
-
{}
-
Major ticks configuration. Omitted options are inherited from options above.
-
-
-
-
Minor Tick Configuration
-
The minorTick configuration is nested under the ticks configuration in the minor key. It defines options for the minor tick marks that are generated by the axis. Omitted options are inherited from ticks configuration.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
callback
-
Function
-
-
Returns the string representation of the tick value as it should be displayed on the chart. See callback.
Font family for the tick labels, follows CSS font-family options.
-
-
-
fontSize
-
Number
-
12
-
Font size for the tick labels.
-
-
-
fontStyle
-
String
-
'normal'
-
Font style for the tick labels, follows CSS font-style options (i.e. normal, italic, oblique, initial, inherit).
-
-
-
-
Major Tick Configuration
-
The majorTick configuration is nested under the ticks configuration in the major key. It defines options for the major tick marks that are generated by the axis. Omitted options are inherited from ticks configuration.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
callback
-
Function
-
-
Returns the string representation of the tick value as it should be displayed on the chart. See callback.
Both line and radar charts support a fill option on the dataset object which can be used to create area between two datasets or a dataset and a boundary, i.e. the scale origin, start or end (see filling modes).
-
-
Note: this feature is implemented by the filler plugin.
-
-
Filling modes
-
-
-
-
Mode
-
Type
-
Values
-
-
-
-
-
Absolute dataset index 1
-
Number
-
1, 2, 3, ...
-
-
-
Relative dataset index 1
-
String
-
'-1', '-2', '+1', ...
-
-
-
Boundary 2
-
String
-
'start', 'end', 'origin'
-
-
-
Disabled 3
-
Boolean
-
false
-
-
-
-
-
1 dataset filling modes have been introduced in version 2.6.0
-2 prior version 2.6.0, boundary values was 'zero', 'top', 'bottom' (deprecated)
-3 for backward compatibility, fill: true (default) is equivalent to fill: 'origin'
-
-
Example
-
new Chart(ctx, {
- data: {
- datasets: [
- {fill: 'origin'}, // 0: fill to 'origin'
- {fill: '+2'}, // 1: fill to dataset 3
- {fill: 1}, // 2: fill to dataset 1
- {fill: false}, // 3: no fill
- {fill: '-2'} // 4: fill to dataset 2
- ]
- }
-})
-
A bar chart provides a way of showing data values represented as vertical bars. It is sometimes used to show trend data, and the comparison of multiple data sets side by side.
-
-
Example Usage
-
var myBarChart = new Chart(ctx, {
- type: 'bar',
- data: data,
- options: options
-});
-
-
Dataset Properties
-
The bar chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of the bars is generally set this way.
-
Some properties can be specified as an array. If these are set to an array value, the first value applies to the first bar, the second value to the second bar, and so on.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
label
-
String
-
The label for the dataset which appears in the legend and tooltips.
-
-
-
xAxisID
-
String
-
The ID of the x axis to plot this dataset on. If not specified, this defaults to the ID of the first found x axis
-
-
-
yAxisID
-
String
-
The ID of the y axis to plot this dataset on. If not specified, this defaults to the ID of the first found y axis.
Which edge to skip drawing the border for. more...
-
-
-
hoverBackgroundColor
-
Color/Color[]
-
The fill colour of the bars when hovered.
-
-
-
hoverBorderColor
-
Color/Color[]
-
The stroke colour of the bars when hovered.
-
-
-
hoverBorderWidth
-
Number/Number[]
-
The stroke width of the bars when hovered.
-
-
-
-
borderSkipped
-
This setting is used to avoid drawing the bar stroke at the base of the fill. In general, this does not need to be changed except when creating chart types that derive from a bar chart.
-
Options are:
-
-
'bottom'
-
'left'
-
'top'
-
'right'
-
-
Configuration Options
-
The bar chart defines the following configuration options. These options are merged with the global chart configuration options, Chart.defaults.global, to form the options passed to the chart.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
barPercentage
-
Number
-
0.9
-
Percent (0-1) of the available width each bar should be within the category width. 1.0 will take the whole category width and put the bars right next to each other. more...
-
-
-
categoryPercentage
-
Number
-
0.8
-
Percent (0-1) of the available width each category should be within the sample width. more...
-
-
-
barThickness
-
Number
-
-
Manually set width of each bar in pixels. If not set, the base sample widths are calculated automatically so that they take the full available widths without overlap. Then, the bars are sized using barPercentage and categoryPercentage.
-
-
-
maxBarThickness
-
Number
-
-
Set this to ensure that bars are not sized thicker than this.
-
-
-
gridLines.offsetGridLines
-
Boolean
-
true
-
If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval. If false, the grid line will go right down the middle of the bars. more...
-
-
-
-
offsetGridLines
-
If true, the bars for a particular data point fall between the grid lines. The grid line will move to the left by one half of the tick interval, which is the space between the grid lines. If false, the grid line will go right down the middle of the bars. This is set to true for a bar chart while false for other charts by default.
-
This setting applies to the axis configuration. If axes are added to the chart, this setting will need to be set for each new axis.
It is common to want to apply a configuration setting to all created bar charts. The global bar chart settings are stored in Chart.defaults.bar. Changing the global options only affects charts created after the change. Existing charts are not changed.
-
barPercentage vs categoryPercentage
-
The following shows the relationship between the bar percentage option and the category percentage option.
The data property of a dataset for a bar chart is specified as a an array of numbers. Each point in the data array corresponds to the label at the same index on the x axis.
-
data: [20, 10]
-
-
You can also specify the dataset as x/y coordinates when using the time scale.
Bar charts can be configured into stacked bar charts by changing the settings on the X and Y axes to enable stacking. Stacked bar charts can be used to show how one data series is made up of a number of smaller pieces.
The following dataset properties are specific to stacked bar charts.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
stack
-
String
-
The ID of the group to which this dataset belongs to (when stacked, each group will be a separate stack)
-
-
-
-
Horizontal Bar Chart
-
A horizontal bar chart is a variation on a vertical bar chart. It is sometimes used to show trend data, and the comparison of multiple data sets side by side.
-
-
Example
-
var myBarChart = new Chart(ctx, {
- type: 'horizontalBar',
- data: data,
- options: options
-});
-
-
Config Options
-
The configuration options for the horizontal bar chart are the same as for the bar chart. However, any options specified on the x axis in a bar chart, are applied to the y axis in a horizontal bar chart.
-
The default horizontal bar configuration is specified in Chart.defaults.horizontalBar.
A bubble chart is used to display three dimensions of data at the same time. The location of the bubble is determined by the first two dimensions and the corresponding horizontal and vertical axes. The third dimension is represented by the size of the individual bubbles.
-
-
Example Usage
-
// For a bubble chart
-var myBubbleChart = new Chart(ctx,{
- type: 'bubble',
- data: data,
- options: options
-});
-
-
Dataset Properties
-
The bubble chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of the bubbles is generally set this way.
All these values, if undefined, fallback to the associated elements.point.* options.
-
Interactions
-
The interaction with each bubble can be controlled with the following properties:
-
-
-
-
Name
-
Description
-
-
-
-
-
hoverBackgroundColor
-
bubble background color when hovered
-
-
-
hoverBorderColor
-
bubble border color hovered
-
-
-
hoverBorderWidth
-
bubble border width when hovered (in pixels)
-
-
-
hoverRadius
-
bubble additional radius when hovered (in pixels)
-
-
-
hitRadius
-
bubble additional radius for hit detection (in pixels)
-
-
-
-
All these values, if undefined, fallback to the associated elements.point.* options.
-
Default Options
-
We can also change the default values for the Bubble chart type. Doing so will give all bubble charts created after this point the new defaults. The default configuration for the bubble chart can be accessed at Chart.defaults.bubble.
-
Data Structure
-
Bubble chart datasets need to contain a data array of points, each points represented by an object containing the following properties:
-
{
- // X Value
- x: <Number>,
-
- // Y Value
- y: <Number>,
-
- // Bubble radius in pixels (not scaled).
- r: <Number>
-}
-
-
Important: the radius property, r is not scaled by the chart, it is the raw radius in pixels of the bubble that is drawn on the canvas.
Pie and doughnut charts are probably the most commonly used charts. They are divided into segments, the arc of each segment shows the proportional value of each piece of data.
-
They are excellent at showing the relational proportions between data.
-
Pie and doughnut charts are effectively the same class in Chart.js, but have one different default value - their cutoutPercentage. This equates what percentage of the inner should be cut out. This defaults to 0 for pie charts, and 50 for doughnuts.
-
They are also registered under two aliases in the Chart core. Other than their different default value, and different alias, they are exactly the same.
-
-
Example Usage
-
// For a pie chart
-var myPieChart = new Chart(ctx,{
- type: 'pie',
- data: data,
- options: options
-});
-
-
// And for a doughnut chart
-var myDoughnutChart = new Chart(ctx, {
- type: 'doughnut',
- data: data,
- options: options
-});
-
-
Dataset Properties
-
The doughnut/pie chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a the dataset's arc are generally set this way.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
backgroundColor
-
Color[]
-
The fill color of the arcs in the dataset. See Colors
-
-
-
borderColor
-
Color[]
-
The border color of the arcs in the dataset. See Colors
-
-
-
borderWidth
-
Number[]
-
The border width of the arcs in the dataset.
-
-
-
hoverBackgroundColor
-
Color[]
-
The fill colour of the arcs when hovered.
-
-
-
hoverBorderColor
-
Color[]
-
The stroke colour of the arcs when hovered.
-
-
-
hoverBorderWidth
-
Number[]
-
The stroke width of the arcs when hovered.
-
-
-
-
Config Options
-
These are the customisation options specific to Pie & Doughnut charts. These options are merged with the global chart configuration options, and form the options of the chart.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
cutoutPercentage
-
Number
-
50 - for doughnut, 0 - for pie
-
The percentage of the chart that is cut out of the middle.
-
-
-
rotation
-
Number
-
-0.5 * Math.PI
-
Starting angle to draw arcs from.
-
-
-
circumference
-
Number
-
2 * Math.PI
-
Sweep to allow arcs to cover
-
-
-
animation.animateRotate
-
Boolean
-
true
-
If true, the chart will animate in with a rotation animation. This property is in the options.animation object.
-
-
-
animation.animateScale
-
Boolean
-
false
-
If true, will animate scaling the chart from the center outwards.
-
-
-
-
Default Options
-
We can also change these default values for each Doughnut type that is created, this object is available at Chart.defaults.doughnut. Pie charts also have a clone of these defaults available to change at Chart.defaults.pie, with the only difference being cutoutPercentage being set to 0.
-
Data Structure
-
For a pie chart, datasets need to contain an array of data points. The data points should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each.
-
You also need to specify an array of labels so that tooltips appear correctly
-
data = {
- datasets: [{
- data: [10, 20, 30]
- }],
-
- // These labels appear in the legend and in the tooltips when hovering different arcs
- labels: [
- 'Red',
- 'Yellow',
- 'Blue'
- ]
-};
-
A line chart is a way of plotting data points on a line. Often, it is used to show trend data, or the comparison of two data sets.
-
-
Example Usage
-
var myLineChart = new Chart(ctx, {
- type: 'line',
- data: data,
- options: options
-});
-
-
Dataset Properties
-
The line chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a line is generally set this way.
-
All point* properties can be specified as an array. If these are set to an array value, the first value applies to the first point, the second value to the second point, and so on.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
label
-
String
-
The label for the dataset which appears in the legend and tooltips.
-
-
-
xAxisID
-
String
-
The ID of the x axis to plot this dataset on. If not specified, this defaults to the ID of the first found x axis
-
-
-
yAxisID
-
String
-
The ID of the y axis to plot this dataset on. If not specified, this defaults to the ID of the first found y axis.
The 'default' algorithm uses a custom weighted cubic interpolation, which produces pleasant curves for all types of datasets.
-
The 'monotone' algorithm is more suited to y = f(x) datasets : it preserves monotonicity (or piecewise monotonicity) of the dataset being interpolated, and ensures local extremums (if any) stay at input data points.
-
If left untouched (undefined), the global options.elements.line.cubicInterpolationMode property is used.
-
Stepped Line
-
The following values are supported for steppedLine:
-
-
false: No Step Interpolation (default)
-
true: Step-before Interpolation (eq. 'before')
-
'before': Step-before Interpolation
-
'after': Step-after Interpolation
-
-
If the steppedLine value is set to anything other than false, lineTension will be ignored.
-
Configuration Options
-
The line chart defines the following configuration options. These options are merged with the global chart configuration options, Chart.defaults.global, to form the options passed to the chart.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
showLines
-
Boolean
-
true
-
If false, the lines between points are not drawn.
-
-
-
spanGaps
-
Boolean
-
false
-
If false, NaN data causes a break in the line.
-
-
-
-
Default Options
-
It is common to want to apply a configuration setting to all created line charts. The global line chart settings are stored in Chart.defaults.line. Changing the global options only affects charts created after the change. Existing charts are not changed.
-
For example, to configure all line charts with spanGaps = true you would do:
-
Chart.defaults.line.spanGaps = true;
-
-
Data Structure
-
The data property of a dataset for a line chart can be passed in two formats.
-
Number[]
-
data: [20, 10]
-
-
When the data array is an array of numbers, the x axis is generally a category. The points are placed onto the axis using their position in the array. When a line chart is created with a category axis, the labels property of the data object must be specified.
This alternate is used for sparse datasets, such as those in scatter charts. Each data point is specified using an object containing x and y properties.
-
Stacked Area Chart
-
Line charts can be configured into stacked area charts by changing the settings on the y axis to enable stacking. Stacked area charts can be used to show how one data trend is made up of a number of smaller pieces.
When charting a lot of data, the chart render time may start to get quite large. In that case, the following strategies can be used to improve performance.
-
Data Decimation
-
Decimating your data will achieve the best results. When there is a lot of data to display on the graph, it doesn't make sense to show tens of thousands of data points on a graph that is only a few hundred pixels wide.
-
There are many approaches to data decimation and selection of an algorithm will depend on your data and the results you want to achieve. For instance, min/max decimation will preserve peaks in your data but could require up to 4 points for each pixel. This type of decimation would work well for a very noisy signal where you need to see data peaks.
-
Disable Bezier Curves
-
If you are drawing lines on your chart, disabling bezier curves will improve render times since drawing a straight line is more performant than a bezier curve.
If you have a lot of data points, it can be more performant to disable rendering of the line for a dataset and only draw points. Doing this means that there is less to draw on the canvas which will improve render performance.
-
To disable lines:
-
new Chart(ctx, {
- type: 'line',
- data: {
- datasets: [{
- showLine: false, // disable for a single dataset
- }]
- },
- options: {
- showLines: false, // disable for all datasets
- }
-});
-
-
Disable Animations
-
If your charts have long render times, it is a good idea to disable animations. Doing so will mean that the chart needs to only be rendered once during an update instead of multiple times. This will have the effect of reducing CPU usage and improving general page performance.
-
To disable animations
-
new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- animation: {
- duration: 0, // general animation time
- },
- hover: {
- animationDuration: 0, // duration of animations when hovering an item
- },
- responsiveAnimationDuration: 0, // animation duration after a resize
- }
-});
-
With Chart.js, it is possible to create mixed charts that are a combination of two or more different chart types. A common example is a bar chart that also includes a line dataset.
-
Creating a mixed chart starts with the initialization of a basic chart.
-
var myChart = new Chart(ctx, {
- type: 'bar',
- data: data,
- options: options
-});
-
-
At this point we have a standard bar chart. Now we need to convert one of the datasets to a line dataset.
-
var mixedChart = new Chart(ctx, {
- type: 'bar',
- data: {
- datasets: [{
- label: 'Bar Dataset',
- data: [10, 20, 30, 40]
- }, {
- label: 'Line Dataset',
- data: [50, 50, 50, 50],
-
- // Changes this dataset to become a line
- type: 'line'
- }],
- labels: ['January', 'February', 'March', 'April']
- },
- options: options
-});
-
-
At this point we have a chart rendering how we'd like. It's important to note that the default options for a line chart are not merged in this case. Only the options for the default type are merged in. In this case, that means that the default options for a bar chart are merged because that is the type specified by the type field.
The following options can be included in a polar area chart dataset to configure options for that specific dataset.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
backgroundColor
-
Color[]
-
The fill color of the arcs in the dataset. See Colors
-
-
-
borderColor
-
Color[]
-
The border color of the arcs in the dataset. See Colors
-
-
-
borderWidth
-
Number[]
-
The border width of the arcs in the dataset.
-
-
-
hoverBackgroundColor
-
Color[]
-
The fill colour of the arcs when hovered.
-
-
-
hoverBorderColor
-
Color[]
-
The stroke colour of the arcs when hovered.
-
-
-
hoverBorderWidth
-
Number[]
-
The stroke width of the arcs when hovered.
-
-
-
-
Config Options
-
These are the customisation options specific to Polar Area charts. These options are merged with the global chart default options, and form the options of the chart.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
startAngle
-
Number
-
-0.5 * Math.PI
-
Starting angle to draw arcs for the first item in a dataset.
-
-
-
animation.animateRotate
-
Boolean
-
true
-
If true, the chart will animate in with a rotation animation. This property is in the options.animation object.
-
-
-
animation.animateScale
-
Boolean
-
true
-
If true, will animate scaling the chart from the center outwards.
-
-
-
-
Default Options
-
We can also change these defaults values for each PolarArea type that is created, this object is available at Chart.defaults.polarArea. Changing the global options only affects charts created after the change. Existing charts are not changed.
-
For example, to configure all new polar area charts with animateScale = false you would do:
For a polar area chart, datasets need to contain an array of data points. The data points should be a number, Chart.js will total all of the numbers and calculate the relative proportion of each.
-
You also need to specify an array of labels so that tooltips appear correctly for each slice.
-
data = {
- datasets: [{
- data: [10, 20, 30]
- }],
-
- // These labels appear in the legend and in the tooltips when hovering different arcs
- labels: [
- 'Red',
- 'Yellow',
- 'Blue'
- ]
-};
-
A radar chart is a way of showing multiple data points and the variation between them.
-
They are often useful for comparing the points of two or more different data sets.
-
-
Example Usage
-
var myRadarChart = new Chart(ctx, {
- type: 'radar',
- data: data,
- options: options
-});
-
-
Dataset Properties
-
The radar chart allows a number of properties to be specified for each dataset. These are used to set display properties for a specific dataset. For example, the colour of a line is generally set this way.
-
All point* properties can be specified as an array. If these are set to an array value, the first value applies to the first point, the second value to the second point, and so on.
-
-
-
-
Name
-
Type
-
Description
-
-
-
-
-
label
-
String
-
The label for the dataset which appears in the legend and tooltips.
It is common to want to apply a configuration setting to all created radar charts. The global radar chart settings are stored in Chart.defaults.radar. Changing the global options only affects charts created after the change. Existing charts are not changed.
-
Data Structure
-
The data property of a dataset for a radar chart is specified as a an array of numbers. Each point in the data array corresponds to the label at the same index on the x axis.
-
data: [20, 10]
-
-
For a radar chart, to provide context of what each point means, we include an array of strings that show around each point in the chart.
Scatter charts are based on basic line charts with the x axis changed to a linear axis. To use a scatter chart, data must be passed as objects containing X and Y properties. The example below creates a scatter chart with 3 points.
The onProgress and onComplete callbacks are useful for synchronizing an external draw to the chart animation. The callback is passed a Chart.Animation instance:
-
{
- // Chart object
- chart: Chart,
-
- // Current Animation frame number
- currentStep: Number,
-
- // Number of animation frames
- numSteps: Number,
-
- // Animation easing to use
- easing: String,
-
- // Function that renders the chart
- render: Function,
-
- // User callback
- onAnimationProgress: Function,
-
- // User callback
- onAnimationComplete: Function
-}
-
-
The following example fills a progress bar during the chart animation.
While chart types provide settings to configure the styling of each dataset, you sometimes want to style all datasets the same way. A common example would be to stroke all of the bars in a bar chart with the same colour but change the fill per dataset. Options can be configured for four different types of elements: arc, lines, points, and rectangles. When set, these options apply to all objects of that type unless specifically overridden by the configuration attached to a dataset.
-
Global Configuration
-
The element options can be specified per chart or globally. The global options for elements are defined in Chart.defaults.global.elements. For example, to set the border width of all bar charts globally you would do:
The configuration is used to change how the chart behaves. There are properties to control styling, fonts, the legend, etc.
-
Global Configuration
-
This concept was introduced in Chart.js 1.0 to keep configuration DRY, and allow for changing options globally across chart types, avoiding the need to specify options for each instance, or the default for a particular chart type.
-
Chart.js merges the options object passed to the chart with the global configuration using chart type defaults and scales defaults appropriately. This way you can be as specific as you would like in your individual chart configuration, while still changing the defaults for all chart types where applicable. The global general options are defined in Chart.defaults.global. The defaults for each chart type are discussed in the documentation for that chart type.
-
The following example would set the hover mode to 'nearest' for all charts where this was not overridden by the chart type defaults or the options passed to the constructor on creation.
-
Chart.defaults.global.hover.mode = 'nearest';
-
-// Hover mode is set to nearest because it was not overridden here
-var chartHoverModeNearest = new Chart(ctx, {
- type: 'line',
- data: data,
-});
-
-// This chart would have the hover mode that was passed in
-var chartDifferentHoverMode = new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- hover: {
- // Overrides the global setting
- mode: 'index'
- }
- }
-})
-
The layout configuration is passed into the options.layout namespace. The global options for the chart layout is defined in Chart.defaults.global.layout.
If this value is a number, it is applied to all sides of the chart (left, top, right, bottom). If this value is an object, the left property defines the left padding. Similarly the right, top, and bottom properties can also be specified.
-
Lets say you wanted to add 50px of padding to the left side of the chart canvas, you would do:
The chart legend displays data about the datasets that area appearing on the chart.
-
Configuration options
-
The legend configuration is passed into the options.legend namespace. The global options for the chart legend is defined in Chart.defaults.global.legend.
Generates legend items for each thing in the legend. Default implementation returns the text + styling for the color box. See Legend Item for details.
-
-
-
filter
-
Function
-
null
-
Filters legend items out of the legend. Receives 2 parameters, a Legend Item and the chart data.
-
-
-
usePointStyle
-
Boolean
-
false
-
Label style will match corresponding point style (size is based on fontSize, boxWidth is not used in this case).
-
-
-
-
Legend Item Interface
-
Items passed to the legend onClick function are the ones returned from labels.generateLabels. These items must implement the following interface.
-
{
- // Label that will be displayed
- text: String,
-
- // Fill style of the legend box
- fillStyle: Color,
-
- // If true, this item represents a hidden dataset. Label will be rendered with a strike-through effect
- hidden: Boolean,
-
- // For box border. See https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap
- lineCap: String,
-
- // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
- lineDash: Array[Number],
-
- // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset
- lineDashOffset: Number,
-
- // For box border. See https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
- lineJoin: String,
-
- // Width of box border
- lineWidth: Number,
-
- // Stroke style of the legend box
- strokeStyle: Color
-
- // Point style of the legend box (only used if usePointStyle is true)
- pointStyle: String
-}
-
-
Example
-
The following example will create a chart with the legend enabled and turn all of the text red in color.
It can be common to want to trigger different behaviour when clicking an item in the legend. This can be easily achieved using a callback in the config object.
-
The default legend click handler is:
-
function(e, legendItem) {
- var index = legendItem.datasetIndex;
- var ci = this.chart;
- var meta = ci.getDatasetMeta(index);
-
- // See controller.isDatasetVisible comment
- meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
-
- // We hid a dataset ... rerender the chart
- ci.update();
-}
-
-
Lets say we wanted instead to link the display of the first two datasets. We could change the click handler accordingly.
-
var defaultLegendClickHandler = Chart.defaults.global.legend.onClick;
-var newLegendClickHandler = function (e, legendItem) {
- var index = legendItem.datasetIndex;
-
- if (index > 1) {
- // Do the original logic
- defaultLegendClickHandler(e, legendItem);
- } else {
- let ci = this.chart;
- [ci.getDatasetMeta(0),
- ci.getDatasetMeta(1)].forEach(function(meta) {
- meta.hidden = meta.hidden === null? !ci.data.datasets[index].hidden : null;
- });
- ci.update();
- }
-};
-
-var chart = new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- legend: {
-
- }
- }
-});
-
-
Now when you click the legend in this chart, the visibility of the first two datasets will be linked together.
-
HTML Legends
-
Sometimes you need a very complex legend. In these cases, it makes sense to generate an HTML legend. Charts provide a generateLegend() method on their prototype that returns an HTML string for the legend.
-
To configure how this legend is generated, you can change the legendCallback config property.
-
var chart = new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- legendCallback: function(chart) {
- // Return the HTML string here.
- }
- }
-});
-
-
Note that legendCallback is not called automatically and you must call generateLegend() yourself in code when creating a legend using this method.
The tooltip configuration is passed into the options.tooltips namespace. The global options for the chart tooltips is defined in Chart.defaults.global.tooltips.
Spacing to add to top and bottom of each footer line.
-
-
-
footerMarginTop
-
Number
-
6
-
Margin to add before drawing the footer.
-
-
-
xPadding
-
Number
-
6
-
Padding to add on left and right of tooltip.
-
-
-
yPadding
-
Number
-
6
-
Padding to add on top and bottom of tooltip.
-
-
-
caretPadding
-
Number
-
2
-
Extra distance to move the end of the tooltip arrow away from the tooltip point.
-
-
-
caretSize
-
Number
-
5
-
Size, in px, of the tooltip arrow.
-
-
-
cornerRadius
-
Number
-
6
-
Radius of tooltip corner curves.
-
-
-
multiKeyBackground
-
Color
-
'#fff'
-
Color to draw behind the colored boxes when multiple items are in the tooltip
-
-
-
displayColors
-
Boolean
-
true
-
if true, color boxes are shown in the tooltip
-
-
-
borderColor
-
Color
-
'rgba(0,0,0,0)'
-
Color of the border
-
-
-
borderWidth
-
Number
-
0
-
Size of the border
-
-
-
-
Position Modes
-
Possible modes are:
-
-
'average'
-
'nearest'
-
-
'average' mode will place the tooltip at the average position of the items displayed in the tooltip. 'nearest' will place the tooltip at the position of the element closest to the event position.
-
New modes can be defined by adding functions to the Chart.Tooltip.positioners map.
-
Example:
-
/**
- * Custom positioner
- * @function Chart.Tooltip.positioners.custom
- * @param elements {Chart.Element[]} the tooltip elements
- * @param eventPosition {Point} the position of the event in canvas coordinates
- * @returns {Point} the tooltip position
- */
-Chart.Tooltip.positioners.custom = function(elements, eventPosition) {
- /** @type {Chart.Tooltip} */
- var tooltip = this;
-
- /* ... */
-
- return {
- x: 0,
- y: 0
- };
-}
-
-
Sort Callback
-
Allows sorting of tooltip items. Must implement at minimum a function that can be passed to Array.prototype.sort. This function can also accept a third parameter that is the data object passed to the chart.
-
Filter Callback
-
Allows filtering of tooltip items. Must implement at minimum a function that can be passed to Array.prototype.filter. This function can also accept a second parameter that is the data object passed to the chart.
-
Tooltip Callbacks
-
The tooltip label configuration is nested below the tooltip configuration using the callbacks key. The tooltip has the following callbacks for providing text. For all functions, 'this' will be the tooltip object created from the Chart.Tooltip constructor.
-
All functions are called with the same arguments: a tooltip item and the data object passed to the chart. All functions must return either a string or an array of strings. Arrays of strings are treated as multiple lines of text.
-
-
-
-
Name
-
Arguments
-
Description
-
-
-
-
-
beforeTitle
-
Array[tooltipItem], data
-
Returns the text to render before the title.
-
-
-
title
-
Array[tooltipItem], data
-
Returns text to render as the title of the tooltip.
-
-
-
afterTitle
-
Array[tooltipItem], data
-
Returns text to render after the title.
-
-
-
beforeBody
-
Array[tooltipItem], data
-
Returns text to render before the body section.
-
-
-
beforeLabel
-
tooltipItem, data
-
Returns text to render before an individual label. This will be called for each item in the tooltip.
-
-
-
label
-
tooltipItem, data
-
Returns text to render for an individual item in the tooltip.
-
-
-
labelColor
-
tooltipItem, chart
-
Returns the colors to render for the tooltip item. more...
-
-
-
labelTextColor
-
tooltipItem, chart
-
Returns the colors for the text of the label for the tooltip item.
-
-
-
afterLabel
-
tooltipItem, data
-
Returns text to render after an individual label.
-
-
-
afterBody
-
Array[tooltipItem], data
-
Returns text to render after the body section
-
-
-
beforeFooter
-
Array[tooltipItem], data
-
Returns text to render before the footer section.
-
-
-
footer
-
Array[tooltipItem], data
-
Returns text to render as the footer of the tooltip.
-
-
-
afterFooter
-
Array[tooltipItem], data
-
Text to render after the footer section
-
-
-
-
Label Callback
-
The label callback can change the text that displays for a given data point. A common example to round data values; the following example rounds the data to two decimal places.
The tooltip items passed to the tooltip callbacks implement the following interface.
-
{
- // X Value of the tooltip as a string
- xLabel: String,
-
- // Y value of the tooltip as a string
- yLabel: String,
-
- // Index of the dataset the item comes from
- datasetIndex: Number,
-
- // Index of this data item in the dataset
- index: Number,
-
- // X position of matching point
- x: Number,
-
- // Y position of matching point
- y: Number,
-}
-
-
External (Custom) Tooltips
-
Custom tooltips allow you to hook into the tooltip rendering process so that you can render the tooltip in your own custom way. Generally this is used to create an HTML tooltip instead of an oncanvas one. You can enable custom tooltips in the global or chart configuration like so:
-
var myPieChart = new Chart(ctx, {
- type: 'pie',
- data: data,
- options: {
- tooltips: {
- // Disable the on-canvas tooltip
- enabled: false,
-
- custom: function(tooltipModel) {
- // Tooltip Element
- var tooltipEl = document.getElementById('chartjs-tooltip');
-
- // Create element on first render
- if (!tooltipEl) {
- tooltipEl = document.createElement('div');
- tooltipEl.id = 'chartjs-tooltip';
- tooltipEl.innerHTML = "<table></table>";
- document.body.appendChild(tooltipEl);
- }
-
- // Hide if no tooltip
- if (tooltipModel.opacity === 0) {
- tooltipEl.style.opacity = 0;
- return;
- }
-
- // Set caret Position
- tooltipEl.classList.remove('above', 'below', 'no-transform');
- if (tooltipModel.yAlign) {
- tooltipEl.classList.add(tooltipModel.yAlign);
- } else {
- tooltipEl.classList.add('no-transform');
- }
-
- functiongetBody(bodyItem) {
- return bodyItem.lines;
- }
-
- // Set Text
- if (tooltipModel.body) {
- var titleLines = tooltipModel.title || [];
- var bodyLines = tooltipModel.body.map(getBody);
-
- var innerHtml = '<thead>';
-
- titleLines.forEach(function(title) {
- innerHtml += '<tr><th>' + title + '</th></tr>';
- });
- innerHtml += '</thead><tbody>';
-
- bodyLines.forEach(function(body, i) {
- var colors = tooltipModel.labelColors[i];
- var style = 'background:' + colors.backgroundColor;
- style += '; border-color:' + colors.borderColor;
- style += '; border-width: 2px';
- var span = '<span style="' + style + '"></span>';
- innerHtml += '<tr><td>' + span + body + '</td></tr>';
- });
- innerHtml += '</tbody>';
-
- var tableRoot = tooltipEl.querySelector('table');
- tableRoot.innerHTML = innerHtml;
- }
-
- // `this` will be the overall tooltip
- var position = this._chart.canvas.getBoundingClientRect();
-
- // Display, position, and set styles for font
- tooltipEl.style.opacity = 1;
- tooltipEl.style.position = 'absolute';
- tooltipEl.style.left = position.left + tooltipModel.caretX + 'px';
- tooltipEl.style.top = position.top + tooltipModel.caretY + 'px';
- tooltipEl.style.fontFamily = tooltipModel._bodyFontFamily;
- tooltipEl.style.fontSize = tooltipModel.bodyFontSize + 'px';
- tooltipEl.style.fontStyle = tooltipModel._bodyFontStyle;
- tooltipEl.style.padding = tooltipModel.yPadding + 'px ' + tooltipModel.xPadding + 'px';
- }
- }
- }
-});
-
-
See samples for examples on how to get started with custom tooltips.
-
Tooltip Model
-
The tooltip model contains parameters that can be used to render the tooltip.
-
{
- // The items that we are rendering in the tooltip. See Tooltip Item Interface section
- dataPoints: TooltipItem[],
-
- // Positioning
- xPadding: Number,
- yPadding: Number,
- xAlign: String,
- yAlign: String,
-
- // X and Y properties are the top left of the tooltip
- x: Number,
- y: Number,
- width: Number,
- height: Number,
- // Where the tooltip points to
- caretX: Number,
- caretY: Number,
-
- // Body
- // The body lines that need to be rendered
- // Each object contains 3 parameters
- // before: String[] // lines of text before the line with the color square
- // lines: String[], // lines of text to render as the main item with color square
- // after: String[], // lines of text to render after the main lines
- body: Object[],
- // lines of text that appear after the title but before the body
- beforeBody: String[],
- // line of text that appear after the body and before the footer
- afterBody: String[],
- bodyFontColor: Color,
- _bodyFontFamily: String,
- _bodyFontStyle: String,
- _bodyAlign: String,
- bodyFontSize: Number,
- bodySpacing: Number,
-
- // Title
- // lines of text that form the title
- title: String[],
- titleFontColor: Color,
- _titleFontFamily: String,
- _titleFontStyle: String,
- titleFontSize: Number,
- _titleAlign: String,
- titleSpacing: Number,
- titleMarginBottom: Number,
-
- // Footer
- // lines of text that form the footer
- footer: String[],
- footerFontColor: Color,
- _footerFontFamily: String,
- _footerFontStyle: String,
- footerFontSize: Number,
- _footerAlign: String,
- footerSpacing: Number,
- footerMarginTop: Number,
-
- // Appearance
- caretSize: Number,
- cornerRadius: Number,
- backgroundColor: Color,
-
- // colors to render for each item in body[]. This is the color of the squares in the tooltip
- labelColors: Color[],
-
- // 0 opacity is a hidden tooltip
- opacity: Number,
- legendColorBackground: Color,
- displayColors: Boolean,
-}
-
For each chart, there are a set of global prototype methods on the shared ChartType which you may find useful. These are available on all charts created with Chart.js, but for the examples, let's use a line chart we've made.
-
// For example:
-var myLineChart = new Chart(ctx, config);
-
-
.destroy()
-
Use this to destroy any chart instances that are created. This will clean up any references stored to the chart object within Chart.js, along with any associated event listeners attached by Chart.js.
-This must be called before the canvas is reused for a new chart.
-
// Destroys a specific chart instance
-myLineChart.destroy();
-
-
.update(config)
-
Triggers an update of the chart. This can be safely called after updating the data object. This will update all scales, legends, and then re-render the chart.
-
// duration is the time for the animation of the redraw in milliseconds
-// lazy is a boolean. if true, the animation can be interrupted by other animations
-myLineChart.data.datasets[0].data[2] = 50; // Would update the first dataset's value of 'March' to be 50
-myLineChart.update(); // Calling update now animates the position of March from 90 to 50.
-
-
-
Note: replacing the data reference (e.g. myLineChart.data = {datasets: [...]} only works starting version 2.6. Prior that, replacing the entire data object could be achieved with the following workaround: myLineChart.config.data = {datasets: [...]}.
-
-
A config object can be provided with additional configuration for the update process. This is useful when update is manually called inside an event handler and some different animation is desired.
-
The following properties are supported:
-
-
duration (number): Time for the animation of the redraw in milliseconds
-
lazy (boolean): If true, the animation can be interrupted by other animations
-
easing (string): The animation easing function. See Animation Easing for possible values.
Reset the chart to it's state before the initial animation. A new animation can then be triggered using update.
-
myLineChart.reset();
-
-
.render(config)
-
Triggers a redraw of all chart elements. Note, this does not update elements for new data. Use .update() in that case.
-
See .update(config) for more details on the config object.
-
// duration is the time for the animation of the redraw in milliseconds
-// lazy is a boolean. if true, the animation can be interrupted by other animations
-myLineChart.render({
- duration: 800,
- lazy: false,
- easing: 'easeOutBounce'
-});
-
-
.stop()
-
Use this to stop any current animation loop. This will pause the chart during any current animation frame. Call .render() to re-animate.
-
// Stops the charts animation loop at its current frame
-myLineChart.stop();
-// => returns 'this' for chainability
-
-
.resize()
-
Use this to manually resize the canvas element. This is run each time the canvas container is resized, but you can call this method manually if you change the size of the canvas nodes container element.
-
// Resizes & redraws to fill its container element
-myLineChart.resize();
-// => returns 'this' for chainability
-
-
.clear()
-
Will clear the chart canvas. Used extensively internally between animation frames, but you might find it useful.
-
// Will clear the canvas that myLineChart is drawn on
-myLineChart.clear();
-// => returns 'this' for chainability
-
-
.toBase64Image()
-
This returns a base 64 encoded string of the chart in it's current state.
-
myLineChart.toBase64Image();
-// => returns png data url of the image on the canvas
-
-
.generateLegend()
-
Returns an HTML string of a legend for that chart. The legend is generated from the legendCallback in the options.
-
myLineChart.generateLegend();
-// => returns HTML string of a legend for this chart
-
-
.getElementAtEvent(e)
-
Calling getElementAtEvent(event) on your Chart instance passing an argument of an event, or jQuery event, will return the single element at the event position. If there are multiple items within range, only the first is returned. The value returned from this method is an array with a single parameter. An array is used to keep a consistent API between the get*AtEvent methods.
-
myLineChart.getElementAtEvent(e);
-// => returns the first element at the event point.
-
-
To get an item that was clicked on, getElementAtEvent can be used.
-
functionclickHandler(evt) {
- var firstPoint = myChart.getElementAtEvent(evt)[0];
-
- if (firstPoint) {
- var label = myChart.data.labels[firstPoint._index];
- var value = myChart.data.datasets[firstPoint._datasetIndex].data[firstPoint._index];
- }
-}
-
-
.getElementsAtEvent(e)
-
Looks for the element under the event point, then returns all elements at the same data index. This is used internally for 'label' mode highlighting.
-
Calling getElementsAtEvent(event) on your Chart instance passing an argument of an event, or jQuery event, will return the point elements that are at that the same position of that event.
-
canvas.onclick = function(evt){
- var activePoints = myLineChart.getElementsAtEvent(evt);
- // => activePoints is an array of points on the canvas that are at the same position as the click event.
-};
-
-
This functionality may be useful for implementing DOM based tooltips, or triggering custom behaviour in your application.
-
.getDatasetAtEvent(e)
-
Looks for the element under the event point, then returns all elements from that dataset. This is used internally for 'dataset' mode highlighting
-
myLineChart.getDatasetAtEvent(e);
-// => returns an array of elements
-
-
.getDatasetMeta(index)
-
Looks for the dataset that matches the current index and returns that metadata. This returned data has all of the metadata that is used to construct the chart.
-
The data property of the metadata will contain information about each point, rectangle, etc. depending on the chart type.
-
Extensive examples of usage are available in the Chart.js tests.
-
var meta = myChart.getDatasetMeta(0);
-var x = meta.data[0]._model.x
-
Axes in Chart.js can be individually extended. Axes should always derive from Chart.Scale but this is not a mandatory requirement.
-
let MyScale = Chart.Scale.extend({
- /* extensions ... */
-});
-
-// MyScale is now derived from Chart.Scale
-
-
Once you have created your scale class, you need to register it with the global chart object so that it can be used. A default config for the scale may be provided when registering the constructor. The first parameter to the register function is a string key that is used later to identify which scale type to use for a chart.
To use the new scale, simply pass in the string key to the config when creating a chart.
-
var lineChart = new Chart(ctx, {
- data: data,
- type: 'line',
- options: {
- scales: {
- yAxes: [{
- type: 'myScale'// this is the same key that was passed to the registerScaleType function
- }]
- }
- }
-})
-
-
Scale Properties
-
Scale instances are given the following properties during the fitting process.
-
{
- left: Number, // left edge of the scale bounding box
- right: Number, // right edge of the bounding box'
- top: Number,
- bottom: Number,
- width: Number, // the same as right - left
- height: Number, // the same as bottom - top
-
- // Margin on each side. Like css, this is outside the bounding box.
- margins: {
- left: Number,
- right: Number,
- top: Number,
- bottom: Number,
- },
-
- // Amount of padding on the inside of the bounding box (like CSS)
- paddingLeft: Number,
- paddingRight: Number,
- paddingTop: Number,
- paddingBottom: Number,
-}
-
-
Scale Interface
-
To work with Chart.js, custom scale types must implement the following interface.
-
{
- // Determines the data limits. Should set this.min and this.max to be the data max/min
- determineDataLimits: function() {},
-
- // Generate tick marks. this.chart is the chart instance. The data object can be accessed as this.chart.data
- // buildTicks() should create a ticks array on the axis instance, if you intend to use any of the implementations from the base class
- buildTicks: function() {},
-
- // Get the value to show for the data at the given index of the the given dataset, ie this.chart.data.datasets[datasetIndex].data[index]
- getLabelForIndex: function(index, datasetIndex) {},
-
- // Get the pixel (x coordinate for horizontal axis, y coordinate for vertical axis) for a given value
- // @param index: index into the ticks array
- // @param includeOffset: if true, get the pixel halfway between the given tick and the next
- getPixelForTick: function(index, includeOffset) {},
-
- // Get the pixel (x coordinate for horizontal axis, y coordinate for vertical axis) for a given value
- // @param value : the value to get the pixel for
- // @param index : index into the data array of the value
- // @param datasetIndex : index of the dataset the value comes from
- // @param includeOffset : if true, get the pixel halfway between the given tick and the next
- getPixelForValue: function(value, index, datasetIndex, includeOffset) {}
-
- // Get the value for a given pixel (x coordinate for horizontal axis, y coordinate for vertical axis)
- // @param pixel : pixel value
- getValueForPixel: function(pixel) {}
-}
-
-
Optionally, the following methods may also be overwritten, but an implementation is already provided by the Chart.Scale base class.
-
// Transform the ticks array of the scale instance into strings. The default implementation simply calls this.options.ticks.callback(numericalTick, index, ticks);
- convertTicksToLabels: function() {},
-
- // Determine how much the labels will rotate by. The default implementation will only rotate labels if the scale is horizontal.
- calculateTickRotation: function() {},
-
- // Fits the scale into the canvas.
- // this.maxWidth and this.maxHeight will tell you the maximum dimensions the scale instance can be. Scales should endeavour to be as efficient as possible with canvas space.
- // this.margins is the amount of space you have on either side of your scale that you may expand in to. This is used already for calculating the best label rotation
- // You must set this.minSize to be the size of your scale. It must be an object containing 2 properties: width and height.
- // You must set this.width to be the width and this.height to be the height of the scale
- fit: function() {},
-
- // Draws the scale onto the canvas. this.(left|right|top|bottom) will have been populated to tell you the area on the canvas to draw in
- // @param chartArea : an object containing four properties: left, right, top, bottom. This is the rectangle that lines, bars, etc will be drawn in. It may be used, for example, to draw grid lines.
- draw: function(chartArea) {},
-
-
The Core.Scale base class also has some utility functions that you may find useful.
-
{
- // Returns true if the scale instance is horizontal
- isHorizontal: function() {},
-
- // Get the correct value from the value from this.chart.data.datasets[x].data[]
- // If dataValue is an object, returns .x or .y depending on the return of isHorizontal()
- // If the value is undefined, returns NaN
- // Otherwise returns the value.
- // Note that in all cases, the returned value is not guaranteed to be a Number
- getRightValue: function(dataValue) {},
-}
-
Chart.js 2.0 introduces the concept of controllers for each dataset. Like scales, new controllers can be written as needed.
-
Chart.controllers.MyType = Chart.DatasetController.extend({
-
-});
-
-
-// Now we can create a new instance of our chart, using the Chart.js API
-new Chart(ctx, {
- // this is the string the constructor was registered at, ie Chart.controllers.MyType
- type: 'MyType',
- data: data,
- options: options
-});
-
-
Dataset Controller Interface
-
Dataset controllers must implement the following interface.
-
{
- // Create elements for each piece of data in the dataset. Store elements in an array on the dataset as dataset.metaData
- addElements: function() {},
-
- // Create a single element for the data at the given index and reset its state
- addElementAndReset: function(index) {},
-
- // Draw the representation of the dataset
- // @param ease : if specified, this number represents how far to transition elements. See the implementation of draw() in any of the provided controllers to see how this should be used
- draw: function(ease) {},
-
- // Remove hover styling from the given element
- removeHoverStyle: function(element) {},
-
- // Add hover styling to the given element
- setHoverStyle: function(element) {},
-
- // Update the elements in response to new data
- // @param reset : if true, put the elements into a reset state so they can animate to their final values
- update: function(reset) {},
-}
-
-
The following methods may optionally be overridden by derived dataset controllers
-
{
- // Initializes the controller
- initialize: function(chart, datasetIndex) {},
-
- // Ensures that the dataset represented by this controller is linked to a scale. Overridden to helpers.noop in the polar area and doughnut controllers as these
- // chart types using a single scale
- linkScales: function() {},
-
- // Called by the main chart controller when an update is triggered. The default implementation handles the number of data points changing and creating elements appropriately.
- buildOrUpdateElements: function() {}
-}
-
-
Extending Existing Chart Types
-
Extending or replacing an existing controller type is easy. Simply replace the constructor for one of the built in types with your own.
-
The built in controller types are:
-
-
Chart.controllers.line
-
Chart.controllers.bar
-
Chart.controllers.radar
-
Chart.controllers.doughnut
-
Chart.controllers.polarArea
-
Chart.controllers.bubble
-
-
For example, to derive a new chart type that extends from a bubble chart, you would do the following.
-
// Sets the default config for 'derivedBubble' to be the same as the bubble defaults.
-// We look for the defaults by doing Chart.defaults[chartType]
-// It looks like a bug exists when the defaults don't exist
-Chart.defaults.derivedBubble = Chart.defaults.bubble;
-
-// I think the recommend using Chart.controllers.bubble.extend({ extensions here });
-var custom = Chart.controllers.bubble.extend({
- draw: function(ease) {
- // Call super method first
- Chart.controllers.bubble.prototype.draw.call(this, ease);
-
- // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
- var meta = this.getMeta();
- var pt0 = meta.data[0];
- var radius = pt0._view.radius;
-
- var ctx = this.chart.chart.ctx;
- ctx.save();
- ctx.strokeStyle = 'red';
- ctx.lineWidth = 1;
- ctx.strokeRect(pt0._view.x - radius, pt0._view.y - radius, 2 * radius, 2 * radius);
- ctx.restore();
- }
-});
-
-// Stores the controller so that the chart initialization routine can look it up with
-// Chart.controllers[type]
-Chart.controllers.derivedBubble = custom;
-
-// Now we can create and use our new chart type
-new Chart(ctx, {
- type: 'derivedBubble',
- data: data,
- options: options,
-});
-
-
Bar Controller
-
The bar controller has a special property that you should be aware of. To correctly calculate the width of a bar, the controller must determine the number of datasets that map to bars. To do this, the bar controller attaches a property bar to the dataset during initialization. If you are creating a replacement or updated bar controller, you should do the same. This will ensure that charts with regular bars and your new derived bars will work seamlessly.
New contributions to the library are welcome, but we ask that you please follow these guidelines:
-
-
Use tabs for indentation, not spaces.
-
Only change the individual files in /src.
-
Check that your code will pass eslint code standards, gulp lint will run this for you.
-
Check that your code will pass tests, gulp test will run tests for you.
-
Keep pull requests concise, and document new functionality in the relevant .md file.
-
Consider whether your changes are useful for all users, or if creating a Chart.js plugin would be more appropriate.
-
Avoid breaking changes unless there is an upcoming major release, which are infrequent. We encourage people to write plugins for most new advanced features, so care a lot about backwards compatibility.
-
-
Joining the project
-
Active committers and contributors are invited to introduce yourself and request commit access to this project. We have a very active Slack community that you can join here. If you think you can help, we'd love to have you!
-
Building and Testing
-
Chart.js uses gulp to build the library into a single JavaScript file.
-
Firstly, we need to ensure development dependencies are installed. With node and npm installed, after cloning the Chart.js repo to a local directory, and navigating to that directory in the command line, we can run the following:
-
> npm install
-> npm install -g gulp
-
-
This will install the local development dependencies for Chart.js, along with a CLI for the JavaScript task runner gulp.
-
The following commands are now available from the repository root:
-
> gulp build // build Chart.js in ./dist
-> gulp unittest // run tests from ./test/specs
-> gulp unittest --watch // run tests and watch forsource changes
-> gulp unittest --coverage // run tests and generate coverage reports in ./coverage
-> gulp lint // perform code linting (ESLint)
-> gulp test // perform code linting and run unit tests
-> gulp docs // build the documentation in ./dist/docs
-
Please report these on the GitHub page - at github.com/chartjs/Chart.js. Please do not use issues for support requests. For help using Chart.js, please take a look at the chartjs tag on Stack Overflow.
-
Well structured, detailed bug reports are hugely valuable for the project.
-
Guidelines for reporting bugs:
-
-
Check the issue search to see if it has already been reported
Please provide any additional details associated with the bug, if it's browser or screen density specific, or only happens with a certain configuration or data.
Note: Development builds are currently only available via HTTP, so in order to include them in JSFiddle or CodePen, you need to access these tools via HTTP as well.
-
-
WARNING: Development builds MUST not be used for production purposes or as replacement for CDN.
-
Browser support
-
Chart.js offers support for the following browsers:
-
-
Chrome 50+
-
Firefox 45+
-
Internet Explorer 11
-
Edge 14+
-
Safari 9+
-
-
Browser support for the canvas element is available in all modern & major mobile browsers. CanIUse
-
Thanks to BrowserStack for allowing our team to test on thousands of browsers.
-
Previous versions
-
Version 2 has a completely different API than earlier versions.
-
Most earlier version options have current equivalents or are the same.
-
Please use the documentation that is available on chartjs.org for the current version of Chart.js.
-
Please note - documentation for previous versions are available on the GitHub repo.
Plugins are the most efficient way to customize or change the default behavior of a chart. They have been introduced at version 2.1.0 (global plugins only) and extended at version 2.5.0 (per chart plugins and options).
-
Using plugins
-
Plugins can be shared between chart instances:
-
var plugin = { /* plugin implementation */ };
-
-// chart1 and chart2 use "plugin"
-var chart1 = new Chart(ctx, {
- plugins: [plugin]
-});
-
-var chart2 = new Chart(ctx, {
- plugins: [plugin]
-});
-
-// chart3 doesn't use "plugin"
-var chart3 = new Chart(ctx, {});
-
-
Plugins can also be defined directly in the chart plugins config (a.k.a. inline plugins):
should be something short, but also reasonably descriptive
-
-
If a plugin is intended to be released publicly, you may want to check the registry to see if there's something by that name already. Note that in this case, the package name should be prefixed by chartjs-plugin- to appear in Chart.js plugin registry.
-
Plugin options
-
Plugin options are located under the options.plugins config and are scoped by the plugin ID: options.plugins.{plugin-id}.
It's pretty common to want to update charts after they've been created. When the chart data or options are changed, Chart.js will animate to the new data values and options.
-
Adding or Removing Data
-
Adding and removing data is supported by changing the data array. To add data, just add data into the data array as seen in this example.
Scales can be updated separately without changing other options.
-To update the scales, pass in an object containing all the customization including those unchanged ones.
-
Variables referencing any one from chart.scales would be lost after updating scales with a new id or the changed type.
Sometimes when a chart updates, you may not want an animation. To achieve this you can call update with a duration of 0. This will render the chart synchronously and without an animation.
When supplying colors to Chart options, you can use a number of formats. You can specify the color as a string in hexadecimal, RGB, or HSL notations. If a color is needed, but not specified, Chart.js will use the global default color. This color is stored at Chart.defaults.global.defaultColor. It is initially set to 'rgba(0, 0, 0, 0.1)'
-
You can also pass a CanvasGradient object. You will need to create this before passing to the chart, but using it you can achieve some interesting effects.
Using pattern fills for data graphics can help viewers with vision deficiencies (e.g. color-blindness or partial sight) to more easily understand your data.
-
Using the Patternomaly library you can generate patterns to fill datasets.
By default the chart's canvas will use a 1:1 pixel ratio, unless the physical display has a higher pixel ratio (e.g. Retina displays).
-
For applications where a chart will be converted to a bitmap, or printed to a higher DPI medium it can be desirable to render the chart at a higher resolution than the default.
-
Setting devicePixelRatio to a value other than 1 will force the canvas size to be scaled by that amount, relative to the container size. There should be no visible difference on screen; the difference will only be visible when the image is zoomed or printed.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/chart.js/docs/general/device-pixel-ratio.md b/plugins/chart.js/docs/general/device-pixel-ratio.md
deleted file mode 100644
index ae82e7a6e..000000000
--- a/plugins/chart.js/docs/general/device-pixel-ratio.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Device Pixel Ratio
-
-By default the chart's canvas will use a 1:1 pixel ratio, unless the physical display has a higher pixel ratio (e.g. Retina displays).
-
-For applications where a chart will be converted to a bitmap, or printed to a higher DPI medium it can be desirable to render the chart at a higher resolution than the default.
-
-Setting `devicePixelRatio` to a value other than 1 will force the canvas size to be scaled by that amount, relative to the container size. There should be no visible difference on screen; the difference will only be visible when the image is zoomed or printed.
-
-## Configuration Options
-
-| Name | Type | Default | Description
-| ---- | ---- | ------- | -----------
-| `devicePixelRatio` | `Number` | window.devicePixelRatio | Override the window's default devicePixelRatio.
diff --git a/plugins/chart.js/docs/general/fonts.html b/plugins/chart.js/docs/general/fonts.html
deleted file mode 100644
index 9c74ca184..000000000
--- a/plugins/chart.js/docs/general/fonts.html
+++ /dev/null
@@ -1,1019 +0,0 @@
-
-
-
-
-
-
- Fonts · Chart.js documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
There are 4 special global settings that can change all of the fonts on the chart. These options are in Chart.defaults.global. The global font settings only apply when more specific options are not included in the config.
-
For example, in this chart the text will all be red except for the labels in the legend.
-
Chart.defaults.global.defaultFontColor = 'red';
-let chart = new Chart(ctx, {
- type: 'line',
- data: data,
- options: {
- legend: {
- labels: {
- // This more specific font property overrides the global property
- fontColor: 'black'
- }
- }
- }
-});
-
Default font size (in px) for text. Does not apply to radialLinear scale point labels.
-
-
-
defaultFontStyle
-
String
-
'normal'
-
Default font style. Does not apply to tooltip title or footer. Does not apply to chart title.
-
-
-
-
Non-Existant Fonts
-
If a font is specified for a chart that does exist on the system, the browser will not apply the font when it is set. If you notice odd fonts appearing in your charts, check that the font you are applying exists on your system. See issue 3318 for more details.
The hover configuration is passed into the options.hover namespace. The global hover configuration is at Chart.defaults.global.hover. To configure which events trigger chart interactions, see events.
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
mode
-
String
-
'nearest'
-
Sets which elements appear in the tooltip. See Interaction Modes for details.
-
-
-
intersect
-
Boolean
-
true
-
if true, the hover mode only applies when the mouse position intersects an item on the chart.
-
-
-
axis
-
String
-
'x'
-
Can be set to 'x', 'y', or 'xy' to define which directions are used in calculating distances. Defaults to 'x' for index mode and 'xy' in dataset and nearest modes.
-
-
-
animationDuration
-
Number
-
400
-
Duration in milliseconds it takes to animate hover style changes.
Gets the item that is nearest to the point. The nearest item is determined based on the distance to the center of the chart item (point, bar). If 2 or more items are at the same distance, the one with the smallest area is used. If intersect is true, this is only triggered when the mouse position intersects an item in the graph. This is very useful for combo charts where points are hidden behind bars.
Finds the first item that intersects the point and returns it. Behaves like 'nearest' mode with intersect = true.
-
label (deprecated)
-
See 'index' mode
-
index
-
Finds item at the same index. If the intersect setting is true, the first intersecting item is used to determine the index in the data. If intersect false the nearest item, in the x direction, is used to determine the index.
To use index mode in a chart like the horizontal bar chart, where we search along the y direction, you can use the axis setting introduced in v2.7.0. By setting this value to 'y' on the y direction is used.
Finds items in the same dataset. If the intersect setting is true, the first intersecting item is used to determine the index in the data. If intersect false the nearest item is used to determine the index.
Returns all items that would intersect based on the X coordinate of the position only. Would be useful for a vertical cursor implementation. Note that this only applies to cartesian charts
Returns all items that would intersect based on the Y coordinate of the position. This would be useful for a horizontal cursor implementation. Note that this only applies to cartesian charts.
Scriptable options also accept a function which is called for each data and that takes the unique argument context representing contextual information (see option context).
-
Example:
-
color: function(context) {
- var index = context.dataIndex;
- var value = context.dataset.data[index];
- return value < 0 ? 'red' : // draw negative values in red
- index % 2 ? 'blue' : // else, alternate values in blue and green
- 'green';
-}
-
-
-
Note: scriptable options are only supported by a few bubble chart options.
-
-
Indexable Options
-
Indexable options also accept an array in which each item corresponds to the element at the same index. Note that this method requires to provide as many items as data, so, in most cases, using a function is more appropriated if supported.
-
Example:
-
color: [
- 'red', // color for data at index 0
- 'blue', // color for data at index 1
- 'green', // color for data at index 2
- 'black', // color for data at index 3
- //...
-]
-
-
Option Context
-
The option context is used to give contextual information when resolving options and currently only applies to scriptable options.
-
The context object contains the following properties:
-
-
chart: the associated chart
-
dataIndex: index of the current data
-
dataset: dataset at index datasetIndex
-
datasetIndex: index of the current dataset
-
-
Important: since the context can represent different types of entities (dataset, data, etc.), some properties may be undefined so be sure to test any context property before using it.
When it comes to change the chart size based on the window size, a major limitation is that the canvas render size (canvas.width and .height) can not be expressed with relative values, contrary to the display size (canvas.style.width and .height). Furthermore, these sizes are independent from each other and thus the canvas render size does not adjust automatically based on the display size, making the rendering inaccurate.
-
The following examples do not work:
-
-
<canvas height="40vh" width="80vw">: invalid values, the canvas doesn't resize (example)
-
<canvas style="height:40vh; width:80vw">: invalid behavior, the canvas is resized but becomes blurry (example)
-
-
Chart.js provides a few options to enable responsiveness and control the resize behavior of charts by detecting when the canvas display size changes and update the render size accordingly.
-
Configuration Options
-
-
-
-
Name
-
Type
-
Default
-
Description
-
-
-
-
-
responsive
-
Boolean
-
true
-
Resizes the chart canvas when its container does (important note...).
-
-
-
responsiveAnimationDuration
-
Number
-
0
-
Duration in milliseconds it takes to animate to new size after a resize event.
-
-
-
maintainAspectRatio
-
Boolean
-
true
-
Maintain the original canvas aspect ratio (width / height) when resizing.
-
-
-
onResize
-
Function
-
null
-
Called when a resize occurs. Gets passed two arguments: the chart instance and the new size.
-
-
-
-
Important Note
-
Detecting when the canvas size changes can not be done directly from the CANVAS element. Chart.js uses its parent container to update the canvas render and display sizes. However, this method requires the container to be relatively positioned and dedicated to the chart canvas only. Responsiveness can then be achieved by setting relative values for the container size (example):
The chart can also be programmatically resized by modifying the container size:
-
chart.canvas.parentNode.style.height = '128px';
-
-
Printing Resizeable Charts
-
CSS media queries allow changing styles when printing a page. The CSS applied from these media queries may cause charts to need to resize. However, the resize won't happen automatically. To support resizing charts when printing, one needs to hook the onbeforeprint event and manually trigger resizing of each chart.
-
functionbeforePrintHandler () {
- for (var id in Chart.instances) {
- Chart.instances[id].resize()
- }
-}
-
Now, we can create a chart. We add a script to our page:
-
var ctx = document.getElementById('myChart').getContext('2d');
-var chart = new Chart(ctx, {
- // The type of chart we want to create
- type: 'line',
-
- // The data for our dataset
- data: {
- labels: ["January", "February", "March", "April", "May", "June", "July"],
- datasets: [{
- label: "My First dataset",
- backgroundColor: 'rgb(255, 99, 132)',
- borderColor: 'rgb(255, 99, 132)',
- data: [0, 10, 5, 2, 20, 30, 45],
- }]
- },
-
- // Configuration options go here
- options: {}
-});
-
-
It's that easy to get started using Chart.js! From here you can explore the many options that can help you customise your charts with scales, tooltips, labels, colors, custom actions, and much more.
-
There are many examples of Chart.js that are available in the /samples folder of Chart.js.zip that is attached to every release.
If you download or clone the repository, you must build Chart.js to generate the dist files. Chart.js no longer comes with prebuilt release versions, so an alternative option to downloading the repo is strongly advised.
-
Selecting the Correct Build
-
Chart.js provides two different builds for you to choose: Stand-Alone Build, Bundled Build.
-
Stand-Alone Build
-
Files:
-
-
dist/Chart.js
-
dist/Chart.min.js
-
-
The stand-alone build includes Chart.js as well as the color parsing library. If this version is used, you are required to include Moment.js before Chart.js for the functionality of the time axis.
-
Bundled Build
-
Files:
-
-
dist/Chart.bundle.js
-
dist/Chart.bundle.min.js
-
-
The bundled build includes Moment.js in a single file. You should use this version if you require time axes and want to include a single file. You should not use this build if your application already included Moment.js. Otherwise, Moment.js will be included twice which results in increasing page load time and possible version compatability issues.
Chart.js can be integrated with plain JavaScript or with different module loaders. The examples below show how to load Chart.js in different systems.
-
ES6 Modules
-
import Chart from'chart.js';
-var myChart = new Chart(ctx, {...});
-
-
Script Tag
-
<scriptsrc="path/to/chartjs/dist/Chart.js"></script>
-<script>
- var myChart = new Chart(ctx, {...});
-</script>
-
-
Common JS
-
var Chart = require('chart.js');
-var myChart = new Chart(ctx, {...});
-
-
Require JS
-
require(['path/to/chartjs/dist/Chart.js'], function(Chart){
- var myChart = new Chart(ctx, {...});
-});
-
-
-
Important: RequireJS can not load CommonJS module as is, so be sure to require one of the built UMD files instead (i.e. dist/Chart.js, dist/Chart.min.js, etc.).
Chart.js can be used with ES6 modules, plain JavaScript and module loaders.
-
Creating a Chart
-
To create a chart, we need to instantiate the Chart class. To do this, we need to pass in the node, jQuery instance, or 2d context of the canvas of where we want to draw the chart. Here's an example.
// Any of the following formats may be used
-var ctx = document.getElementById("myChart");
-var ctx = document.getElementById("myChart").getContext("2d");
-var ctx = $("#myChart");
-var ctx = "myChart";
-
-
Once you have the element or context, you're ready to instantiate a pre-defined chart-type or create your own!
-
The following example instantiates a bar chart showing the number of votes for different colors and the y-axis starting at 0.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/FontAwesome.otf b/plugins/chart.js/docs/gitbook/fonts/fontawesome/FontAwesome.otf
deleted file mode 100644
index d4de13e83..000000000
Binary files a/plugins/chart.js/docs/gitbook/fonts/fontawesome/FontAwesome.otf and /dev/null differ
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot b/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot
deleted file mode 100644
index c7b00d2ba..000000000
Binary files a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.eot and /dev/null differ
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg b/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg
deleted file mode 100644
index 8b66187fe..000000000
--- a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.svg
+++ /dev/null
@@ -1,685 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf b/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf
deleted file mode 100644
index f221e50a2..000000000
Binary files a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.ttf and /dev/null differ
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff b/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff
deleted file mode 100644
index 6e7483cf6..000000000
Binary files a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff and /dev/null differ
diff --git a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 b/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2
deleted file mode 100644
index 7eb74fd12..000000000
Binary files a/plugins/chart.js/docs/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 and /dev/null differ
diff --git a/plugins/chart.js/docs/gitbook/gitbook-plugin-anchorjs/anchor-style.js b/plugins/chart.js/docs/gitbook/gitbook-plugin-anchorjs/anchor-style.js
deleted file mode 100644
index 7eb43ceae..000000000
--- a/plugins/chart.js/docs/gitbook/gitbook-plugin-anchorjs/anchor-style.js
+++ /dev/null
@@ -1,7 +0,0 @@
-gitbook.events.bind('start', function(e, config) {
- anchors.options = config.anchorjs || {};
-});
-
-gitbook.events.bind('page.change', function() {
- anchors.add(anchors.options.selector || 'h2,h3,h4,h5');
-});
diff --git a/plugins/chart.js/docs/gitbook/gitbook-plugin-chartjs/Chart.bundle.js b/plugins/chart.js/docs/gitbook/gitbook-plugin-chartjs/Chart.bundle.js
deleted file mode 100644
index f015a67d0..000000000
--- a/plugins/chart.js/docs/gitbook/gitbook-plugin-chartjs/Chart.bundle.js
+++ /dev/null
@@ -1,16570 +0,0 @@
-/*!
- * Chart.js
- * http://chartjs.org/
- * Version: 2.5.0
- *
- * Copyright 2017 Nick Downie
- * Released under the MIT license
- * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md
- */
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o lum2) {
- return (lum1 + 0.05) / (lum2 + 0.05);
- }
- return (lum2 + 0.05) / (lum1 + 0.05);
- },
-
- level: function (color2) {
- var contrastRatio = this.contrast(color2);
- if (contrastRatio >= 7.1) {
- return 'AAA';
- }
-
- return (contrastRatio >= 4.5) ? 'AA' : '';
- },
-
- dark: function () {
- // YIQ equation from http://24ways.org/2010/calculating-color-contrast
- var rgb = this.values.rgb;
- var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
- return yiq < 128;
- },
-
- light: function () {
- return !this.dark();
- },
-
- negate: function () {
- var rgb = [];
- for (var i = 0; i < 3; i++) {
- rgb[i] = 255 - this.values.rgb[i];
- }
- this.setValues('rgb', rgb);
- return this;
- },
-
- lighten: function (ratio) {
- var hsl = this.values.hsl;
- hsl[2] += hsl[2] * ratio;
- this.setValues('hsl', hsl);
- return this;
- },
-
- darken: function (ratio) {
- var hsl = this.values.hsl;
- hsl[2] -= hsl[2] * ratio;
- this.setValues('hsl', hsl);
- return this;
- },
-
- saturate: function (ratio) {
- var hsl = this.values.hsl;
- hsl[1] += hsl[1] * ratio;
- this.setValues('hsl', hsl);
- return this;
- },
-
- desaturate: function (ratio) {
- var hsl = this.values.hsl;
- hsl[1] -= hsl[1] * ratio;
- this.setValues('hsl', hsl);
- return this;
- },
-
- whiten: function (ratio) {
- var hwb = this.values.hwb;
- hwb[1] += hwb[1] * ratio;
- this.setValues('hwb', hwb);
- return this;
- },
-
- blacken: function (ratio) {
- var hwb = this.values.hwb;
- hwb[2] += hwb[2] * ratio;
- this.setValues('hwb', hwb);
- return this;
- },
-
- greyscale: function () {
- var rgb = this.values.rgb;
- // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
- var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
- this.setValues('rgb', [val, val, val]);
- return this;
- },
-
- clearer: function (ratio) {
- var alpha = this.values.alpha;
- this.setValues('alpha', alpha - (alpha * ratio));
- return this;
- },
-
- opaquer: function (ratio) {
- var alpha = this.values.alpha;
- this.setValues('alpha', alpha + (alpha * ratio));
- return this;
- },
-
- rotate: function (degrees) {
- var hsl = this.values.hsl;
- var hue = (hsl[0] + degrees) % 360;
- hsl[0] = hue < 0 ? 360 + hue : hue;
- this.setValues('hsl', hsl);
- return this;
- },
-
- /**
- * Ported from sass implementation in C
- * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
- */
- mix: function (mixinColor, weight) {
- var color1 = this;
- var color2 = mixinColor;
- var p = weight === undefined ? 0.5 : weight;
-
- var w = 2 * p - 1;
- var a = color1.alpha() - color2.alpha();
-
- var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
- var w2 = 1 - w1;
-
- return this
- .rgb(
- w1 * color1.red() + w2 * color2.red(),
- w1 * color1.green() + w2 * color2.green(),
- w1 * color1.blue() + w2 * color2.blue()
- )
- .alpha(color1.alpha() * p + color2.alpha() * (1 - p));
- },
-
- toJSON: function () {
- return this.rgb();
- },
-
- clone: function () {
- // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
- // making the final build way to big to embed in Chart.js. So let's do it manually,
- // assuming that values to clone are 1 dimension arrays containing only numbers,
- // except 'alpha' which is a number.
- var result = new Color();
- var source = this.values;
- var target = result.values;
- var value, type;
-
- for (var prop in source) {
- if (source.hasOwnProperty(prop)) {
- value = source[prop];
- type = ({}).toString.call(value);
- if (type === '[object Array]') {
- target[prop] = value.slice(0);
- } else if (type === '[object Number]') {
- target[prop] = value;
- } else {
- console.error('unexpected color value:', value);
- }
- }
- }
-
- return result;
- }
-};
-
-Color.prototype.spaces = {
- rgb: ['red', 'green', 'blue'],
- hsl: ['hue', 'saturation', 'lightness'],
- hsv: ['hue', 'saturation', 'value'],
- hwb: ['hue', 'whiteness', 'blackness'],
- cmyk: ['cyan', 'magenta', 'yellow', 'black']
-};
-
-Color.prototype.maxes = {
- rgb: [255, 255, 255],
- hsl: [360, 100, 100],
- hsv: [360, 100, 100],
- hwb: [360, 100, 100],
- cmyk: [100, 100, 100, 100]
-};
-
-Color.prototype.getValues = function (space) {
- var values = this.values;
- var vals = {};
-
- for (var i = 0; i < space.length; i++) {
- vals[space.charAt(i)] = values[space][i];
- }
-
- if (values.alpha !== 1) {
- vals.a = values.alpha;
- }
-
- // {r: 255, g: 255, b: 255, a: 0.4}
- return vals;
-};
-
-Color.prototype.setValues = function (space, vals) {
- var values = this.values;
- var spaces = this.spaces;
- var maxes = this.maxes;
- var alpha = 1;
- var i;
-
- if (space === 'alpha') {
- alpha = vals;
- } else if (vals.length) {
- // [10, 10, 10]
- values[space] = vals.slice(0, space.length);
- alpha = vals[space.length];
- } else if (vals[space.charAt(0)] !== undefined) {
- // {r: 10, g: 10, b: 10}
- for (i = 0; i < space.length; i++) {
- values[space][i] = vals[space.charAt(i)];
- }
-
- alpha = vals.a;
- } else if (vals[spaces[space][0]] !== undefined) {
- // {red: 10, green: 10, blue: 10}
- var chans = spaces[space];
-
- for (i = 0; i < space.length; i++) {
- values[space][i] = vals[chans[i]];
- }
-
- alpha = vals.alpha;
- }
-
- values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
-
- if (space === 'alpha') {
- return false;
- }
-
- var capped;
-
- // cap values of the space prior converting all values
- for (i = 0; i < space.length; i++) {
- capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
- values[space][i] = Math.round(capped);
- }
-
- // convert to all the other color spaces
- for (var sname in spaces) {
- if (sname !== space) {
- values[sname] = convert[space][sname](values[space]);
- }
- }
-
- return true;
-};
-
-Color.prototype.setSpace = function (space, args) {
- var vals = args[0];
-
- if (vals === undefined) {
- // color.rgb()
- return this.getValues(space);
- }
-
- // color.rgb(10, 10, 10)
- if (typeof vals === 'number') {
- vals = Array.prototype.slice.call(args);
- }
-
- this.setValues(space, vals);
- return this;
-};
-
-Color.prototype.setChannel = function (space, index, val) {
- var svalues = this.values[space];
- if (val === undefined) {
- // color.red()
- return svalues[index];
- } else if (val === svalues[index]) {
- // color.red(color.red())
- return this;
- }
-
- // color.red(100)
- svalues[index] = val;
- this.setValues(space, svalues);
-
- return this;
-};
-
-if (typeof window !== 'undefined') {
- window.Color = Color;
-}
-
-module.exports = Color;
-
-},{"1":1,"4":4}],3:[function(require,module,exports){
-/* MIT license */
-
-module.exports = {
- rgb2hsl: rgb2hsl,
- rgb2hsv: rgb2hsv,
- rgb2hwb: rgb2hwb,
- rgb2cmyk: rgb2cmyk,
- rgb2keyword: rgb2keyword,
- rgb2xyz: rgb2xyz,
- rgb2lab: rgb2lab,
- rgb2lch: rgb2lch,
-
- hsl2rgb: hsl2rgb,
- hsl2hsv: hsl2hsv,
- hsl2hwb: hsl2hwb,
- hsl2cmyk: hsl2cmyk,
- hsl2keyword: hsl2keyword,
-
- hsv2rgb: hsv2rgb,
- hsv2hsl: hsv2hsl,
- hsv2hwb: hsv2hwb,
- hsv2cmyk: hsv2cmyk,
- hsv2keyword: hsv2keyword,
-
- hwb2rgb: hwb2rgb,
- hwb2hsl: hwb2hsl,
- hwb2hsv: hwb2hsv,
- hwb2cmyk: hwb2cmyk,
- hwb2keyword: hwb2keyword,
-
- cmyk2rgb: cmyk2rgb,
- cmyk2hsl: cmyk2hsl,
- cmyk2hsv: cmyk2hsv,
- cmyk2hwb: cmyk2hwb,
- cmyk2keyword: cmyk2keyword,
-
- keyword2rgb: keyword2rgb,
- keyword2hsl: keyword2hsl,
- keyword2hsv: keyword2hsv,
- keyword2hwb: keyword2hwb,
- keyword2cmyk: keyword2cmyk,
- keyword2lab: keyword2lab,
- keyword2xyz: keyword2xyz,
-
- xyz2rgb: xyz2rgb,
- xyz2lab: xyz2lab,
- xyz2lch: xyz2lch,
-
- lab2xyz: lab2xyz,
- lab2rgb: lab2rgb,
- lab2lch: lab2lch,
-
- lch2lab: lch2lab,
- lch2xyz: lch2xyz,
- lch2rgb: lch2rgb
-}
-
-
-function rgb2hsl(rgb) {
- var r = rgb[0]/255,
- g = rgb[1]/255,
- b = rgb[2]/255,
- min = Math.min(r, g, b),
- max = Math.max(r, g, b),
- delta = max - min,
- h, s, l;
-
- if (max == min)
- h = 0;
- else if (r == max)
- h = (g - b) / delta;
- else if (g == max)
- h = 2 + (b - r) / delta;
- else if (b == max)
- h = 4 + (r - g)/ delta;
-
- h = Math.min(h * 60, 360);
-
- if (h < 0)
- h += 360;
-
- l = (min + max) / 2;
-
- if (max == min)
- s = 0;
- else if (l <= 0.5)
- s = delta / (max + min);
- else
- s = delta / (2 - max - min);
-
- return [h, s * 100, l * 100];
-}
-
-function rgb2hsv(rgb) {
- var r = rgb[0],
- g = rgb[1],
- b = rgb[2],
- min = Math.min(r, g, b),
- max = Math.max(r, g, b),
- delta = max - min,
- h, s, v;
-
- if (max == 0)
- s = 0;
- else
- s = (delta/max * 1000)/10;
-
- if (max == min)
- h = 0;
- else if (r == max)
- h = (g - b) / delta;
- else if (g == max)
- h = 2 + (b - r) / delta;
- else if (b == max)
- h = 4 + (r - g) / delta;
-
- h = Math.min(h * 60, 360);
-
- if (h < 0)
- h += 360;
-
- v = ((max / 255) * 1000) / 10;
-
- return [h, s, v];
-}
-
-function rgb2hwb(rgb) {
- var r = rgb[0],
- g = rgb[1],
- b = rgb[2],
- h = rgb2hsl(rgb)[0],
- w = 1/255 * Math.min(r, Math.min(g, b)),
- b = 1 - 1/255 * Math.max(r, Math.max(g, b));
-
- return [h, w * 100, b * 100];
-}
-
-function rgb2cmyk(rgb) {
- var r = rgb[0] / 255,
- g = rgb[1] / 255,
- b = rgb[2] / 255,
- c, m, y, k;
-
- k = Math.min(1 - r, 1 - g, 1 - b);
- c = (1 - r - k) / (1 - k) || 0;
- m = (1 - g - k) / (1 - k) || 0;
- y = (1 - b - k) / (1 - k) || 0;
- return [c * 100, m * 100, y * 100, k * 100];
-}
-
-function rgb2keyword(rgb) {
- return reverseKeywords[JSON.stringify(rgb)];
-}
-
-function rgb2xyz(rgb) {
- var r = rgb[0] / 255,
- g = rgb[1] / 255,
- b = rgb[2] / 255;
-
- // assume sRGB
- r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
- g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
- b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
-
- var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
- var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
- var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
-
- return [x * 100, y *100, z * 100];
-}
-
-function rgb2lab(rgb) {
- var xyz = rgb2xyz(rgb),
- x = xyz[0],
- y = xyz[1],
- z = xyz[2],
- l, a, b;
-
- x /= 95.047;
- y /= 100;
- z /= 108.883;
-
- x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
- y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
- z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
-
- l = (116 * y) - 16;
- a = 500 * (x - y);
- b = 200 * (y - z);
-
- return [l, a, b];
-}
-
-function rgb2lch(args) {
- return lab2lch(rgb2lab(args));
-}
-
-function hsl2rgb(hsl) {
- var h = hsl[0] / 360,
- s = hsl[1] / 100,
- l = hsl[2] / 100,
- t1, t2, t3, rgb, val;
-
- if (s == 0) {
- val = l * 255;
- return [val, val, val];
- }
-
- if (l < 0.5)
- t2 = l * (1 + s);
- else
- t2 = l + s - l * s;
- t1 = 2 * l - t2;
-
- rgb = [0, 0, 0];
- for (var i = 0; i < 3; i++) {
- t3 = h + 1 / 3 * - (i - 1);
- t3 < 0 && t3++;
- t3 > 1 && t3--;
-
- if (6 * t3 < 1)
- val = t1 + (t2 - t1) * 6 * t3;
- else if (2 * t3 < 1)
- val = t2;
- else if (3 * t3 < 2)
- val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
- else
- val = t1;
-
- rgb[i] = val * 255;
- }
-
- return rgb;
-}
-
-function hsl2hsv(hsl) {
- var h = hsl[0],
- s = hsl[1] / 100,
- l = hsl[2] / 100,
- sv, v;
-
- if(l === 0) {
- // no need to do calc on black
- // also avoids divide by 0 error
- return [0, 0, 0];
- }
-
- l *= 2;
- s *= (l <= 1) ? l : 2 - l;
- v = (l + s) / 2;
- sv = (2 * s) / (l + s);
- return [h, sv * 100, v * 100];
-}
-
-function hsl2hwb(args) {
- return rgb2hwb(hsl2rgb(args));
-}
-
-function hsl2cmyk(args) {
- return rgb2cmyk(hsl2rgb(args));
-}
-
-function hsl2keyword(args) {
- return rgb2keyword(hsl2rgb(args));
-}
-
-
-function hsv2rgb(hsv) {
- var h = hsv[0] / 60,
- s = hsv[1] / 100,
- v = hsv[2] / 100,
- hi = Math.floor(h) % 6;
-
- var f = h - Math.floor(h),
- p = 255 * v * (1 - s),
- q = 255 * v * (1 - (s * f)),
- t = 255 * v * (1 - (s * (1 - f))),
- v = 255 * v;
-
- switch(hi) {
- case 0:
- return [v, t, p];
- case 1:
- return [q, v, p];
- case 2:
- return [p, v, t];
- case 3:
- return [p, q, v];
- case 4:
- return [t, p, v];
- case 5:
- return [v, p, q];
- }
-}
-
-function hsv2hsl(hsv) {
- var h = hsv[0],
- s = hsv[1] / 100,
- v = hsv[2] / 100,
- sl, l;
-
- l = (2 - s) * v;
- sl = s * v;
- sl /= (l <= 1) ? l : 2 - l;
- sl = sl || 0;
- l /= 2;
- return [h, sl * 100, l * 100];
-}
-
-function hsv2hwb(args) {
- return rgb2hwb(hsv2rgb(args))
-}
-
-function hsv2cmyk(args) {
- return rgb2cmyk(hsv2rgb(args));
-}
-
-function hsv2keyword(args) {
- return rgb2keyword(hsv2rgb(args));
-}
-
-// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
-function hwb2rgb(hwb) {
- var h = hwb[0] / 360,
- wh = hwb[1] / 100,
- bl = hwb[2] / 100,
- ratio = wh + bl,
- i, v, f, n;
-
- // wh + bl cant be > 1
- if (ratio > 1) {
- wh /= ratio;
- bl /= ratio;
- }
-
- i = Math.floor(6 * h);
- v = 1 - bl;
- f = 6 * h - i;
- if ((i & 0x01) != 0) {
- f = 1 - f;
- }
- n = wh + f * (v - wh); // linear interpolation
-
- switch (i) {
- default:
- case 6:
- case 0: r = v; g = n; b = wh; break;
- case 1: r = n; g = v; b = wh; break;
- case 2: r = wh; g = v; b = n; break;
- case 3: r = wh; g = n; b = v; break;
- case 4: r = n; g = wh; b = v; break;
- case 5: r = v; g = wh; b = n; break;
- }
-
- return [r * 255, g * 255, b * 255];
-}
-
-function hwb2hsl(args) {
- return rgb2hsl(hwb2rgb(args));
-}
-
-function hwb2hsv(args) {
- return rgb2hsv(hwb2rgb(args));
-}
-
-function hwb2cmyk(args) {
- return rgb2cmyk(hwb2rgb(args));
-}
-
-function hwb2keyword(args) {
- return rgb2keyword(hwb2rgb(args));
-}
-
-function cmyk2rgb(cmyk) {
- var c = cmyk[0] / 100,
- m = cmyk[1] / 100,
- y = cmyk[2] / 100,
- k = cmyk[3] / 100,
- r, g, b;
-
- r = 1 - Math.min(1, c * (1 - k) + k);
- g = 1 - Math.min(1, m * (1 - k) + k);
- b = 1 - Math.min(1, y * (1 - k) + k);
- return [r * 255, g * 255, b * 255];
-}
-
-function cmyk2hsl(args) {
- return rgb2hsl(cmyk2rgb(args));
-}
-
-function cmyk2hsv(args) {
- return rgb2hsv(cmyk2rgb(args));
-}
-
-function cmyk2hwb(args) {
- return rgb2hwb(cmyk2rgb(args));
-}
-
-function cmyk2keyword(args) {
- return rgb2keyword(cmyk2rgb(args));
-}
-
-
-function xyz2rgb(xyz) {
- var x = xyz[0] / 100,
- y = xyz[1] / 100,
- z = xyz[2] / 100,
- r, g, b;
-
- r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
- g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
- b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
-
- // assume sRGB
- r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
- : r = (r * 12.92);
-
- g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
- : g = (g * 12.92);
-
- b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
- : b = (b * 12.92);
-
- r = Math.min(Math.max(0, r), 1);
- g = Math.min(Math.max(0, g), 1);
- b = Math.min(Math.max(0, b), 1);
-
- return [r * 255, g * 255, b * 255];
-}
-
-function xyz2lab(xyz) {
- var x = xyz[0],
- y = xyz[1],
- z = xyz[2],
- l, a, b;
-
- x /= 95.047;
- y /= 100;
- z /= 108.883;
-
- x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
- y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
- z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
-
- l = (116 * y) - 16;
- a = 500 * (x - y);
- b = 200 * (y - z);
-
- return [l, a, b];
-}
-
-function xyz2lch(args) {
- return lab2lch(xyz2lab(args));
-}
-
-function lab2xyz(lab) {
- var l = lab[0],
- a = lab[1],
- b = lab[2],
- x, y, z, y2;
-
- if (l <= 8) {
- y = (l * 100) / 903.3;
- y2 = (7.787 * (y / 100)) + (16 / 116);
- } else {
- y = 100 * Math.pow((l + 16) / 116, 3);
- y2 = Math.pow(y / 100, 1/3);
- }
-
- x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
-
- z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
-
- return [x, y, z];
-}
-
-function lab2lch(lab) {
- var l = lab[0],
- a = lab[1],
- b = lab[2],
- hr, h, c;
-
- hr = Math.atan2(b, a);
- h = hr * 360 / 2 / Math.PI;
- if (h < 0) {
- h += 360;
- }
- c = Math.sqrt(a * a + b * b);
- return [l, c, h];
-}
-
-function lab2rgb(args) {
- return xyz2rgb(lab2xyz(args));
-}
-
-function lch2lab(lch) {
- var l = lch[0],
- c = lch[1],
- h = lch[2],
- a, b, hr;
-
- hr = h / 360 * 2 * Math.PI;
- a = c * Math.cos(hr);
- b = c * Math.sin(hr);
- return [l, a, b];
-}
-
-function lch2xyz(args) {
- return lab2xyz(lch2lab(args));
-}
-
-function lch2rgb(args) {
- return lab2rgb(lch2lab(args));
-}
-
-function keyword2rgb(keyword) {
- return cssKeywords[keyword];
-}
-
-function keyword2hsl(args) {
- return rgb2hsl(keyword2rgb(args));
-}
-
-function keyword2hsv(args) {
- return rgb2hsv(keyword2rgb(args));
-}
-
-function keyword2hwb(args) {
- return rgb2hwb(keyword2rgb(args));
-}
-
-function keyword2cmyk(args) {
- return rgb2cmyk(keyword2rgb(args));
-}
-
-function keyword2lab(args) {
- return rgb2lab(keyword2rgb(args));
-}
-
-function keyword2xyz(args) {
- return rgb2xyz(keyword2rgb(args));
-}
-
-var cssKeywords = {
- aliceblue: [240,248,255],
- antiquewhite: [250,235,215],
- aqua: [0,255,255],
- aquamarine: [127,255,212],
- azure: [240,255,255],
- beige: [245,245,220],
- bisque: [255,228,196],
- black: [0,0,0],
- blanchedalmond: [255,235,205],
- blue: [0,0,255],
- blueviolet: [138,43,226],
- brown: [165,42,42],
- burlywood: [222,184,135],
- cadetblue: [95,158,160],
- chartreuse: [127,255,0],
- chocolate: [210,105,30],
- coral: [255,127,80],
- cornflowerblue: [100,149,237],
- cornsilk: [255,248,220],
- crimson: [220,20,60],
- cyan: [0,255,255],
- darkblue: [0,0,139],
- darkcyan: [0,139,139],
- darkgoldenrod: [184,134,11],
- darkgray: [169,169,169],
- darkgreen: [0,100,0],
- darkgrey: [169,169,169],
- darkkhaki: [189,183,107],
- darkmagenta: [139,0,139],
- darkolivegreen: [85,107,47],
- darkorange: [255,140,0],
- darkorchid: [153,50,204],
- darkred: [139,0,0],
- darksalmon: [233,150,122],
- darkseagreen: [143,188,143],
- darkslateblue: [72,61,139],
- darkslategray: [47,79,79],
- darkslategrey: [47,79,79],
- darkturquoise: [0,206,209],
- darkviolet: [148,0,211],
- deeppink: [255,20,147],
- deepskyblue: [0,191,255],
- dimgray: [105,105,105],
- dimgrey: [105,105,105],
- dodgerblue: [30,144,255],
- firebrick: [178,34,34],
- floralwhite: [255,250,240],
- forestgreen: [34,139,34],
- fuchsia: [255,0,255],
- gainsboro: [220,220,220],
- ghostwhite: [248,248,255],
- gold: [255,215,0],
- goldenrod: [218,165,32],
- gray: [128,128,128],
- green: [0,128,0],
- greenyellow: [173,255,47],
- grey: [128,128,128],
- honeydew: [240,255,240],
- hotpink: [255,105,180],
- indianred: [205,92,92],
- indigo: [75,0,130],
- ivory: [255,255,240],
- khaki: [240,230,140],
- lavender: [230,230,250],
- lavenderblush: [255,240,245],
- lawngreen: [124,252,0],
- lemonchiffon: [255,250,205],
- lightblue: [173,216,230],
- lightcoral: [240,128,128],
- lightcyan: [224,255,255],
- lightgoldenrodyellow: [250,250,210],
- lightgray: [211,211,211],
- lightgreen: [144,238,144],
- lightgrey: [211,211,211],
- lightpink: [255,182,193],
- lightsalmon: [255,160,122],
- lightseagreen: [32,178,170],
- lightskyblue: [135,206,250],
- lightslategray: [119,136,153],
- lightslategrey: [119,136,153],
- lightsteelblue: [176,196,222],
- lightyellow: [255,255,224],
- lime: [0,255,0],
- limegreen: [50,205,50],
- linen: [250,240,230],
- magenta: [255,0,255],
- maroon: [128,0,0],
- mediumaquamarine: [102,205,170],
- mediumblue: [0,0,205],
- mediumorchid: [186,85,211],
- mediumpurple: [147,112,219],
- mediumseagreen: [60,179,113],
- mediumslateblue: [123,104,238],
- mediumspringgreen: [0,250,154],
- mediumturquoise: [72,209,204],
- mediumvioletred: [199,21,133],
- midnightblue: [25,25,112],
- mintcream: [245,255,250],
- mistyrose: [255,228,225],
- moccasin: [255,228,181],
- navajowhite: [255,222,173],
- navy: [0,0,128],
- oldlace: [253,245,230],
- olive: [128,128,0],
- olivedrab: [107,142,35],
- orange: [255,165,0],
- orangered: [255,69,0],
- orchid: [218,112,214],
- palegoldenrod: [238,232,170],
- palegreen: [152,251,152],
- paleturquoise: [175,238,238],
- palevioletred: [219,112,147],
- papayawhip: [255,239,213],
- peachpuff: [255,218,185],
- peru: [205,133,63],
- pink: [255,192,203],
- plum: [221,160,221],
- powderblue: [176,224,230],
- purple: [128,0,128],
- rebeccapurple: [102, 51, 153],
- red: [255,0,0],
- rosybrown: [188,143,143],
- royalblue: [65,105,225],
- saddlebrown: [139,69,19],
- salmon: [250,128,114],
- sandybrown: [244,164,96],
- seagreen: [46,139,87],
- seashell: [255,245,238],
- sienna: [160,82,45],
- silver: [192,192,192],
- skyblue: [135,206,235],
- slateblue: [106,90,205],
- slategray: [112,128,144],
- slategrey: [112,128,144],
- snow: [255,250,250],
- springgreen: [0,255,127],
- steelblue: [70,130,180],
- tan: [210,180,140],
- teal: [0,128,128],
- thistle: [216,191,216],
- tomato: [255,99,71],
- turquoise: [64,224,208],
- violet: [238,130,238],
- wheat: [245,222,179],
- white: [255,255,255],
- whitesmoke: [245,245,245],
- yellow: [255,255,0],
- yellowgreen: [154,205,50]
-};
-
-var reverseKeywords = {};
-for (var key in cssKeywords) {
- reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
-}
-
-},{}],4:[function(require,module,exports){
-var conversions = require(3);
-
-var convert = function() {
- return new Converter();
-}
-
-for (var func in conversions) {
- // export Raw versions
- convert[func + "Raw"] = (function(func) {
- // accept array or plain args
- return function(arg) {
- if (typeof arg == "number")
- arg = Array.prototype.slice.call(arguments);
- return conversions[func](arg);
- }
- })(func);
-
- var pair = /(\w+)2(\w+)/.exec(func),
- from = pair[1],
- to = pair[2];
-
- // export rgb2hsl and ["rgb"]["hsl"]
- convert[from] = convert[from] || {};
-
- convert[from][to] = convert[func] = (function(func) {
- return function(arg) {
- if (typeof arg == "number")
- arg = Array.prototype.slice.call(arguments);
-
- var val = conversions[func](arg);
- if (typeof val == "string" || val === undefined)
- return val; // keyword
-
- for (var i = 0; i < val.length; i++)
- val[i] = Math.round(val[i]);
- return val;
- }
- })(func);
-}
-
-
-/* Converter does lazy conversion and caching */
-var Converter = function() {
- this.convs = {};
-};
-
-/* Either get the values for a space or
- set the values for a space, depending on args */
-Converter.prototype.routeSpace = function(space, args) {
- var values = args[0];
- if (values === undefined) {
- // color.rgb()
- return this.getValues(space);
- }
- // color.rgb(10, 10, 10)
- if (typeof values == "number") {
- values = Array.prototype.slice.call(args);
- }
-
- return this.setValues(space, values);
-};
-
-/* Set the values for a space, invalidating cache */
-Converter.prototype.setValues = function(space, values) {
- this.space = space;
- this.convs = {};
- this.convs[space] = values;
- return this;
-};
-
-/* Get the values for a space. If there's already
- a conversion for the space, fetch it, otherwise
- compute it */
-Converter.prototype.getValues = function(space) {
- var vals = this.convs[space];
- if (!vals) {
- var fspace = this.space,
- from = this.convs[fspace];
- vals = convert[fspace][space](from);
-
- this.convs[space] = vals;
- }
- return vals;
-};
-
-["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
- Converter.prototype[space] = function(vals) {
- return this.routeSpace(space, arguments);
- }
-});
-
-module.exports = convert;
-},{"3":3}],5:[function(require,module,exports){
-module.exports = {
- "aliceblue": [240, 248, 255],
- "antiquewhite": [250, 235, 215],
- "aqua": [0, 255, 255],
- "aquamarine": [127, 255, 212],
- "azure": [240, 255, 255],
- "beige": [245, 245, 220],
- "bisque": [255, 228, 196],
- "black": [0, 0, 0],
- "blanchedalmond": [255, 235, 205],
- "blue": [0, 0, 255],
- "blueviolet": [138, 43, 226],
- "brown": [165, 42, 42],
- "burlywood": [222, 184, 135],
- "cadetblue": [95, 158, 160],
- "chartreuse": [127, 255, 0],
- "chocolate": [210, 105, 30],
- "coral": [255, 127, 80],
- "cornflowerblue": [100, 149, 237],
- "cornsilk": [255, 248, 220],
- "crimson": [220, 20, 60],
- "cyan": [0, 255, 255],
- "darkblue": [0, 0, 139],
- "darkcyan": [0, 139, 139],
- "darkgoldenrod": [184, 134, 11],
- "darkgray": [169, 169, 169],
- "darkgreen": [0, 100, 0],
- "darkgrey": [169, 169, 169],
- "darkkhaki": [189, 183, 107],
- "darkmagenta": [139, 0, 139],
- "darkolivegreen": [85, 107, 47],
- "darkorange": [255, 140, 0],
- "darkorchid": [153, 50, 204],
- "darkred": [139, 0, 0],
- "darksalmon": [233, 150, 122],
- "darkseagreen": [143, 188, 143],
- "darkslateblue": [72, 61, 139],
- "darkslategray": [47, 79, 79],
- "darkslategrey": [47, 79, 79],
- "darkturquoise": [0, 206, 209],
- "darkviolet": [148, 0, 211],
- "deeppink": [255, 20, 147],
- "deepskyblue": [0, 191, 255],
- "dimgray": [105, 105, 105],
- "dimgrey": [105, 105, 105],
- "dodgerblue": [30, 144, 255],
- "firebrick": [178, 34, 34],
- "floralwhite": [255, 250, 240],
- "forestgreen": [34, 139, 34],
- "fuchsia": [255, 0, 255],
- "gainsboro": [220, 220, 220],
- "ghostwhite": [248, 248, 255],
- "gold": [255, 215, 0],
- "goldenrod": [218, 165, 32],
- "gray": [128, 128, 128],
- "green": [0, 128, 0],
- "greenyellow": [173, 255, 47],
- "grey": [128, 128, 128],
- "honeydew": [240, 255, 240],
- "hotpink": [255, 105, 180],
- "indianred": [205, 92, 92],
- "indigo": [75, 0, 130],
- "ivory": [255, 255, 240],
- "khaki": [240, 230, 140],
- "lavender": [230, 230, 250],
- "lavenderblush": [255, 240, 245],
- "lawngreen": [124, 252, 0],
- "lemonchiffon": [255, 250, 205],
- "lightblue": [173, 216, 230],
- "lightcoral": [240, 128, 128],
- "lightcyan": [224, 255, 255],
- "lightgoldenrodyellow": [250, 250, 210],
- "lightgray": [211, 211, 211],
- "lightgreen": [144, 238, 144],
- "lightgrey": [211, 211, 211],
- "lightpink": [255, 182, 193],
- "lightsalmon": [255, 160, 122],
- "lightseagreen": [32, 178, 170],
- "lightskyblue": [135, 206, 250],
- "lightslategray": [119, 136, 153],
- "lightslategrey": [119, 136, 153],
- "lightsteelblue": [176, 196, 222],
- "lightyellow": [255, 255, 224],
- "lime": [0, 255, 0],
- "limegreen": [50, 205, 50],
- "linen": [250, 240, 230],
- "magenta": [255, 0, 255],
- "maroon": [128, 0, 0],
- "mediumaquamarine": [102, 205, 170],
- "mediumblue": [0, 0, 205],
- "mediumorchid": [186, 85, 211],
- "mediumpurple": [147, 112, 219],
- "mediumseagreen": [60, 179, 113],
- "mediumslateblue": [123, 104, 238],
- "mediumspringgreen": [0, 250, 154],
- "mediumturquoise": [72, 209, 204],
- "mediumvioletred": [199, 21, 133],
- "midnightblue": [25, 25, 112],
- "mintcream": [245, 255, 250],
- "mistyrose": [255, 228, 225],
- "moccasin": [255, 228, 181],
- "navajowhite": [255, 222, 173],
- "navy": [0, 0, 128],
- "oldlace": [253, 245, 230],
- "olive": [128, 128, 0],
- "olivedrab": [107, 142, 35],
- "orange": [255, 165, 0],
- "orangered": [255, 69, 0],
- "orchid": [218, 112, 214],
- "palegoldenrod": [238, 232, 170],
- "palegreen": [152, 251, 152],
- "paleturquoise": [175, 238, 238],
- "palevioletred": [219, 112, 147],
- "papayawhip": [255, 239, 213],
- "peachpuff": [255, 218, 185],
- "peru": [205, 133, 63],
- "pink": [255, 192, 203],
- "plum": [221, 160, 221],
- "powderblue": [176, 224, 230],
- "purple": [128, 0, 128],
- "rebeccapurple": [102, 51, 153],
- "red": [255, 0, 0],
- "rosybrown": [188, 143, 143],
- "royalblue": [65, 105, 225],
- "saddlebrown": [139, 69, 19],
- "salmon": [250, 128, 114],
- "sandybrown": [244, 164, 96],
- "seagreen": [46, 139, 87],
- "seashell": [255, 245, 238],
- "sienna": [160, 82, 45],
- "silver": [192, 192, 192],
- "skyblue": [135, 206, 235],
- "slateblue": [106, 90, 205],
- "slategray": [112, 128, 144],
- "slategrey": [112, 128, 144],
- "snow": [255, 250, 250],
- "springgreen": [0, 255, 127],
- "steelblue": [70, 130, 180],
- "tan": [210, 180, 140],
- "teal": [0, 128, 128],
- "thistle": [216, 191, 216],
- "tomato": [255, 99, 71],
- "turquoise": [64, 224, 208],
- "violet": [238, 130, 238],
- "wheat": [245, 222, 179],
- "white": [255, 255, 255],
- "whitesmoke": [245, 245, 245],
- "yellow": [255, 255, 0],
- "yellowgreen": [154, 205, 50]
-};
-},{}],6:[function(require,module,exports){
-//! moment.js
-//! version : 2.17.1
-//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
-//! license : MIT
-//! momentjs.com
-
-;(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- global.moment = factory()
-}(this, (function () { 'use strict';
-
-var hookCallback;
-
-function hooks () {
- return hookCallback.apply(null, arguments);
-}
-
-// This is done to register the method called with moment()
-// without creating circular dependencies.
-function setHookCallback (callback) {
- hookCallback = callback;
-}
-
-function isArray(input) {
- return input instanceof Array || Object.prototype.toString.call(input) === '[object Array]';
-}
-
-function isObject(input) {
- // IE8 will treat undefined and null as object if it wasn't for
- // input != null
- return input != null && Object.prototype.toString.call(input) === '[object Object]';
-}
-
-function isObjectEmpty(obj) {
- var k;
- for (k in obj) {
- // even if its not own property I'd still call it non-empty
- return false;
- }
- return true;
-}
-
-function isNumber(input) {
- return typeof input === 'number' || Object.prototype.toString.call(input) === '[object Number]';
-}
-
-function isDate(input) {
- return input instanceof Date || Object.prototype.toString.call(input) === '[object Date]';
-}
-
-function map(arr, fn) {
- var res = [], i;
- for (i = 0; i < arr.length; ++i) {
- res.push(fn(arr[i], i));
- }
- return res;
-}
-
-function hasOwnProp(a, b) {
- return Object.prototype.hasOwnProperty.call(a, b);
-}
-
-function extend(a, b) {
- for (var i in b) {
- if (hasOwnProp(b, i)) {
- a[i] = b[i];
- }
- }
-
- if (hasOwnProp(b, 'toString')) {
- a.toString = b.toString;
- }
-
- if (hasOwnProp(b, 'valueOf')) {
- a.valueOf = b.valueOf;
- }
-
- return a;
-}
-
-function createUTC (input, format, locale, strict) {
- return createLocalOrUTC(input, format, locale, strict, true).utc();
-}
-
-function defaultParsingFlags() {
- // We need to deep clone this object.
- return {
- empty : false,
- unusedTokens : [],
- unusedInput : [],
- overflow : -2,
- charsLeftOver : 0,
- nullInput : false,
- invalidMonth : null,
- invalidFormat : false,
- userInvalidated : false,
- iso : false,
- parsedDateParts : [],
- meridiem : null
- };
-}
-
-function getParsingFlags(m) {
- if (m._pf == null) {
- m._pf = defaultParsingFlags();
- }
- return m._pf;
-}
-
-var some;
-if (Array.prototype.some) {
- some = Array.prototype.some;
-} else {
- some = function (fun) {
- var t = Object(this);
- var len = t.length >>> 0;
-
- for (var i = 0; i < len; i++) {
- if (i in t && fun.call(this, t[i], i, t)) {
- return true;
- }
- }
-
- return false;
- };
-}
-
-var some$1 = some;
-
-function isValid(m) {
- if (m._isValid == null) {
- var flags = getParsingFlags(m);
- var parsedParts = some$1.call(flags.parsedDateParts, function (i) {
- return i != null;
- });
- var isNowValid = !isNaN(m._d.getTime()) &&
- flags.overflow < 0 &&
- !flags.empty &&
- !flags.invalidMonth &&
- !flags.invalidWeekday &&
- !flags.nullInput &&
- !flags.invalidFormat &&
- !flags.userInvalidated &&
- (!flags.meridiem || (flags.meridiem && parsedParts));
-
- if (m._strict) {
- isNowValid = isNowValid &&
- flags.charsLeftOver === 0 &&
- flags.unusedTokens.length === 0 &&
- flags.bigHour === undefined;
- }
-
- if (Object.isFrozen == null || !Object.isFrozen(m)) {
- m._isValid = isNowValid;
- }
- else {
- return isNowValid;
- }
- }
- return m._isValid;
-}
-
-function createInvalid (flags) {
- var m = createUTC(NaN);
- if (flags != null) {
- extend(getParsingFlags(m), flags);
- }
- else {
- getParsingFlags(m).userInvalidated = true;
- }
-
- return m;
-}
-
-function isUndefined(input) {
- return input === void 0;
-}
-
-// Plugins that add properties should also add the key here (null value),
-// so we can properly clone ourselves.
-var momentProperties = hooks.momentProperties = [];
-
-function copyConfig(to, from) {
- var i, prop, val;
-
- if (!isUndefined(from._isAMomentObject)) {
- to._isAMomentObject = from._isAMomentObject;
- }
- if (!isUndefined(from._i)) {
- to._i = from._i;
- }
- if (!isUndefined(from._f)) {
- to._f = from._f;
- }
- if (!isUndefined(from._l)) {
- to._l = from._l;
- }
- if (!isUndefined(from._strict)) {
- to._strict = from._strict;
- }
- if (!isUndefined(from._tzm)) {
- to._tzm = from._tzm;
- }
- if (!isUndefined(from._isUTC)) {
- to._isUTC = from._isUTC;
- }
- if (!isUndefined(from._offset)) {
- to._offset = from._offset;
- }
- if (!isUndefined(from._pf)) {
- to._pf = getParsingFlags(from);
- }
- if (!isUndefined(from._locale)) {
- to._locale = from._locale;
- }
-
- if (momentProperties.length > 0) {
- for (i in momentProperties) {
- prop = momentProperties[i];
- val = from[prop];
- if (!isUndefined(val)) {
- to[prop] = val;
- }
- }
- }
-
- return to;
-}
-
-var updateInProgress = false;
-
-// Moment prototype object
-function Moment(config) {
- copyConfig(this, config);
- this._d = new Date(config._d != null ? config._d.getTime() : NaN);
- if (!this.isValid()) {
- this._d = new Date(NaN);
- }
- // Prevent infinite loop in case updateOffset creates new moment
- // objects.
- if (updateInProgress === false) {
- updateInProgress = true;
- hooks.updateOffset(this);
- updateInProgress = false;
- }
-}
-
-function isMoment (obj) {
- return obj instanceof Moment || (obj != null && obj._isAMomentObject != null);
-}
-
-function absFloor (number) {
- if (number < 0) {
- // -0 -> 0
- return Math.ceil(number) || 0;
- } else {
- return Math.floor(number);
- }
-}
-
-function toInt(argumentForCoercion) {
- var coercedNumber = +argumentForCoercion,
- value = 0;
-
- if (coercedNumber !== 0 && isFinite(coercedNumber)) {
- value = absFloor(coercedNumber);
- }
-
- return value;
-}
-
-// compare two arrays, return the number of differences
-function compareArrays(array1, array2, dontConvert) {
- var len = Math.min(array1.length, array2.length),
- lengthDiff = Math.abs(array1.length - array2.length),
- diffs = 0,
- i;
- for (i = 0; i < len; i++) {
- if ((dontConvert && array1[i] !== array2[i]) ||
- (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) {
- diffs++;
- }
- }
- return diffs + lengthDiff;
-}
-
-function warn(msg) {
- if (hooks.suppressDeprecationWarnings === false &&
- (typeof console !== 'undefined') && console.warn) {
- console.warn('Deprecation warning: ' + msg);
- }
-}
-
-function deprecate(msg, fn) {
- var firstTime = true;
-
- return extend(function () {
- if (hooks.deprecationHandler != null) {
- hooks.deprecationHandler(null, msg);
- }
- if (firstTime) {
- var args = [];
- var arg;
- for (var i = 0; i < arguments.length; i++) {
- arg = '';
- if (typeof arguments[i] === 'object') {
- arg += '\n[' + i + '] ';
- for (var key in arguments[0]) {
- arg += key + ': ' + arguments[0][key] + ', ';
- }
- arg = arg.slice(0, -2); // Remove trailing comma and space
- } else {
- arg = arguments[i];
- }
- args.push(arg);
- }
- warn(msg + '\nArguments: ' + Array.prototype.slice.call(args).join('') + '\n' + (new Error()).stack);
- firstTime = false;
- }
- return fn.apply(this, arguments);
- }, fn);
-}
-
-var deprecations = {};
-
-function deprecateSimple(name, msg) {
- if (hooks.deprecationHandler != null) {
- hooks.deprecationHandler(name, msg);
- }
- if (!deprecations[name]) {
- warn(msg);
- deprecations[name] = true;
- }
-}
-
-hooks.suppressDeprecationWarnings = false;
-hooks.deprecationHandler = null;
-
-function isFunction(input) {
- return input instanceof Function || Object.prototype.toString.call(input) === '[object Function]';
-}
-
-function set (config) {
- var prop, i;
- for (i in config) {
- prop = config[i];
- if (isFunction(prop)) {
- this[i] = prop;
- } else {
- this['_' + i] = prop;
- }
- }
- this._config = config;
- // Lenient ordinal parsing accepts just a number in addition to
- // number + (possibly) stuff coming from _ordinalParseLenient.
- this._ordinalParseLenient = new RegExp(this._ordinalParse.source + '|' + (/\d{1,2}/).source);
-}
-
-function mergeConfigs(parentConfig, childConfig) {
- var res = extend({}, parentConfig), prop;
- for (prop in childConfig) {
- if (hasOwnProp(childConfig, prop)) {
- if (isObject(parentConfig[prop]) && isObject(childConfig[prop])) {
- res[prop] = {};
- extend(res[prop], parentConfig[prop]);
- extend(res[prop], childConfig[prop]);
- } else if (childConfig[prop] != null) {
- res[prop] = childConfig[prop];
- } else {
- delete res[prop];
- }
- }
- }
- for (prop in parentConfig) {
- if (hasOwnProp(parentConfig, prop) &&
- !hasOwnProp(childConfig, prop) &&
- isObject(parentConfig[prop])) {
- // make sure changes to properties don't modify parent config
- res[prop] = extend({}, res[prop]);
- }
- }
- return res;
-}
-
-function Locale(config) {
- if (config != null) {
- this.set(config);
- }
-}
-
-var keys;
-
-if (Object.keys) {
- keys = Object.keys;
-} else {
- keys = function (obj) {
- var i, res = [];
- for (i in obj) {
- if (hasOwnProp(obj, i)) {
- res.push(i);
- }
- }
- return res;
- };
-}
-
-var keys$1 = keys;
-
-var defaultCalendar = {
- sameDay : '[Today at] LT',
- nextDay : '[Tomorrow at] LT',
- nextWeek : 'dddd [at] LT',
- lastDay : '[Yesterday at] LT',
- lastWeek : '[Last] dddd [at] LT',
- sameElse : 'L'
-};
-
-function calendar (key, mom, now) {
- var output = this._calendar[key] || this._calendar['sameElse'];
- return isFunction(output) ? output.call(mom, now) : output;
-}
-
-var defaultLongDateFormat = {
- LTS : 'h:mm:ss A',
- LT : 'h:mm A',
- L : 'MM/DD/YYYY',
- LL : 'MMMM D, YYYY',
- LLL : 'MMMM D, YYYY h:mm A',
- LLLL : 'dddd, MMMM D, YYYY h:mm A'
-};
-
-function longDateFormat (key) {
- var format = this._longDateFormat[key],
- formatUpper = this._longDateFormat[key.toUpperCase()];
-
- if (format || !formatUpper) {
- return format;
- }
-
- this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) {
- return val.slice(1);
- });
-
- return this._longDateFormat[key];
-}
-
-var defaultInvalidDate = 'Invalid date';
-
-function invalidDate () {
- return this._invalidDate;
-}
-
-var defaultOrdinal = '%d';
-var defaultOrdinalParse = /\d{1,2}/;
-
-function ordinal (number) {
- return this._ordinal.replace('%d', number);
-}
-
-var defaultRelativeTime = {
- future : 'in %s',
- past : '%s ago',
- s : 'a few seconds',
- m : 'a minute',
- mm : '%d minutes',
- h : 'an hour',
- hh : '%d hours',
- d : 'a day',
- dd : '%d days',
- M : 'a month',
- MM : '%d months',
- y : 'a year',
- yy : '%d years'
-};
-
-function relativeTime (number, withoutSuffix, string, isFuture) {
- var output = this._relativeTime[string];
- return (isFunction(output)) ?
- output(number, withoutSuffix, string, isFuture) :
- output.replace(/%d/i, number);
-}
-
-function pastFuture (diff, output) {
- var format = this._relativeTime[diff > 0 ? 'future' : 'past'];
- return isFunction(format) ? format(output) : format.replace(/%s/i, output);
-}
-
-var aliases = {};
-
-function addUnitAlias (unit, shorthand) {
- var lowerCase = unit.toLowerCase();
- aliases[lowerCase] = aliases[lowerCase + 's'] = aliases[shorthand] = unit;
-}
-
-function normalizeUnits(units) {
- return typeof units === 'string' ? aliases[units] || aliases[units.toLowerCase()] : undefined;
-}
-
-function normalizeObjectUnits(inputObject) {
- var normalizedInput = {},
- normalizedProp,
- prop;
-
- for (prop in inputObject) {
- if (hasOwnProp(inputObject, prop)) {
- normalizedProp = normalizeUnits(prop);
- if (normalizedProp) {
- normalizedInput[normalizedProp] = inputObject[prop];
- }
- }
- }
-
- return normalizedInput;
-}
-
-var priorities = {};
-
-function addUnitPriority(unit, priority) {
- priorities[unit] = priority;
-}
-
-function getPrioritizedUnits(unitsObj) {
- var units = [];
- for (var u in unitsObj) {
- units.push({unit: u, priority: priorities[u]});
- }
- units.sort(function (a, b) {
- return a.priority - b.priority;
- });
- return units;
-}
-
-function makeGetSet (unit, keepTime) {
- return function (value) {
- if (value != null) {
- set$1(this, unit, value);
- hooks.updateOffset(this, keepTime);
- return this;
- } else {
- return get(this, unit);
- }
- };
-}
-
-function get (mom, unit) {
- return mom.isValid() ?
- mom._d['get' + (mom._isUTC ? 'UTC' : '') + unit]() : NaN;
-}
-
-function set$1 (mom, unit, value) {
- if (mom.isValid()) {
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + unit](value);
- }
-}
-
-// MOMENTS
-
-function stringGet (units) {
- units = normalizeUnits(units);
- if (isFunction(this[units])) {
- return this[units]();
- }
- return this;
-}
-
-
-function stringSet (units, value) {
- if (typeof units === 'object') {
- units = normalizeObjectUnits(units);
- var prioritized = getPrioritizedUnits(units);
- for (var i = 0; i < prioritized.length; i++) {
- this[prioritized[i].unit](units[prioritized[i].unit]);
- }
- } else {
- units = normalizeUnits(units);
- if (isFunction(this[units])) {
- return this[units](value);
- }
- }
- return this;
-}
-
-function zeroFill(number, targetLength, forceSign) {
- var absNumber = '' + Math.abs(number),
- zerosToFill = targetLength - absNumber.length,
- sign = number >= 0;
- return (sign ? (forceSign ? '+' : '') : '-') +
- Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber;
-}
-
-var formattingTokens = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g;
-
-var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g;
-
-var formatFunctions = {};
-
-var formatTokenFunctions = {};
-
-// token: 'M'
-// padded: ['MM', 2]
-// ordinal: 'Mo'
-// callback: function () { this.month() + 1 }
-function addFormatToken (token, padded, ordinal, callback) {
- var func = callback;
- if (typeof callback === 'string') {
- func = function () {
- return this[callback]();
- };
- }
- if (token) {
- formatTokenFunctions[token] = func;
- }
- if (padded) {
- formatTokenFunctions[padded[0]] = function () {
- return zeroFill(func.apply(this, arguments), padded[1], padded[2]);
- };
- }
- if (ordinal) {
- formatTokenFunctions[ordinal] = function () {
- return this.localeData().ordinal(func.apply(this, arguments), token);
- };
- }
-}
-
-function removeFormattingTokens(input) {
- if (input.match(/\[[\s\S]/)) {
- return input.replace(/^\[|\]$/g, '');
- }
- return input.replace(/\\/g, '');
-}
-
-function makeFormatFunction(format) {
- var array = format.match(formattingTokens), i, length;
-
- for (i = 0, length = array.length; i < length; i++) {
- if (formatTokenFunctions[array[i]]) {
- array[i] = formatTokenFunctions[array[i]];
- } else {
- array[i] = removeFormattingTokens(array[i]);
- }
- }
-
- return function (mom) {
- var output = '', i;
- for (i = 0; i < length; i++) {
- output += array[i] instanceof Function ? array[i].call(mom, format) : array[i];
- }
- return output;
- };
-}
-
-// format date using native date object
-function formatMoment(m, format) {
- if (!m.isValid()) {
- return m.localeData().invalidDate();
- }
-
- format = expandFormat(format, m.localeData());
- formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format);
-
- return formatFunctions[format](m);
-}
-
-function expandFormat(format, locale) {
- var i = 5;
-
- function replaceLongDateFormatTokens(input) {
- return locale.longDateFormat(input) || input;
- }
-
- localFormattingTokens.lastIndex = 0;
- while (i >= 0 && localFormattingTokens.test(format)) {
- format = format.replace(localFormattingTokens, replaceLongDateFormatTokens);
- localFormattingTokens.lastIndex = 0;
- i -= 1;
- }
-
- return format;
-}
-
-var match1 = /\d/; // 0 - 9
-var match2 = /\d\d/; // 00 - 99
-var match3 = /\d{3}/; // 000 - 999
-var match4 = /\d{4}/; // 0000 - 9999
-var match6 = /[+-]?\d{6}/; // -999999 - 999999
-var match1to2 = /\d\d?/; // 0 - 99
-var match3to4 = /\d\d\d\d?/; // 999 - 9999
-var match5to6 = /\d\d\d\d\d\d?/; // 99999 - 999999
-var match1to3 = /\d{1,3}/; // 0 - 999
-var match1to4 = /\d{1,4}/; // 0 - 9999
-var match1to6 = /[+-]?\d{1,6}/; // -999999 - 999999
-
-var matchUnsigned = /\d+/; // 0 - inf
-var matchSigned = /[+-]?\d+/; // -inf - inf
-
-var matchOffset = /Z|[+-]\d\d:?\d\d/gi; // +00:00 -00:00 +0000 -0000 or Z
-var matchShortOffset = /Z|[+-]\d\d(?::?\d\d)?/gi; // +00 -00 +00:00 -00:00 +0000 -0000 or Z
-
-var matchTimestamp = /[+-]?\d+(\.\d{1,3})?/; // 123456789 123456789.123
-
-// any word (or two) characters or numbers including two/three word month in arabic.
-// includes scottish gaelic two word and hyphenated months
-var matchWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i;
-
-
-var regexes = {};
-
-function addRegexToken (token, regex, strictRegex) {
- regexes[token] = isFunction(regex) ? regex : function (isStrict, localeData) {
- return (isStrict && strictRegex) ? strictRegex : regex;
- };
-}
-
-function getParseRegexForToken (token, config) {
- if (!hasOwnProp(regexes, token)) {
- return new RegExp(unescapeFormat(token));
- }
-
- return regexes[token](config._strict, config._locale);
-}
-
-// Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
-function unescapeFormat(s) {
- return regexEscape(s.replace('\\', '').replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) {
- return p1 || p2 || p3 || p4;
- }));
-}
-
-function regexEscape(s) {
- return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
-}
-
-var tokens = {};
-
-function addParseToken (token, callback) {
- var i, func = callback;
- if (typeof token === 'string') {
- token = [token];
- }
- if (isNumber(callback)) {
- func = function (input, array) {
- array[callback] = toInt(input);
- };
- }
- for (i = 0; i < token.length; i++) {
- tokens[token[i]] = func;
- }
-}
-
-function addWeekParseToken (token, callback) {
- addParseToken(token, function (input, array, config, token) {
- config._w = config._w || {};
- callback(input, config._w, config, token);
- });
-}
-
-function addTimeToArrayFromToken(token, input, config) {
- if (input != null && hasOwnProp(tokens, token)) {
- tokens[token](input, config._a, config, token);
- }
-}
-
-var YEAR = 0;
-var MONTH = 1;
-var DATE = 2;
-var HOUR = 3;
-var MINUTE = 4;
-var SECOND = 5;
-var MILLISECOND = 6;
-var WEEK = 7;
-var WEEKDAY = 8;
-
-var indexOf;
-
-if (Array.prototype.indexOf) {
- indexOf = Array.prototype.indexOf;
-} else {
- indexOf = function (o) {
- // I know
- var i;
- for (i = 0; i < this.length; ++i) {
- if (this[i] === o) {
- return i;
- }
- }
- return -1;
- };
-}
-
-var indexOf$1 = indexOf;
-
-function daysInMonth(year, month) {
- return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
-}
-
-// FORMATTING
-
-addFormatToken('M', ['MM', 2], 'Mo', function () {
- return this.month() + 1;
-});
-
-addFormatToken('MMM', 0, 0, function (format) {
- return this.localeData().monthsShort(this, format);
-});
-
-addFormatToken('MMMM', 0, 0, function (format) {
- return this.localeData().months(this, format);
-});
-
-// ALIASES
-
-addUnitAlias('month', 'M');
-
-// PRIORITY
-
-addUnitPriority('month', 8);
-
-// PARSING
-
-addRegexToken('M', match1to2);
-addRegexToken('MM', match1to2, match2);
-addRegexToken('MMM', function (isStrict, locale) {
- return locale.monthsShortRegex(isStrict);
-});
-addRegexToken('MMMM', function (isStrict, locale) {
- return locale.monthsRegex(isStrict);
-});
-
-addParseToken(['M', 'MM'], function (input, array) {
- array[MONTH] = toInt(input) - 1;
-});
-
-addParseToken(['MMM', 'MMMM'], function (input, array, config, token) {
- var month = config._locale.monthsParse(input, token, config._strict);
- // if we didn't find a month name, mark the date as invalid.
- if (month != null) {
- array[MONTH] = month;
- } else {
- getParsingFlags(config).invalidMonth = input;
- }
-});
-
-// LOCALES
-
-var MONTHS_IN_FORMAT = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/;
-var defaultLocaleMonths = 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_');
-function localeMonths (m, format) {
- if (!m) {
- return this._months;
- }
- return isArray(this._months) ? this._months[m.month()] :
- this._months[(this._months.isFormat || MONTHS_IN_FORMAT).test(format) ? 'format' : 'standalone'][m.month()];
-}
-
-var defaultLocaleMonthsShort = 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_');
-function localeMonthsShort (m, format) {
- if (!m) {
- return this._monthsShort;
- }
- return isArray(this._monthsShort) ? this._monthsShort[m.month()] :
- this._monthsShort[MONTHS_IN_FORMAT.test(format) ? 'format' : 'standalone'][m.month()];
-}
-
-function handleStrictParse(monthName, format, strict) {
- var i, ii, mom, llc = monthName.toLocaleLowerCase();
- if (!this._monthsParse) {
- // this is not used
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- for (i = 0; i < 12; ++i) {
- mom = createUTC([2000, i]);
- this._shortMonthsParse[i] = this.monthsShort(mom, '').toLocaleLowerCase();
- this._longMonthsParse[i] = this.months(mom, '').toLocaleLowerCase();
- }
- }
-
- if (strict) {
- if (format === 'MMM') {
- ii = indexOf$1.call(this._shortMonthsParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf$1.call(this._longMonthsParse, llc);
- return ii !== -1 ? ii : null;
- }
- } else {
- if (format === 'MMM') {
- ii = indexOf$1.call(this._shortMonthsParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._longMonthsParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf$1.call(this._longMonthsParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._shortMonthsParse, llc);
- return ii !== -1 ? ii : null;
- }
- }
-}
-
-function localeMonthsParse (monthName, format, strict) {
- var i, mom, regex;
-
- if (this._monthsParseExact) {
- return handleStrictParse.call(this, monthName, format, strict);
- }
-
- if (!this._monthsParse) {
- this._monthsParse = [];
- this._longMonthsParse = [];
- this._shortMonthsParse = [];
- }
-
- // TODO: add sorting
- // Sorting makes sure if one month (or abbr) is a prefix of another
- // see sorting in computeMonthsParse
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = createUTC([2000, i]);
- if (strict && !this._longMonthsParse[i]) {
- this._longMonthsParse[i] = new RegExp('^' + this.months(mom, '').replace('.', '') + '$', 'i');
- this._shortMonthsParse[i] = new RegExp('^' + this.monthsShort(mom, '').replace('.', '') + '$', 'i');
- }
- if (!strict && !this._monthsParse[i]) {
- regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, '');
- this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'MMMM' && this._longMonthsParse[i].test(monthName)) {
- return i;
- } else if (strict && format === 'MMM' && this._shortMonthsParse[i].test(monthName)) {
- return i;
- } else if (!strict && this._monthsParse[i].test(monthName)) {
- return i;
- }
- }
-}
-
-// MOMENTS
-
-function setMonth (mom, value) {
- var dayOfMonth;
-
- if (!mom.isValid()) {
- // No op
- return mom;
- }
-
- if (typeof value === 'string') {
- if (/^\d+$/.test(value)) {
- value = toInt(value);
- } else {
- value = mom.localeData().monthsParse(value);
- // TODO: Another silent failure?
- if (!isNumber(value)) {
- return mom;
- }
- }
- }
-
- dayOfMonth = Math.min(mom.date(), daysInMonth(mom.year(), value));
- mom._d['set' + (mom._isUTC ? 'UTC' : '') + 'Month'](value, dayOfMonth);
- return mom;
-}
-
-function getSetMonth (value) {
- if (value != null) {
- setMonth(this, value);
- hooks.updateOffset(this, true);
- return this;
- } else {
- return get(this, 'Month');
- }
-}
-
-function getDaysInMonth () {
- return daysInMonth(this.year(), this.month());
-}
-
-var defaultMonthsShortRegex = matchWord;
-function monthsShortRegex (isStrict) {
- if (this._monthsParseExact) {
- if (!hasOwnProp(this, '_monthsRegex')) {
- computeMonthsParse.call(this);
- }
- if (isStrict) {
- return this._monthsShortStrictRegex;
- } else {
- return this._monthsShortRegex;
- }
- } else {
- if (!hasOwnProp(this, '_monthsShortRegex')) {
- this._monthsShortRegex = defaultMonthsShortRegex;
- }
- return this._monthsShortStrictRegex && isStrict ?
- this._monthsShortStrictRegex : this._monthsShortRegex;
- }
-}
-
-var defaultMonthsRegex = matchWord;
-function monthsRegex (isStrict) {
- if (this._monthsParseExact) {
- if (!hasOwnProp(this, '_monthsRegex')) {
- computeMonthsParse.call(this);
- }
- if (isStrict) {
- return this._monthsStrictRegex;
- } else {
- return this._monthsRegex;
- }
- } else {
- if (!hasOwnProp(this, '_monthsRegex')) {
- this._monthsRegex = defaultMonthsRegex;
- }
- return this._monthsStrictRegex && isStrict ?
- this._monthsStrictRegex : this._monthsRegex;
- }
-}
-
-function computeMonthsParse () {
- function cmpLenRev(a, b) {
- return b.length - a.length;
- }
-
- var shortPieces = [], longPieces = [], mixedPieces = [],
- i, mom;
- for (i = 0; i < 12; i++) {
- // make the regex if we don't have it already
- mom = createUTC([2000, i]);
- shortPieces.push(this.monthsShort(mom, ''));
- longPieces.push(this.months(mom, ''));
- mixedPieces.push(this.months(mom, ''));
- mixedPieces.push(this.monthsShort(mom, ''));
- }
- // Sorting makes sure if one month (or abbr) is a prefix of another it
- // will match the longer piece.
- shortPieces.sort(cmpLenRev);
- longPieces.sort(cmpLenRev);
- mixedPieces.sort(cmpLenRev);
- for (i = 0; i < 12; i++) {
- shortPieces[i] = regexEscape(shortPieces[i]);
- longPieces[i] = regexEscape(longPieces[i]);
- }
- for (i = 0; i < 24; i++) {
- mixedPieces[i] = regexEscape(mixedPieces[i]);
- }
-
- this._monthsRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
- this._monthsShortRegex = this._monthsRegex;
- this._monthsStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
- this._monthsShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
-}
-
-// FORMATTING
-
-addFormatToken('Y', 0, 0, function () {
- var y = this.year();
- return y <= 9999 ? '' + y : '+' + y;
-});
-
-addFormatToken(0, ['YY', 2], 0, function () {
- return this.year() % 100;
-});
-
-addFormatToken(0, ['YYYY', 4], 0, 'year');
-addFormatToken(0, ['YYYYY', 5], 0, 'year');
-addFormatToken(0, ['YYYYYY', 6, true], 0, 'year');
-
-// ALIASES
-
-addUnitAlias('year', 'y');
-
-// PRIORITIES
-
-addUnitPriority('year', 1);
-
-// PARSING
-
-addRegexToken('Y', matchSigned);
-addRegexToken('YY', match1to2, match2);
-addRegexToken('YYYY', match1to4, match4);
-addRegexToken('YYYYY', match1to6, match6);
-addRegexToken('YYYYYY', match1to6, match6);
-
-addParseToken(['YYYYY', 'YYYYYY'], YEAR);
-addParseToken('YYYY', function (input, array) {
- array[YEAR] = input.length === 2 ? hooks.parseTwoDigitYear(input) : toInt(input);
-});
-addParseToken('YY', function (input, array) {
- array[YEAR] = hooks.parseTwoDigitYear(input);
-});
-addParseToken('Y', function (input, array) {
- array[YEAR] = parseInt(input, 10);
-});
-
-// HELPERS
-
-function daysInYear(year) {
- return isLeapYear(year) ? 366 : 365;
-}
-
-function isLeapYear(year) {
- return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
-}
-
-// HOOKS
-
-hooks.parseTwoDigitYear = function (input) {
- return toInt(input) + (toInt(input) > 68 ? 1900 : 2000);
-};
-
-// MOMENTS
-
-var getSetYear = makeGetSet('FullYear', true);
-
-function getIsLeapYear () {
- return isLeapYear(this.year());
-}
-
-function createDate (y, m, d, h, M, s, ms) {
- //can't just apply() to create a date:
- //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply
- var date = new Date(y, m, d, h, M, s, ms);
-
- //the date constructor remaps years 0-99 to 1900-1999
- if (y < 100 && y >= 0 && isFinite(date.getFullYear())) {
- date.setFullYear(y);
- }
- return date;
-}
-
-function createUTCDate (y) {
- var date = new Date(Date.UTC.apply(null, arguments));
-
- //the Date.UTC function remaps years 0-99 to 1900-1999
- if (y < 100 && y >= 0 && isFinite(date.getUTCFullYear())) {
- date.setUTCFullYear(y);
- }
- return date;
-}
-
-// start-of-first-week - start-of-year
-function firstWeekOffset(year, dow, doy) {
- var // first-week day -- which january is always in the first week (4 for iso, 1 for other)
- fwd = 7 + dow - doy,
- // first-week day local weekday -- which local weekday is fwd
- fwdlw = (7 + createUTCDate(year, 0, fwd).getUTCDay() - dow) % 7;
-
- return -fwdlw + fwd - 1;
-}
-
-//http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday
-function dayOfYearFromWeeks(year, week, weekday, dow, doy) {
- var localWeekday = (7 + weekday - dow) % 7,
- weekOffset = firstWeekOffset(year, dow, doy),
- dayOfYear = 1 + 7 * (week - 1) + localWeekday + weekOffset,
- resYear, resDayOfYear;
-
- if (dayOfYear <= 0) {
- resYear = year - 1;
- resDayOfYear = daysInYear(resYear) + dayOfYear;
- } else if (dayOfYear > daysInYear(year)) {
- resYear = year + 1;
- resDayOfYear = dayOfYear - daysInYear(year);
- } else {
- resYear = year;
- resDayOfYear = dayOfYear;
- }
-
- return {
- year: resYear,
- dayOfYear: resDayOfYear
- };
-}
-
-function weekOfYear(mom, dow, doy) {
- var weekOffset = firstWeekOffset(mom.year(), dow, doy),
- week = Math.floor((mom.dayOfYear() - weekOffset - 1) / 7) + 1,
- resWeek, resYear;
-
- if (week < 1) {
- resYear = mom.year() - 1;
- resWeek = week + weeksInYear(resYear, dow, doy);
- } else if (week > weeksInYear(mom.year(), dow, doy)) {
- resWeek = week - weeksInYear(mom.year(), dow, doy);
- resYear = mom.year() + 1;
- } else {
- resYear = mom.year();
- resWeek = week;
- }
-
- return {
- week: resWeek,
- year: resYear
- };
-}
-
-function weeksInYear(year, dow, doy) {
- var weekOffset = firstWeekOffset(year, dow, doy),
- weekOffsetNext = firstWeekOffset(year + 1, dow, doy);
- return (daysInYear(year) - weekOffset + weekOffsetNext) / 7;
-}
-
-// FORMATTING
-
-addFormatToken('w', ['ww', 2], 'wo', 'week');
-addFormatToken('W', ['WW', 2], 'Wo', 'isoWeek');
-
-// ALIASES
-
-addUnitAlias('week', 'w');
-addUnitAlias('isoWeek', 'W');
-
-// PRIORITIES
-
-addUnitPriority('week', 5);
-addUnitPriority('isoWeek', 5);
-
-// PARSING
-
-addRegexToken('w', match1to2);
-addRegexToken('ww', match1to2, match2);
-addRegexToken('W', match1to2);
-addRegexToken('WW', match1to2, match2);
-
-addWeekParseToken(['w', 'ww', 'W', 'WW'], function (input, week, config, token) {
- week[token.substr(0, 1)] = toInt(input);
-});
-
-// HELPERS
-
-// LOCALES
-
-function localeWeek (mom) {
- return weekOfYear(mom, this._week.dow, this._week.doy).week;
-}
-
-var defaultLocaleWeek = {
- dow : 0, // Sunday is the first day of the week.
- doy : 6 // The week that contains Jan 1st is the first week of the year.
-};
-
-function localeFirstDayOfWeek () {
- return this._week.dow;
-}
-
-function localeFirstDayOfYear () {
- return this._week.doy;
-}
-
-// MOMENTS
-
-function getSetWeek (input) {
- var week = this.localeData().week(this);
- return input == null ? week : this.add((input - week) * 7, 'd');
-}
-
-function getSetISOWeek (input) {
- var week = weekOfYear(this, 1, 4).week;
- return input == null ? week : this.add((input - week) * 7, 'd');
-}
-
-// FORMATTING
-
-addFormatToken('d', 0, 'do', 'day');
-
-addFormatToken('dd', 0, 0, function (format) {
- return this.localeData().weekdaysMin(this, format);
-});
-
-addFormatToken('ddd', 0, 0, function (format) {
- return this.localeData().weekdaysShort(this, format);
-});
-
-addFormatToken('dddd', 0, 0, function (format) {
- return this.localeData().weekdays(this, format);
-});
-
-addFormatToken('e', 0, 0, 'weekday');
-addFormatToken('E', 0, 0, 'isoWeekday');
-
-// ALIASES
-
-addUnitAlias('day', 'd');
-addUnitAlias('weekday', 'e');
-addUnitAlias('isoWeekday', 'E');
-
-// PRIORITY
-addUnitPriority('day', 11);
-addUnitPriority('weekday', 11);
-addUnitPriority('isoWeekday', 11);
-
-// PARSING
-
-addRegexToken('d', match1to2);
-addRegexToken('e', match1to2);
-addRegexToken('E', match1to2);
-addRegexToken('dd', function (isStrict, locale) {
- return locale.weekdaysMinRegex(isStrict);
-});
-addRegexToken('ddd', function (isStrict, locale) {
- return locale.weekdaysShortRegex(isStrict);
-});
-addRegexToken('dddd', function (isStrict, locale) {
- return locale.weekdaysRegex(isStrict);
-});
-
-addWeekParseToken(['dd', 'ddd', 'dddd'], function (input, week, config, token) {
- var weekday = config._locale.weekdaysParse(input, token, config._strict);
- // if we didn't get a weekday name, mark the date as invalid
- if (weekday != null) {
- week.d = weekday;
- } else {
- getParsingFlags(config).invalidWeekday = input;
- }
-});
-
-addWeekParseToken(['d', 'e', 'E'], function (input, week, config, token) {
- week[token] = toInt(input);
-});
-
-// HELPERS
-
-function parseWeekday(input, locale) {
- if (typeof input !== 'string') {
- return input;
- }
-
- if (!isNaN(input)) {
- return parseInt(input, 10);
- }
-
- input = locale.weekdaysParse(input);
- if (typeof input === 'number') {
- return input;
- }
-
- return null;
-}
-
-function parseIsoWeekday(input, locale) {
- if (typeof input === 'string') {
- return locale.weekdaysParse(input) % 7 || 7;
- }
- return isNaN(input) ? null : input;
-}
-
-// LOCALES
-
-var defaultLocaleWeekdays = 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_');
-function localeWeekdays (m, format) {
- if (!m) {
- return this._weekdays;
- }
- return isArray(this._weekdays) ? this._weekdays[m.day()] :
- this._weekdays[this._weekdays.isFormat.test(format) ? 'format' : 'standalone'][m.day()];
-}
-
-var defaultLocaleWeekdaysShort = 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_');
-function localeWeekdaysShort (m) {
- return (m) ? this._weekdaysShort[m.day()] : this._weekdaysShort;
-}
-
-var defaultLocaleWeekdaysMin = 'Su_Mo_Tu_We_Th_Fr_Sa'.split('_');
-function localeWeekdaysMin (m) {
- return (m) ? this._weekdaysMin[m.day()] : this._weekdaysMin;
-}
-
-function handleStrictParse$1(weekdayName, format, strict) {
- var i, ii, mom, llc = weekdayName.toLocaleLowerCase();
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- this._shortWeekdaysParse = [];
- this._minWeekdaysParse = [];
-
- for (i = 0; i < 7; ++i) {
- mom = createUTC([2000, 1]).day(i);
- this._minWeekdaysParse[i] = this.weekdaysMin(mom, '').toLocaleLowerCase();
- this._shortWeekdaysParse[i] = this.weekdaysShort(mom, '').toLocaleLowerCase();
- this._weekdaysParse[i] = this.weekdays(mom, '').toLocaleLowerCase();
- }
- }
-
- if (strict) {
- if (format === 'dddd') {
- ii = indexOf$1.call(this._weekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else if (format === 'ddd') {
- ii = indexOf$1.call(this._shortWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf$1.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- }
- } else {
- if (format === 'dddd') {
- ii = indexOf$1.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._shortWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else if (format === 'ddd') {
- ii = indexOf$1.call(this._shortWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._minWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- } else {
- ii = indexOf$1.call(this._minWeekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._weekdaysParse, llc);
- if (ii !== -1) {
- return ii;
- }
- ii = indexOf$1.call(this._shortWeekdaysParse, llc);
- return ii !== -1 ? ii : null;
- }
- }
-}
-
-function localeWeekdaysParse (weekdayName, format, strict) {
- var i, mom, regex;
-
- if (this._weekdaysParseExact) {
- return handleStrictParse$1.call(this, weekdayName, format, strict);
- }
-
- if (!this._weekdaysParse) {
- this._weekdaysParse = [];
- this._minWeekdaysParse = [];
- this._shortWeekdaysParse = [];
- this._fullWeekdaysParse = [];
- }
-
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
-
- mom = createUTC([2000, 1]).day(i);
- if (strict && !this._fullWeekdaysParse[i]) {
- this._fullWeekdaysParse[i] = new RegExp('^' + this.weekdays(mom, '').replace('.', '\.?') + '$', 'i');
- this._shortWeekdaysParse[i] = new RegExp('^' + this.weekdaysShort(mom, '').replace('.', '\.?') + '$', 'i');
- this._minWeekdaysParse[i] = new RegExp('^' + this.weekdaysMin(mom, '').replace('.', '\.?') + '$', 'i');
- }
- if (!this._weekdaysParse[i]) {
- regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, '');
- this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i');
- }
- // test the regex
- if (strict && format === 'dddd' && this._fullWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (strict && format === 'ddd' && this._shortWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (strict && format === 'dd' && this._minWeekdaysParse[i].test(weekdayName)) {
- return i;
- } else if (!strict && this._weekdaysParse[i].test(weekdayName)) {
- return i;
- }
- }
-}
-
-// MOMENTS
-
-function getSetDayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
- var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
- if (input != null) {
- input = parseWeekday(input, this.localeData());
- return this.add(input - day, 'd');
- } else {
- return day;
- }
-}
-
-function getSetLocaleDayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
- var weekday = (this.day() + 7 - this.localeData()._week.dow) % 7;
- return input == null ? weekday : this.add(input - weekday, 'd');
-}
-
-function getSetISODayOfWeek (input) {
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
-
- // behaves the same as moment#day except
- // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6)
- // as a setter, sunday should belong to the previous week.
-
- if (input != null) {
- var weekday = parseIsoWeekday(input, this.localeData());
- return this.day(this.day() % 7 ? weekday : weekday - 7);
- } else {
- return this.day() || 7;
- }
-}
-
-var defaultWeekdaysRegex = matchWord;
-function weekdaysRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysStrictRegex;
- } else {
- return this._weekdaysRegex;
- }
- } else {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- this._weekdaysRegex = defaultWeekdaysRegex;
- }
- return this._weekdaysStrictRegex && isStrict ?
- this._weekdaysStrictRegex : this._weekdaysRegex;
- }
-}
-
-var defaultWeekdaysShortRegex = matchWord;
-function weekdaysShortRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysShortStrictRegex;
- } else {
- return this._weekdaysShortRegex;
- }
- } else {
- if (!hasOwnProp(this, '_weekdaysShortRegex')) {
- this._weekdaysShortRegex = defaultWeekdaysShortRegex;
- }
- return this._weekdaysShortStrictRegex && isStrict ?
- this._weekdaysShortStrictRegex : this._weekdaysShortRegex;
- }
-}
-
-var defaultWeekdaysMinRegex = matchWord;
-function weekdaysMinRegex (isStrict) {
- if (this._weekdaysParseExact) {
- if (!hasOwnProp(this, '_weekdaysRegex')) {
- computeWeekdaysParse.call(this);
- }
- if (isStrict) {
- return this._weekdaysMinStrictRegex;
- } else {
- return this._weekdaysMinRegex;
- }
- } else {
- if (!hasOwnProp(this, '_weekdaysMinRegex')) {
- this._weekdaysMinRegex = defaultWeekdaysMinRegex;
- }
- return this._weekdaysMinStrictRegex && isStrict ?
- this._weekdaysMinStrictRegex : this._weekdaysMinRegex;
- }
-}
-
-
-function computeWeekdaysParse () {
- function cmpLenRev(a, b) {
- return b.length - a.length;
- }
-
- var minPieces = [], shortPieces = [], longPieces = [], mixedPieces = [],
- i, mom, minp, shortp, longp;
- for (i = 0; i < 7; i++) {
- // make the regex if we don't have it already
- mom = createUTC([2000, 1]).day(i);
- minp = this.weekdaysMin(mom, '');
- shortp = this.weekdaysShort(mom, '');
- longp = this.weekdays(mom, '');
- minPieces.push(minp);
- shortPieces.push(shortp);
- longPieces.push(longp);
- mixedPieces.push(minp);
- mixedPieces.push(shortp);
- mixedPieces.push(longp);
- }
- // Sorting makes sure if one weekday (or abbr) is a prefix of another it
- // will match the longer piece.
- minPieces.sort(cmpLenRev);
- shortPieces.sort(cmpLenRev);
- longPieces.sort(cmpLenRev);
- mixedPieces.sort(cmpLenRev);
- for (i = 0; i < 7; i++) {
- shortPieces[i] = regexEscape(shortPieces[i]);
- longPieces[i] = regexEscape(longPieces[i]);
- mixedPieces[i] = regexEscape(mixedPieces[i]);
- }
-
- this._weekdaysRegex = new RegExp('^(' + mixedPieces.join('|') + ')', 'i');
- this._weekdaysShortRegex = this._weekdaysRegex;
- this._weekdaysMinRegex = this._weekdaysRegex;
-
- this._weekdaysStrictRegex = new RegExp('^(' + longPieces.join('|') + ')', 'i');
- this._weekdaysShortStrictRegex = new RegExp('^(' + shortPieces.join('|') + ')', 'i');
- this._weekdaysMinStrictRegex = new RegExp('^(' + minPieces.join('|') + ')', 'i');
-}
-
-// FORMATTING
-
-function hFormat() {
- return this.hours() % 12 || 12;
-}
-
-function kFormat() {
- return this.hours() || 24;
-}
-
-addFormatToken('H', ['HH', 2], 0, 'hour');
-addFormatToken('h', ['hh', 2], 0, hFormat);
-addFormatToken('k', ['kk', 2], 0, kFormat);
-
-addFormatToken('hmm', 0, 0, function () {
- return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2);
-});
-
-addFormatToken('hmmss', 0, 0, function () {
- return '' + hFormat.apply(this) + zeroFill(this.minutes(), 2) +
- zeroFill(this.seconds(), 2);
-});
-
-addFormatToken('Hmm', 0, 0, function () {
- return '' + this.hours() + zeroFill(this.minutes(), 2);
-});
-
-addFormatToken('Hmmss', 0, 0, function () {
- return '' + this.hours() + zeroFill(this.minutes(), 2) +
- zeroFill(this.seconds(), 2);
-});
-
-function meridiem (token, lowercase) {
- addFormatToken(token, 0, 0, function () {
- return this.localeData().meridiem(this.hours(), this.minutes(), lowercase);
- });
-}
-
-meridiem('a', true);
-meridiem('A', false);
-
-// ALIASES
-
-addUnitAlias('hour', 'h');
-
-// PRIORITY
-addUnitPriority('hour', 13);
-
-// PARSING
-
-function matchMeridiem (isStrict, locale) {
- return locale._meridiemParse;
-}
-
-addRegexToken('a', matchMeridiem);
-addRegexToken('A', matchMeridiem);
-addRegexToken('H', match1to2);
-addRegexToken('h', match1to2);
-addRegexToken('HH', match1to2, match2);
-addRegexToken('hh', match1to2, match2);
-
-addRegexToken('hmm', match3to4);
-addRegexToken('hmmss', match5to6);
-addRegexToken('Hmm', match3to4);
-addRegexToken('Hmmss', match5to6);
-
-addParseToken(['H', 'HH'], HOUR);
-addParseToken(['a', 'A'], function (input, array, config) {
- config._isPm = config._locale.isPM(input);
- config._meridiem = input;
-});
-addParseToken(['h', 'hh'], function (input, array, config) {
- array[HOUR] = toInt(input);
- getParsingFlags(config).bigHour = true;
-});
-addParseToken('hmm', function (input, array, config) {
- var pos = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos));
- array[MINUTE] = toInt(input.substr(pos));
- getParsingFlags(config).bigHour = true;
-});
-addParseToken('hmmss', function (input, array, config) {
- var pos1 = input.length - 4;
- var pos2 = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos1));
- array[MINUTE] = toInt(input.substr(pos1, 2));
- array[SECOND] = toInt(input.substr(pos2));
- getParsingFlags(config).bigHour = true;
-});
-addParseToken('Hmm', function (input, array, config) {
- var pos = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos));
- array[MINUTE] = toInt(input.substr(pos));
-});
-addParseToken('Hmmss', function (input, array, config) {
- var pos1 = input.length - 4;
- var pos2 = input.length - 2;
- array[HOUR] = toInt(input.substr(0, pos1));
- array[MINUTE] = toInt(input.substr(pos1, 2));
- array[SECOND] = toInt(input.substr(pos2));
-});
-
-// LOCALES
-
-function localeIsPM (input) {
- // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays
- // Using charAt should be more compatible.
- return ((input + '').toLowerCase().charAt(0) === 'p');
-}
-
-var defaultLocaleMeridiemParse = /[ap]\.?m?\.?/i;
-function localeMeridiem (hours, minutes, isLower) {
- if (hours > 11) {
- return isLower ? 'pm' : 'PM';
- } else {
- return isLower ? 'am' : 'AM';
- }
-}
-
-
-// MOMENTS
-
-// Setting the hour should keep the time, because the user explicitly
-// specified which hour he wants. So trying to maintain the same hour (in
-// a new timezone) makes sense. Adding/subtracting hours does not follow
-// this rule.
-var getSetHour = makeGetSet('Hours', true);
-
-// months
-// week
-// weekdays
-// meridiem
-var baseConfig = {
- calendar: defaultCalendar,
- longDateFormat: defaultLongDateFormat,
- invalidDate: defaultInvalidDate,
- ordinal: defaultOrdinal,
- ordinalParse: defaultOrdinalParse,
- relativeTime: defaultRelativeTime,
-
- months: defaultLocaleMonths,
- monthsShort: defaultLocaleMonthsShort,
-
- week: defaultLocaleWeek,
-
- weekdays: defaultLocaleWeekdays,
- weekdaysMin: defaultLocaleWeekdaysMin,
- weekdaysShort: defaultLocaleWeekdaysShort,
-
- meridiemParse: defaultLocaleMeridiemParse
-};
-
-// internal storage for locale config files
-var locales = {};
-var localeFamilies = {};
-var globalLocale;
-
-function normalizeLocale(key) {
- return key ? key.toLowerCase().replace('_', '-') : key;
-}
-
-// pick the locale from the array
-// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
-// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root
-function chooseLocale(names) {
- var i = 0, j, next, locale, split;
-
- while (i < names.length) {
- split = normalizeLocale(names[i]).split('-');
- j = split.length;
- next = normalizeLocale(names[i + 1]);
- next = next ? next.split('-') : null;
- while (j > 0) {
- locale = loadLocale(split.slice(0, j).join('-'));
- if (locale) {
- return locale;
- }
- if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) {
- //the next array item is better than a shallower substring of this one
- break;
- }
- j--;
- }
- i++;
- }
- return null;
-}
-
-function loadLocale(name) {
- var oldLocale = null;
- // TODO: Find a better way to register and load all the locales in Node
- if (!locales[name] && (typeof module !== 'undefined') &&
- module && module.exports) {
- try {
- oldLocale = globalLocale._abbr;
- require('./locale/' + name);
- // because defineLocale currently also sets the global locale, we
- // want to undo that for lazy loaded locales
- getSetGlobalLocale(oldLocale);
- } catch (e) { }
- }
- return locales[name];
-}
-
-// This function will load locale and then set the global locale. If
-// no arguments are passed in, it will simply return the current global
-// locale key.
-function getSetGlobalLocale (key, values) {
- var data;
- if (key) {
- if (isUndefined(values)) {
- data = getLocale(key);
- }
- else {
- data = defineLocale(key, values);
- }
-
- if (data) {
- // moment.duration._locale = moment._locale = data;
- globalLocale = data;
- }
- }
-
- return globalLocale._abbr;
-}
-
-function defineLocale (name, config) {
- if (config !== null) {
- var parentConfig = baseConfig;
- config.abbr = name;
- if (locales[name] != null) {
- deprecateSimple('defineLocaleOverride',
- 'use moment.updateLocale(localeName, config) to change ' +
- 'an existing locale. moment.defineLocale(localeName, ' +
- 'config) should only be used for creating a new locale ' +
- 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
- parentConfig = locales[name]._config;
- } else if (config.parentLocale != null) {
- if (locales[config.parentLocale] != null) {
- parentConfig = locales[config.parentLocale]._config;
- } else {
- if (!localeFamilies[config.parentLocale]) {
- localeFamilies[config.parentLocale] = [];
- }
- localeFamilies[config.parentLocale].push({
- name: name,
- config: config
- });
- return null;
- }
- }
- locales[name] = new Locale(mergeConfigs(parentConfig, config));
-
- if (localeFamilies[name]) {
- localeFamilies[name].forEach(function (x) {
- defineLocale(x.name, x.config);
- });
- }
-
- // backwards compat for now: also set the locale
- // make sure we set the locale AFTER all child locales have been
- // created, so we won't end up with the child locale set.
- getSetGlobalLocale(name);
-
-
- return locales[name];
- } else {
- // useful for testing
- delete locales[name];
- return null;
- }
-}
-
-function updateLocale(name, config) {
- if (config != null) {
- var locale, parentConfig = baseConfig;
- // MERGE
- if (locales[name] != null) {
- parentConfig = locales[name]._config;
- }
- config = mergeConfigs(parentConfig, config);
- locale = new Locale(config);
- locale.parentLocale = locales[name];
- locales[name] = locale;
-
- // backwards compat for now: also set the locale
- getSetGlobalLocale(name);
- } else {
- // pass null for config to unupdate, useful for tests
- if (locales[name] != null) {
- if (locales[name].parentLocale != null) {
- locales[name] = locales[name].parentLocale;
- } else if (locales[name] != null) {
- delete locales[name];
- }
- }
- }
- return locales[name];
-}
-
-// returns locale data
-function getLocale (key) {
- var locale;
-
- if (key && key._locale && key._locale._abbr) {
- key = key._locale._abbr;
- }
-
- if (!key) {
- return globalLocale;
- }
-
- if (!isArray(key)) {
- //short-circuit everything else
- locale = loadLocale(key);
- if (locale) {
- return locale;
- }
- key = [key];
- }
-
- return chooseLocale(key);
-}
-
-function listLocales() {
- return keys$1(locales);
-}
-
-function checkOverflow (m) {
- var overflow;
- var a = m._a;
-
- if (a && getParsingFlags(m).overflow === -2) {
- overflow =
- a[MONTH] < 0 || a[MONTH] > 11 ? MONTH :
- a[DATE] < 1 || a[DATE] > daysInMonth(a[YEAR], a[MONTH]) ? DATE :
- a[HOUR] < 0 || a[HOUR] > 24 || (a[HOUR] === 24 && (a[MINUTE] !== 0 || a[SECOND] !== 0 || a[MILLISECOND] !== 0)) ? HOUR :
- a[MINUTE] < 0 || a[MINUTE] > 59 ? MINUTE :
- a[SECOND] < 0 || a[SECOND] > 59 ? SECOND :
- a[MILLISECOND] < 0 || a[MILLISECOND] > 999 ? MILLISECOND :
- -1;
-
- if (getParsingFlags(m)._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) {
- overflow = DATE;
- }
- if (getParsingFlags(m)._overflowWeeks && overflow === -1) {
- overflow = WEEK;
- }
- if (getParsingFlags(m)._overflowWeekday && overflow === -1) {
- overflow = WEEKDAY;
- }
-
- getParsingFlags(m).overflow = overflow;
- }
-
- return m;
-}
-
-// iso 8601 regex
-// 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00)
-var extendedIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
-var basicIsoRegex = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/;
-
-var tzRegex = /Z|[+-]\d\d(?::?\d\d)?/;
-
-var isoDates = [
- ['YYYYYY-MM-DD', /[+-]\d{6}-\d\d-\d\d/],
- ['YYYY-MM-DD', /\d{4}-\d\d-\d\d/],
- ['GGGG-[W]WW-E', /\d{4}-W\d\d-\d/],
- ['GGGG-[W]WW', /\d{4}-W\d\d/, false],
- ['YYYY-DDD', /\d{4}-\d{3}/],
- ['YYYY-MM', /\d{4}-\d\d/, false],
- ['YYYYYYMMDD', /[+-]\d{10}/],
- ['YYYYMMDD', /\d{8}/],
- // YYYYMM is NOT allowed by the standard
- ['GGGG[W]WWE', /\d{4}W\d{3}/],
- ['GGGG[W]WW', /\d{4}W\d{2}/, false],
- ['YYYYDDD', /\d{7}/]
-];
-
-// iso time formats and regexes
-var isoTimes = [
- ['HH:mm:ss.SSSS', /\d\d:\d\d:\d\d\.\d+/],
- ['HH:mm:ss,SSSS', /\d\d:\d\d:\d\d,\d+/],
- ['HH:mm:ss', /\d\d:\d\d:\d\d/],
- ['HH:mm', /\d\d:\d\d/],
- ['HHmmss.SSSS', /\d\d\d\d\d\d\.\d+/],
- ['HHmmss,SSSS', /\d\d\d\d\d\d,\d+/],
- ['HHmmss', /\d\d\d\d\d\d/],
- ['HHmm', /\d\d\d\d/],
- ['HH', /\d\d/]
-];
-
-var aspNetJsonRegex = /^\/?Date\((\-?\d+)/i;
-
-// date from iso format
-function configFromISO(config) {
- var i, l,
- string = config._i,
- match = extendedIsoRegex.exec(string) || basicIsoRegex.exec(string),
- allowTime, dateFormat, timeFormat, tzFormat;
-
- if (match) {
- getParsingFlags(config).iso = true;
-
- for (i = 0, l = isoDates.length; i < l; i++) {
- if (isoDates[i][1].exec(match[1])) {
- dateFormat = isoDates[i][0];
- allowTime = isoDates[i][2] !== false;
- break;
- }
- }
- if (dateFormat == null) {
- config._isValid = false;
- return;
- }
- if (match[3]) {
- for (i = 0, l = isoTimes.length; i < l; i++) {
- if (isoTimes[i][1].exec(match[3])) {
- // match[2] should be 'T' or space
- timeFormat = (match[2] || ' ') + isoTimes[i][0];
- break;
- }
- }
- if (timeFormat == null) {
- config._isValid = false;
- return;
- }
- }
- if (!allowTime && timeFormat != null) {
- config._isValid = false;
- return;
- }
- if (match[4]) {
- if (tzRegex.exec(match[4])) {
- tzFormat = 'Z';
- } else {
- config._isValid = false;
- return;
- }
- }
- config._f = dateFormat + (timeFormat || '') + (tzFormat || '');
- configFromStringAndFormat(config);
- } else {
- config._isValid = false;
- }
-}
-
-// date from iso format or fallback
-function configFromString(config) {
- var matched = aspNetJsonRegex.exec(config._i);
-
- if (matched !== null) {
- config._d = new Date(+matched[1]);
- return;
- }
-
- configFromISO(config);
- if (config._isValid === false) {
- delete config._isValid;
- hooks.createFromInputFallback(config);
- }
-}
-
-hooks.createFromInputFallback = deprecate(
- 'value provided is not in a recognized ISO format. moment construction falls back to js Date(), ' +
- 'which is not reliable across all browsers and versions. Non ISO date formats are ' +
- 'discouraged and will be removed in an upcoming major release. Please refer to ' +
- 'http://momentjs.com/guides/#/warnings/js-date/ for more info.',
- function (config) {
- config._d = new Date(config._i + (config._useUTC ? ' UTC' : ''));
- }
-);
-
-// Pick the first defined of two or three arguments.
-function defaults(a, b, c) {
- if (a != null) {
- return a;
- }
- if (b != null) {
- return b;
- }
- return c;
-}
-
-function currentDateArray(config) {
- // hooks is actually the exported moment object
- var nowValue = new Date(hooks.now());
- if (config._useUTC) {
- return [nowValue.getUTCFullYear(), nowValue.getUTCMonth(), nowValue.getUTCDate()];
- }
- return [nowValue.getFullYear(), nowValue.getMonth(), nowValue.getDate()];
-}
-
-// convert an array to a date.
-// the array should mirror the parameters below
-// note: all values past the year are optional and will default to the lowest possible value.
-// [year, month, day , hour, minute, second, millisecond]
-function configFromArray (config) {
- var i, date, input = [], currentDate, yearToUse;
-
- if (config._d) {
- return;
- }
-
- currentDate = currentDateArray(config);
-
- //compute day of the year from weeks and weekdays
- if (config._w && config._a[DATE] == null && config._a[MONTH] == null) {
- dayOfYearFromWeekInfo(config);
- }
-
- //if the day of the year is set, figure out what it is
- if (config._dayOfYear) {
- yearToUse = defaults(config._a[YEAR], currentDate[YEAR]);
-
- if (config._dayOfYear > daysInYear(yearToUse)) {
- getParsingFlags(config)._overflowDayOfYear = true;
- }
-
- date = createUTCDate(yearToUse, 0, config._dayOfYear);
- config._a[MONTH] = date.getUTCMonth();
- config._a[DATE] = date.getUTCDate();
- }
-
- // Default to current date.
- // * if no year, month, day of month are given, default to today
- // * if day of month is given, default month and year
- // * if month is given, default only year
- // * if year is given, don't default anything
- for (i = 0; i < 3 && config._a[i] == null; ++i) {
- config._a[i] = input[i] = currentDate[i];
- }
-
- // Zero out whatever was not defaulted, including time
- for (; i < 7; i++) {
- config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i];
- }
-
- // Check for 24:00:00.000
- if (config._a[HOUR] === 24 &&
- config._a[MINUTE] === 0 &&
- config._a[SECOND] === 0 &&
- config._a[MILLISECOND] === 0) {
- config._nextDay = true;
- config._a[HOUR] = 0;
- }
-
- config._d = (config._useUTC ? createUTCDate : createDate).apply(null, input);
- // Apply timezone offset from input. The actual utcOffset can be changed
- // with parseZone.
- if (config._tzm != null) {
- config._d.setUTCMinutes(config._d.getUTCMinutes() - config._tzm);
- }
-
- if (config._nextDay) {
- config._a[HOUR] = 24;
- }
-}
-
-function dayOfYearFromWeekInfo(config) {
- var w, weekYear, week, weekday, dow, doy, temp, weekdayOverflow;
-
- w = config._w;
- if (w.GG != null || w.W != null || w.E != null) {
- dow = 1;
- doy = 4;
-
- // TODO: We need to take the current isoWeekYear, but that depends on
- // how we interpret now (local, utc, fixed offset). So create
- // a now version of current config (take local/utc/offset flags, and
- // create now).
- weekYear = defaults(w.GG, config._a[YEAR], weekOfYear(createLocal(), 1, 4).year);
- week = defaults(w.W, 1);
- weekday = defaults(w.E, 1);
- if (weekday < 1 || weekday > 7) {
- weekdayOverflow = true;
- }
- } else {
- dow = config._locale._week.dow;
- doy = config._locale._week.doy;
-
- var curWeek = weekOfYear(createLocal(), dow, doy);
-
- weekYear = defaults(w.gg, config._a[YEAR], curWeek.year);
-
- // Default to current week.
- week = defaults(w.w, curWeek.week);
-
- if (w.d != null) {
- // weekday -- low day numbers are considered next week
- weekday = w.d;
- if (weekday < 0 || weekday > 6) {
- weekdayOverflow = true;
- }
- } else if (w.e != null) {
- // local weekday -- counting starts from begining of week
- weekday = w.e + dow;
- if (w.e < 0 || w.e > 6) {
- weekdayOverflow = true;
- }
- } else {
- // default to begining of week
- weekday = dow;
- }
- }
- if (week < 1 || week > weeksInYear(weekYear, dow, doy)) {
- getParsingFlags(config)._overflowWeeks = true;
- } else if (weekdayOverflow != null) {
- getParsingFlags(config)._overflowWeekday = true;
- } else {
- temp = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy);
- config._a[YEAR] = temp.year;
- config._dayOfYear = temp.dayOfYear;
- }
-}
-
-// constant that refers to the ISO standard
-hooks.ISO_8601 = function () {};
-
-// date from string and format string
-function configFromStringAndFormat(config) {
- // TODO: Move this to another part of the creation flow to prevent circular deps
- if (config._f === hooks.ISO_8601) {
- configFromISO(config);
- return;
- }
-
- config._a = [];
- getParsingFlags(config).empty = true;
-
- // This array is used to make a Date, either with `new Date` or `Date.UTC`
- var string = '' + config._i,
- i, parsedInput, tokens, token, skipped,
- stringLength = string.length,
- totalParsedInputLength = 0;
-
- tokens = expandFormat(config._f, config._locale).match(formattingTokens) || [];
-
- for (i = 0; i < tokens.length; i++) {
- token = tokens[i];
- parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0];
- // console.log('token', token, 'parsedInput', parsedInput,
- // 'regex', getParseRegexForToken(token, config));
- if (parsedInput) {
- skipped = string.substr(0, string.indexOf(parsedInput));
- if (skipped.length > 0) {
- getParsingFlags(config).unusedInput.push(skipped);
- }
- string = string.slice(string.indexOf(parsedInput) + parsedInput.length);
- totalParsedInputLength += parsedInput.length;
- }
- // don't parse if it's not a known token
- if (formatTokenFunctions[token]) {
- if (parsedInput) {
- getParsingFlags(config).empty = false;
- }
- else {
- getParsingFlags(config).unusedTokens.push(token);
- }
- addTimeToArrayFromToken(token, parsedInput, config);
- }
- else if (config._strict && !parsedInput) {
- getParsingFlags(config).unusedTokens.push(token);
- }
- }
-
- // add remaining unparsed input length to the string
- getParsingFlags(config).charsLeftOver = stringLength - totalParsedInputLength;
- if (string.length > 0) {
- getParsingFlags(config).unusedInput.push(string);
- }
-
- // clear _12h flag if hour is <= 12
- if (config._a[HOUR] <= 12 &&
- getParsingFlags(config).bigHour === true &&
- config._a[HOUR] > 0) {
- getParsingFlags(config).bigHour = undefined;
- }
-
- getParsingFlags(config).parsedDateParts = config._a.slice(0);
- getParsingFlags(config).meridiem = config._meridiem;
- // handle meridiem
- config._a[HOUR] = meridiemFixWrap(config._locale, config._a[HOUR], config._meridiem);
-
- configFromArray(config);
- checkOverflow(config);
-}
-
-
-function meridiemFixWrap (locale, hour, meridiem) {
- var isPm;
-
- if (meridiem == null) {
- // nothing to do
- return hour;
- }
- if (locale.meridiemHour != null) {
- return locale.meridiemHour(hour, meridiem);
- } else if (locale.isPM != null) {
- // Fallback
- isPm = locale.isPM(meridiem);
- if (isPm && hour < 12) {
- hour += 12;
- }
- if (!isPm && hour === 12) {
- hour = 0;
- }
- return hour;
- } else {
- // this is not supposed to happen
- return hour;
- }
-}
-
-// date from string and array of format strings
-function configFromStringAndArray(config) {
- var tempConfig,
- bestMoment,
-
- scoreToBeat,
- i,
- currentScore;
-
- if (config._f.length === 0) {
- getParsingFlags(config).invalidFormat = true;
- config._d = new Date(NaN);
- return;
- }
-
- for (i = 0; i < config._f.length; i++) {
- currentScore = 0;
- tempConfig = copyConfig({}, config);
- if (config._useUTC != null) {
- tempConfig._useUTC = config._useUTC;
- }
- tempConfig._f = config._f[i];
- configFromStringAndFormat(tempConfig);
-
- if (!isValid(tempConfig)) {
- continue;
- }
-
- // if there is any input that was not parsed add a penalty for that format
- currentScore += getParsingFlags(tempConfig).charsLeftOver;
-
- //or tokens
- currentScore += getParsingFlags(tempConfig).unusedTokens.length * 10;
-
- getParsingFlags(tempConfig).score = currentScore;
-
- if (scoreToBeat == null || currentScore < scoreToBeat) {
- scoreToBeat = currentScore;
- bestMoment = tempConfig;
- }
- }
-
- extend(config, bestMoment || tempConfig);
-}
-
-function configFromObject(config) {
- if (config._d) {
- return;
- }
-
- var i = normalizeObjectUnits(config._i);
- config._a = map([i.year, i.month, i.day || i.date, i.hour, i.minute, i.second, i.millisecond], function (obj) {
- return obj && parseInt(obj, 10);
- });
-
- configFromArray(config);
-}
-
-function createFromConfig (config) {
- var res = new Moment(checkOverflow(prepareConfig(config)));
- if (res._nextDay) {
- // Adding is smart enough around DST
- res.add(1, 'd');
- res._nextDay = undefined;
- }
-
- return res;
-}
-
-function prepareConfig (config) {
- var input = config._i,
- format = config._f;
-
- config._locale = config._locale || getLocale(config._l);
-
- if (input === null || (format === undefined && input === '')) {
- return createInvalid({nullInput: true});
- }
-
- if (typeof input === 'string') {
- config._i = input = config._locale.preparse(input);
- }
-
- if (isMoment(input)) {
- return new Moment(checkOverflow(input));
- } else if (isDate(input)) {
- config._d = input;
- } else if (isArray(format)) {
- configFromStringAndArray(config);
- } else if (format) {
- configFromStringAndFormat(config);
- } else {
- configFromInput(config);
- }
-
- if (!isValid(config)) {
- config._d = null;
- }
-
- return config;
-}
-
-function configFromInput(config) {
- var input = config._i;
- if (input === undefined) {
- config._d = new Date(hooks.now());
- } else if (isDate(input)) {
- config._d = new Date(input.valueOf());
- } else if (typeof input === 'string') {
- configFromString(config);
- } else if (isArray(input)) {
- config._a = map(input.slice(0), function (obj) {
- return parseInt(obj, 10);
- });
- configFromArray(config);
- } else if (typeof(input) === 'object') {
- configFromObject(config);
- } else if (isNumber(input)) {
- // from milliseconds
- config._d = new Date(input);
- } else {
- hooks.createFromInputFallback(config);
- }
-}
-
-function createLocalOrUTC (input, format, locale, strict, isUTC) {
- var c = {};
-
- if (locale === true || locale === false) {
- strict = locale;
- locale = undefined;
- }
-
- if ((isObject(input) && isObjectEmpty(input)) ||
- (isArray(input) && input.length === 0)) {
- input = undefined;
- }
- // object construction must be done this way.
- // https://github.com/moment/moment/issues/1423
- c._isAMomentObject = true;
- c._useUTC = c._isUTC = isUTC;
- c._l = locale;
- c._i = input;
- c._f = format;
- c._strict = strict;
-
- return createFromConfig(c);
-}
-
-function createLocal (input, format, locale, strict) {
- return createLocalOrUTC(input, format, locale, strict, false);
-}
-
-var prototypeMin = deprecate(
- 'moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/',
- function () {
- var other = createLocal.apply(null, arguments);
- if (this.isValid() && other.isValid()) {
- return other < this ? this : other;
- } else {
- return createInvalid();
- }
- }
-);
-
-var prototypeMax = deprecate(
- 'moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/',
- function () {
- var other = createLocal.apply(null, arguments);
- if (this.isValid() && other.isValid()) {
- return other > this ? this : other;
- } else {
- return createInvalid();
- }
- }
-);
-
-// Pick a moment m from moments so that m[fn](other) is true for all
-// other. This relies on the function fn to be transitive.
-//
-// moments should either be an array of moment objects or an array, whose
-// first element is an array of moment objects.
-function pickBy(fn, moments) {
- var res, i;
- if (moments.length === 1 && isArray(moments[0])) {
- moments = moments[0];
- }
- if (!moments.length) {
- return createLocal();
- }
- res = moments[0];
- for (i = 1; i < moments.length; ++i) {
- if (!moments[i].isValid() || moments[i][fn](res)) {
- res = moments[i];
- }
- }
- return res;
-}
-
-// TODO: Use [].sort instead?
-function min () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isBefore', args);
-}
-
-function max () {
- var args = [].slice.call(arguments, 0);
-
- return pickBy('isAfter', args);
-}
-
-var now = function () {
- return Date.now ? Date.now() : +(new Date());
-};
-
-function Duration (duration) {
- var normalizedInput = normalizeObjectUnits(duration),
- years = normalizedInput.year || 0,
- quarters = normalizedInput.quarter || 0,
- months = normalizedInput.month || 0,
- weeks = normalizedInput.week || 0,
- days = normalizedInput.day || 0,
- hours = normalizedInput.hour || 0,
- minutes = normalizedInput.minute || 0,
- seconds = normalizedInput.second || 0,
- milliseconds = normalizedInput.millisecond || 0;
-
- // representation for dateAddRemove
- this._milliseconds = +milliseconds +
- seconds * 1e3 + // 1000
- minutes * 6e4 + // 1000 * 60
- hours * 1000 * 60 * 60; //using 1000 * 60 * 60 instead of 36e5 to avoid floating point rounding errors https://github.com/moment/moment/issues/2978
- // Because of dateAddRemove treats 24 hours as different from a
- // day when working around DST, we need to store them separately
- this._days = +days +
- weeks * 7;
- // It is impossible translate months into days without knowing
- // which months you are are talking about, so we have to store
- // it separately.
- this._months = +months +
- quarters * 3 +
- years * 12;
-
- this._data = {};
-
- this._locale = getLocale();
-
- this._bubble();
-}
-
-function isDuration (obj) {
- return obj instanceof Duration;
-}
-
-function absRound (number) {
- if (number < 0) {
- return Math.round(-1 * number) * -1;
- } else {
- return Math.round(number);
- }
-}
-
-// FORMATTING
-
-function offset (token, separator) {
- addFormatToken(token, 0, 0, function () {
- var offset = this.utcOffset();
- var sign = '+';
- if (offset < 0) {
- offset = -offset;
- sign = '-';
- }
- return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
- });
-}
-
-offset('Z', ':');
-offset('ZZ', '');
-
-// PARSING
-
-addRegexToken('Z', matchShortOffset);
-addRegexToken('ZZ', matchShortOffset);
-addParseToken(['Z', 'ZZ'], function (input, array, config) {
- config._useUTC = true;
- config._tzm = offsetFromString(matchShortOffset, input);
-});
-
-// HELPERS
-
-// timezone chunker
-// '+10:00' > ['10', '00']
-// '-1530' > ['-15', '30']
-var chunkOffset = /([\+\-]|\d\d)/gi;
-
-function offsetFromString(matcher, string) {
- var matches = (string || '').match(matcher);
-
- if (matches === null) {
- return null;
- }
-
- var chunk = matches[matches.length - 1] || [];
- var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
- var minutes = +(parts[1] * 60) + toInt(parts[2]);
-
- return minutes === 0 ?
- 0 :
- parts[0] === '+' ? minutes : -minutes;
-}
-
-// Return a moment from input, that is local/utc/zone equivalent to model.
-function cloneWithOffset(input, model) {
- var res, diff;
- if (model._isUTC) {
- res = model.clone();
- diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
- // Use low-level api, because this fn is low-level api.
- res._d.setTime(res._d.valueOf() + diff);
- hooks.updateOffset(res, false);
- return res;
- } else {
- return createLocal(input).local();
- }
-}
-
-function getDateOffset (m) {
- // On Firefox.24 Date#getTimezoneOffset returns a floating point.
- // https://github.com/moment/moment/pull/1871
- return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
-}
-
-// HOOKS
-
-// This function will be called whenever a moment is mutated.
-// It is intended to keep the offset in sync with the timezone.
-hooks.updateOffset = function () {};
-
-// MOMENTS
-
-// keepLocalTime = true means only change the timezone, without
-// affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
-// 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
-// +0200, so we adjust the time as needed, to be valid.
-//
-// Keeping the time actually adds/subtracts (one hour)
-// from the actual represented time. That is why we call updateOffset
-// a second time. In case it wants us to change the offset again
-// _changeInProgress == true case, then we have to adjust, because
-// there is no such time in the given timezone.
-function getSetOffset (input, keepLocalTime) {
- var offset = this._offset || 0,
- localAdjust;
- if (!this.isValid()) {
- return input != null ? this : NaN;
- }
- if (input != null) {
- if (typeof input === 'string') {
- input = offsetFromString(matchShortOffset, input);
- if (input === null) {
- return this;
- }
- } else if (Math.abs(input) < 16) {
- input = input * 60;
- }
- if (!this._isUTC && keepLocalTime) {
- localAdjust = getDateOffset(this);
- }
- this._offset = input;
- this._isUTC = true;
- if (localAdjust != null) {
- this.add(localAdjust, 'm');
- }
- if (offset !== input) {
- if (!keepLocalTime || this._changeInProgress) {
- addSubtract(this, createDuration(input - offset, 'm'), 1, false);
- } else if (!this._changeInProgress) {
- this._changeInProgress = true;
- hooks.updateOffset(this, true);
- this._changeInProgress = null;
- }
- }
- return this;
- } else {
- return this._isUTC ? offset : getDateOffset(this);
- }
-}
-
-function getSetZone (input, keepLocalTime) {
- if (input != null) {
- if (typeof input !== 'string') {
- input = -input;
- }
-
- this.utcOffset(input, keepLocalTime);
-
- return this;
- } else {
- return -this.utcOffset();
- }
-}
-
-function setOffsetToUTC (keepLocalTime) {
- return this.utcOffset(0, keepLocalTime);
-}
-
-function setOffsetToLocal (keepLocalTime) {
- if (this._isUTC) {
- this.utcOffset(0, keepLocalTime);
- this._isUTC = false;
-
- if (keepLocalTime) {
- this.subtract(getDateOffset(this), 'm');
- }
- }
- return this;
-}
-
-function setOffsetToParsedOffset () {
- if (this._tzm != null) {
- this.utcOffset(this._tzm);
- } else if (typeof this._i === 'string') {
- var tZone = offsetFromString(matchOffset, this._i);
- if (tZone != null) {
- this.utcOffset(tZone);
- }
- else {
- this.utcOffset(0, true);
- }
- }
- return this;
-}
-
-function hasAlignedHourOffset (input) {
- if (!this.isValid()) {
- return false;
- }
- input = input ? createLocal(input).utcOffset() : 0;
-
- return (this.utcOffset() - input) % 60 === 0;
-}
-
-function isDaylightSavingTime () {
- return (
- this.utcOffset() > this.clone().month(0).utcOffset() ||
- this.utcOffset() > this.clone().month(5).utcOffset()
- );
-}
-
-function isDaylightSavingTimeShifted () {
- if (!isUndefined(this._isDSTShifted)) {
- return this._isDSTShifted;
- }
-
- var c = {};
-
- copyConfig(c, this);
- c = prepareConfig(c);
-
- if (c._a) {
- var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
- this._isDSTShifted = this.isValid() &&
- compareArrays(c._a, other.toArray()) > 0;
- } else {
- this._isDSTShifted = false;
- }
-
- return this._isDSTShifted;
-}
-
-function isLocal () {
- return this.isValid() ? !this._isUTC : false;
-}
-
-function isUtcOffset () {
- return this.isValid() ? this._isUTC : false;
-}
-
-function isUtc () {
- return this.isValid() ? this._isUTC && this._offset === 0 : false;
-}
-
-// ASP.NET json date format regex
-var aspNetRegex = /^(\-)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/;
-
-// from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html
-// somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere
-// and further modified to allow for strings containing both week and day
-var isoRegex = /^(-)?P(?:(-?[0-9,.]*)Y)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)W)?(?:(-?[0-9,.]*)D)?(?:T(?:(-?[0-9,.]*)H)?(?:(-?[0-9,.]*)M)?(?:(-?[0-9,.]*)S)?)?$/;
-
-function createDuration (input, key) {
- var duration = input,
- // matching against regexp is expensive, do it on demand
- match = null,
- sign,
- ret,
- diffRes;
-
- if (isDuration(input)) {
- duration = {
- ms : input._milliseconds,
- d : input._days,
- M : input._months
- };
- } else if (isNumber(input)) {
- duration = {};
- if (key) {
- duration[key] = input;
- } else {
- duration.milliseconds = input;
- }
- } else if (!!(match = aspNetRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y : 0,
- d : toInt(match[DATE]) * sign,
- h : toInt(match[HOUR]) * sign,
- m : toInt(match[MINUTE]) * sign,
- s : toInt(match[SECOND]) * sign,
- ms : toInt(absRound(match[MILLISECOND] * 1000)) * sign // the millisecond decimal point is included in the match
- };
- } else if (!!(match = isoRegex.exec(input))) {
- sign = (match[1] === '-') ? -1 : 1;
- duration = {
- y : parseIso(match[2], sign),
- M : parseIso(match[3], sign),
- w : parseIso(match[4], sign),
- d : parseIso(match[5], sign),
- h : parseIso(match[6], sign),
- m : parseIso(match[7], sign),
- s : parseIso(match[8], sign)
- };
- } else if (duration == null) {// checks for null or undefined
- duration = {};
- } else if (typeof duration === 'object' && ('from' in duration || 'to' in duration)) {
- diffRes = momentsDifference(createLocal(duration.from), createLocal(duration.to));
-
- duration = {};
- duration.ms = diffRes.milliseconds;
- duration.M = diffRes.months;
- }
-
- ret = new Duration(duration);
-
- if (isDuration(input) && hasOwnProp(input, '_locale')) {
- ret._locale = input._locale;
- }
-
- return ret;
-}
-
-createDuration.fn = Duration.prototype;
-
-function parseIso (inp, sign) {
- // We'd normally use ~~inp for this, but unfortunately it also
- // converts floats to ints.
- // inp may be undefined, so careful calling replace on it.
- var res = inp && parseFloat(inp.replace(',', '.'));
- // apply sign while we're at it
- return (isNaN(res) ? 0 : res) * sign;
-}
-
-function positiveMomentsDifference(base, other) {
- var res = {milliseconds: 0, months: 0};
-
- res.months = other.month() - base.month() +
- (other.year() - base.year()) * 12;
- if (base.clone().add(res.months, 'M').isAfter(other)) {
- --res.months;
- }
-
- res.milliseconds = +other - +(base.clone().add(res.months, 'M'));
-
- return res;
-}
-
-function momentsDifference(base, other) {
- var res;
- if (!(base.isValid() && other.isValid())) {
- return {milliseconds: 0, months: 0};
- }
-
- other = cloneWithOffset(other, base);
- if (base.isBefore(other)) {
- res = positiveMomentsDifference(base, other);
- } else {
- res = positiveMomentsDifference(other, base);
- res.milliseconds = -res.milliseconds;
- res.months = -res.months;
- }
-
- return res;
-}
-
-// TODO: remove 'name' arg after deprecation is removed
-function createAdder(direction, name) {
- return function (val, period) {
- var dur, tmp;
- //invert the arguments, but complain about it
- if (period !== null && !isNaN(+period)) {
- deprecateSimple(name, 'moment().' + name + '(period, number) is deprecated. Please use moment().' + name + '(number, period). ' +
- 'See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info.');
- tmp = val; val = period; period = tmp;
- }
-
- val = typeof val === 'string' ? +val : val;
- dur = createDuration(val, period);
- addSubtract(this, dur, direction);
- return this;
- };
-}
-
-function addSubtract (mom, duration, isAdding, updateOffset) {
- var milliseconds = duration._milliseconds,
- days = absRound(duration._days),
- months = absRound(duration._months);
-
- if (!mom.isValid()) {
- // No op
- return;
- }
-
- updateOffset = updateOffset == null ? true : updateOffset;
-
- if (milliseconds) {
- mom._d.setTime(mom._d.valueOf() + milliseconds * isAdding);
- }
- if (days) {
- set$1(mom, 'Date', get(mom, 'Date') + days * isAdding);
- }
- if (months) {
- setMonth(mom, get(mom, 'Month') + months * isAdding);
- }
- if (updateOffset) {
- hooks.updateOffset(mom, days || months);
- }
-}
-
-var add = createAdder(1, 'add');
-var subtract = createAdder(-1, 'subtract');
-
-function getCalendarFormat(myMoment, now) {
- var diff = myMoment.diff(now, 'days', true);
- return diff < -6 ? 'sameElse' :
- diff < -1 ? 'lastWeek' :
- diff < 0 ? 'lastDay' :
- diff < 1 ? 'sameDay' :
- diff < 2 ? 'nextDay' :
- diff < 7 ? 'nextWeek' : 'sameElse';
-}
-
-function calendar$1 (time, formats) {
- // We want to compare the start of today, vs this.
- // Getting start-of-today depends on whether we're local/utc/offset or not.
- var now = time || createLocal(),
- sod = cloneWithOffset(now, this).startOf('day'),
- format = hooks.calendarFormat(this, sod) || 'sameElse';
-
- var output = formats && (isFunction(formats[format]) ? formats[format].call(this, now) : formats[format]);
-
- return this.format(output || this.localeData().calendar(format, this, createLocal(now)));
-}
-
-function clone () {
- return new Moment(this);
-}
-
-function isAfter (input, units) {
- var localInput = isMoment(input) ? input : createLocal(input);
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() > localInput.valueOf();
- } else {
- return localInput.valueOf() < this.clone().startOf(units).valueOf();
- }
-}
-
-function isBefore (input, units) {
- var localInput = isMoment(input) ? input : createLocal(input);
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(!isUndefined(units) ? units : 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() < localInput.valueOf();
- } else {
- return this.clone().endOf(units).valueOf() < localInput.valueOf();
- }
-}
-
-function isBetween (from, to, units, inclusivity) {
- inclusivity = inclusivity || '()';
- return (inclusivity[0] === '(' ? this.isAfter(from, units) : !this.isBefore(from, units)) &&
- (inclusivity[1] === ')' ? this.isBefore(to, units) : !this.isAfter(to, units));
-}
-
-function isSame (input, units) {
- var localInput = isMoment(input) ? input : createLocal(input),
- inputMs;
- if (!(this.isValid() && localInput.isValid())) {
- return false;
- }
- units = normalizeUnits(units || 'millisecond');
- if (units === 'millisecond') {
- return this.valueOf() === localInput.valueOf();
- } else {
- inputMs = localInput.valueOf();
- return this.clone().startOf(units).valueOf() <= inputMs && inputMs <= this.clone().endOf(units).valueOf();
- }
-}
-
-function isSameOrAfter (input, units) {
- return this.isSame(input, units) || this.isAfter(input,units);
-}
-
-function isSameOrBefore (input, units) {
- return this.isSame(input, units) || this.isBefore(input,units);
-}
-
-function diff (input, units, asFloat) {
- var that,
- zoneDelta,
- delta, output;
-
- if (!this.isValid()) {
- return NaN;
- }
-
- that = cloneWithOffset(input, this);
-
- if (!that.isValid()) {
- return NaN;
- }
-
- zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4;
-
- units = normalizeUnits(units);
-
- if (units === 'year' || units === 'month' || units === 'quarter') {
- output = monthDiff(this, that);
- if (units === 'quarter') {
- output = output / 3;
- } else if (units === 'year') {
- output = output / 12;
- }
- } else {
- delta = this - that;
- output = units === 'second' ? delta / 1e3 : // 1000
- units === 'minute' ? delta / 6e4 : // 1000 * 60
- units === 'hour' ? delta / 36e5 : // 1000 * 60 * 60
- units === 'day' ? (delta - zoneDelta) / 864e5 : // 1000 * 60 * 60 * 24, negate dst
- units === 'week' ? (delta - zoneDelta) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst
- delta;
- }
- return asFloat ? output : absFloor(output);
-}
-
-function monthDiff (a, b) {
- // difference in months
- var wholeMonthDiff = ((b.year() - a.year()) * 12) + (b.month() - a.month()),
- // b is in (anchor - 1 month, anchor + 1 month)
- anchor = a.clone().add(wholeMonthDiff, 'months'),
- anchor2, adjust;
-
- if (b - anchor < 0) {
- anchor2 = a.clone().add(wholeMonthDiff - 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor - anchor2);
- } else {
- anchor2 = a.clone().add(wholeMonthDiff + 1, 'months');
- // linear across the month
- adjust = (b - anchor) / (anchor2 - anchor);
- }
-
- //check for negative zero, return zero if negative zero
- return -(wholeMonthDiff + adjust) || 0;
-}
-
-hooks.defaultFormat = 'YYYY-MM-DDTHH:mm:ssZ';
-hooks.defaultFormatUtc = 'YYYY-MM-DDTHH:mm:ss[Z]';
-
-function toString () {
- return this.clone().locale('en').format('ddd MMM DD YYYY HH:mm:ss [GMT]ZZ');
-}
-
-function toISOString () {
- var m = this.clone().utc();
- if (0 < m.year() && m.year() <= 9999) {
- if (isFunction(Date.prototype.toISOString)) {
- // native implementation is ~50x faster, use it when we can
- return this.toDate().toISOString();
- } else {
- return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
- } else {
- return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]');
- }
-}
-
-/**
- * Return a human readable representation of a moment that can
- * also be evaluated to get a new moment which is the same
- *
- * @link https://nodejs.org/dist/latest/docs/api/util.html#util_custom_inspect_function_on_objects
- */
-function inspect () {
- if (!this.isValid()) {
- return 'moment.invalid(/* ' + this._i + ' */)';
- }
- var func = 'moment';
- var zone = '';
- if (!this.isLocal()) {
- func = this.utcOffset() === 0 ? 'moment.utc' : 'moment.parseZone';
- zone = 'Z';
- }
- var prefix = '[' + func + '("]';
- var year = (0 < this.year() && this.year() <= 9999) ? 'YYYY' : 'YYYYYY';
- var datetime = '-MM-DD[T]HH:mm:ss.SSS';
- var suffix = zone + '[")]';
-
- return this.format(prefix + year + datetime + suffix);
-}
-
-function format (inputString) {
- if (!inputString) {
- inputString = this.isUtc() ? hooks.defaultFormatUtc : hooks.defaultFormat;
- }
- var output = formatMoment(this, inputString);
- return this.localeData().postformat(output);
-}
-
-function from (time, withoutSuffix) {
- if (this.isValid() &&
- ((isMoment(time) && time.isValid()) ||
- createLocal(time).isValid())) {
- return createDuration({to: this, from: time}).locale(this.locale()).humanize(!withoutSuffix);
- } else {
- return this.localeData().invalidDate();
- }
-}
-
-function fromNow (withoutSuffix) {
- return this.from(createLocal(), withoutSuffix);
-}
-
-function to (time, withoutSuffix) {
- if (this.isValid() &&
- ((isMoment(time) && time.isValid()) ||
- createLocal(time).isValid())) {
- return createDuration({from: this, to: time}).locale(this.locale()).humanize(!withoutSuffix);
- } else {
- return this.localeData().invalidDate();
- }
-}
-
-function toNow (withoutSuffix) {
- return this.to(createLocal(), withoutSuffix);
-}
-
-// If passed a locale key, it will set the locale for this
-// instance. Otherwise, it will return the locale configuration
-// variables for this instance.
-function locale (key) {
- var newLocaleData;
-
- if (key === undefined) {
- return this._locale._abbr;
- } else {
- newLocaleData = getLocale(key);
- if (newLocaleData != null) {
- this._locale = newLocaleData;
- }
- return this;
- }
-}
-
-var lang = deprecate(
- 'moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.',
- function (key) {
- if (key === undefined) {
- return this.localeData();
- } else {
- return this.locale(key);
- }
- }
-);
-
-function localeData () {
- return this._locale;
-}
-
-function startOf (units) {
- units = normalizeUnits(units);
- // the following switch intentionally omits break keywords
- // to utilize falling through the cases.
- switch (units) {
- case 'year':
- this.month(0);
- /* falls through */
- case 'quarter':
- case 'month':
- this.date(1);
- /* falls through */
- case 'week':
- case 'isoWeek':
- case 'day':
- case 'date':
- this.hours(0);
- /* falls through */
- case 'hour':
- this.minutes(0);
- /* falls through */
- case 'minute':
- this.seconds(0);
- /* falls through */
- case 'second':
- this.milliseconds(0);
- }
-
- // weeks are a special case
- if (units === 'week') {
- this.weekday(0);
- }
- if (units === 'isoWeek') {
- this.isoWeekday(1);
- }
-
- // quarters are also special
- if (units === 'quarter') {
- this.month(Math.floor(this.month() / 3) * 3);
- }
-
- return this;
-}
-
-function endOf (units) {
- units = normalizeUnits(units);
- if (units === undefined || units === 'millisecond') {
- return this;
- }
-
- // 'date' is an alias for 'day', so it should be considered as such.
- if (units === 'date') {
- units = 'day';
- }
-
- return this.startOf(units).add(1, (units === 'isoWeek' ? 'week' : units)).subtract(1, 'ms');
-}
-
-function valueOf () {
- return this._d.valueOf() - ((this._offset || 0) * 60000);
-}
-
-function unix () {
- return Math.floor(this.valueOf() / 1000);
-}
-
-function toDate () {
- return new Date(this.valueOf());
-}
-
-function toArray () {
- var m = this;
- return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()];
-}
-
-function toObject () {
- var m = this;
- return {
- years: m.year(),
- months: m.month(),
- date: m.date(),
- hours: m.hours(),
- minutes: m.minutes(),
- seconds: m.seconds(),
- milliseconds: m.milliseconds()
- };
-}
-
-function toJSON () {
- // new Date(NaN).toJSON() === null
- return this.isValid() ? this.toISOString() : null;
-}
-
-function isValid$1 () {
- return isValid(this);
-}
-
-function parsingFlags () {
- return extend({}, getParsingFlags(this));
-}
-
-function invalidAt () {
- return getParsingFlags(this).overflow;
-}
-
-function creationData() {
- return {
- input: this._i,
- format: this._f,
- locale: this._locale,
- isUTC: this._isUTC,
- strict: this._strict
- };
-}
-
-// FORMATTING
-
-addFormatToken(0, ['gg', 2], 0, function () {
- return this.weekYear() % 100;
-});
-
-addFormatToken(0, ['GG', 2], 0, function () {
- return this.isoWeekYear() % 100;
-});
-
-function addWeekYearFormatToken (token, getter) {
- addFormatToken(0, [token, token.length], 0, getter);
-}
-
-addWeekYearFormatToken('gggg', 'weekYear');
-addWeekYearFormatToken('ggggg', 'weekYear');
-addWeekYearFormatToken('GGGG', 'isoWeekYear');
-addWeekYearFormatToken('GGGGG', 'isoWeekYear');
-
-// ALIASES
-
-addUnitAlias('weekYear', 'gg');
-addUnitAlias('isoWeekYear', 'GG');
-
-// PRIORITY
-
-addUnitPriority('weekYear', 1);
-addUnitPriority('isoWeekYear', 1);
-
-
-// PARSING
-
-addRegexToken('G', matchSigned);
-addRegexToken('g', matchSigned);
-addRegexToken('GG', match1to2, match2);
-addRegexToken('gg', match1to2, match2);
-addRegexToken('GGGG', match1to4, match4);
-addRegexToken('gggg', match1to4, match4);
-addRegexToken('GGGGG', match1to6, match6);
-addRegexToken('ggggg', match1to6, match6);
-
-addWeekParseToken(['gggg', 'ggggg', 'GGGG', 'GGGGG'], function (input, week, config, token) {
- week[token.substr(0, 2)] = toInt(input);
-});
-
-addWeekParseToken(['gg', 'GG'], function (input, week, config, token) {
- week[token] = hooks.parseTwoDigitYear(input);
-});
-
-// MOMENTS
-
-function getSetWeekYear (input) {
- return getSetWeekYearHelper.call(this,
- input,
- this.week(),
- this.weekday(),
- this.localeData()._week.dow,
- this.localeData()._week.doy);
-}
-
-function getSetISOWeekYear (input) {
- return getSetWeekYearHelper.call(this,
- input, this.isoWeek(), this.isoWeekday(), 1, 4);
-}
-
-function getISOWeeksInYear () {
- return weeksInYear(this.year(), 1, 4);
-}
-
-function getWeeksInYear () {
- var weekInfo = this.localeData()._week;
- return weeksInYear(this.year(), weekInfo.dow, weekInfo.doy);
-}
-
-function getSetWeekYearHelper(input, week, weekday, dow, doy) {
- var weeksTarget;
- if (input == null) {
- return weekOfYear(this, dow, doy).year;
- } else {
- weeksTarget = weeksInYear(input, dow, doy);
- if (week > weeksTarget) {
- week = weeksTarget;
- }
- return setWeekAll.call(this, input, week, weekday, dow, doy);
- }
-}
-
-function setWeekAll(weekYear, week, weekday, dow, doy) {
- var dayOfYearData = dayOfYearFromWeeks(weekYear, week, weekday, dow, doy),
- date = createUTCDate(dayOfYearData.year, 0, dayOfYearData.dayOfYear);
-
- this.year(date.getUTCFullYear());
- this.month(date.getUTCMonth());
- this.date(date.getUTCDate());
- return this;
-}
-
-// FORMATTING
-
-addFormatToken('Q', 0, 'Qo', 'quarter');
-
-// ALIASES
-
-addUnitAlias('quarter', 'Q');
-
-// PRIORITY
-
-addUnitPriority('quarter', 7);
-
-// PARSING
-
-addRegexToken('Q', match1);
-addParseToken('Q', function (input, array) {
- array[MONTH] = (toInt(input) - 1) * 3;
-});
-
-// MOMENTS
-
-function getSetQuarter (input) {
- return input == null ? Math.ceil((this.month() + 1) / 3) : this.month((input - 1) * 3 + this.month() % 3);
-}
-
-// FORMATTING
-
-addFormatToken('D', ['DD', 2], 'Do', 'date');
-
-// ALIASES
-
-addUnitAlias('date', 'D');
-
-// PRIOROITY
-addUnitPriority('date', 9);
-
-// PARSING
-
-addRegexToken('D', match1to2);
-addRegexToken('DD', match1to2, match2);
-addRegexToken('Do', function (isStrict, locale) {
- return isStrict ? locale._ordinalParse : locale._ordinalParseLenient;
-});
-
-addParseToken(['D', 'DD'], DATE);
-addParseToken('Do', function (input, array) {
- array[DATE] = toInt(input.match(match1to2)[0], 10);
-});
-
-// MOMENTS
-
-var getSetDayOfMonth = makeGetSet('Date', true);
-
-// FORMATTING
-
-addFormatToken('DDD', ['DDDD', 3], 'DDDo', 'dayOfYear');
-
-// ALIASES
-
-addUnitAlias('dayOfYear', 'DDD');
-
-// PRIORITY
-addUnitPriority('dayOfYear', 4);
-
-// PARSING
-
-addRegexToken('DDD', match1to3);
-addRegexToken('DDDD', match3);
-addParseToken(['DDD', 'DDDD'], function (input, array, config) {
- config._dayOfYear = toInt(input);
-});
-
-// HELPERS
-
-// MOMENTS
-
-function getSetDayOfYear (input) {
- var dayOfYear = Math.round((this.clone().startOf('day') - this.clone().startOf('year')) / 864e5) + 1;
- return input == null ? dayOfYear : this.add((input - dayOfYear), 'd');
-}
-
-// FORMATTING
-
-addFormatToken('m', ['mm', 2], 0, 'minute');
-
-// ALIASES
-
-addUnitAlias('minute', 'm');
-
-// PRIORITY
-
-addUnitPriority('minute', 14);
-
-// PARSING
-
-addRegexToken('m', match1to2);
-addRegexToken('mm', match1to2, match2);
-addParseToken(['m', 'mm'], MINUTE);
-
-// MOMENTS
-
-var getSetMinute = makeGetSet('Minutes', false);
-
-// FORMATTING
-
-addFormatToken('s', ['ss', 2], 0, 'second');
-
-// ALIASES
-
-addUnitAlias('second', 's');
-
-// PRIORITY
-
-addUnitPriority('second', 15);
-
-// PARSING
-
-addRegexToken('s', match1to2);
-addRegexToken('ss', match1to2, match2);
-addParseToken(['s', 'ss'], SECOND);
-
-// MOMENTS
-
-var getSetSecond = makeGetSet('Seconds', false);
-
-// FORMATTING
-
-addFormatToken('S', 0, 0, function () {
- return ~~(this.millisecond() / 100);
-});
-
-addFormatToken(0, ['SS', 2], 0, function () {
- return ~~(this.millisecond() / 10);
-});
-
-addFormatToken(0, ['SSS', 3], 0, 'millisecond');
-addFormatToken(0, ['SSSS', 4], 0, function () {
- return this.millisecond() * 10;
-});
-addFormatToken(0, ['SSSSS', 5], 0, function () {
- return this.millisecond() * 100;
-});
-addFormatToken(0, ['SSSSSS', 6], 0, function () {
- return this.millisecond() * 1000;
-});
-addFormatToken(0, ['SSSSSSS', 7], 0, function () {
- return this.millisecond() * 10000;
-});
-addFormatToken(0, ['SSSSSSSS', 8], 0, function () {
- return this.millisecond() * 100000;
-});
-addFormatToken(0, ['SSSSSSSSS', 9], 0, function () {
- return this.millisecond() * 1000000;
-});
-
-
-// ALIASES
-
-addUnitAlias('millisecond', 'ms');
-
-// PRIORITY
-
-addUnitPriority('millisecond', 16);
-
-// PARSING
-
-addRegexToken('S', match1to3, match1);
-addRegexToken('SS', match1to3, match2);
-addRegexToken('SSS', match1to3, match3);
-
-var token;
-for (token = 'SSSS'; token.length <= 9; token += 'S') {
- addRegexToken(token, matchUnsigned);
-}
-
-function parseMs(input, array) {
- array[MILLISECOND] = toInt(('0.' + input) * 1000);
-}
-
-for (token = 'S'; token.length <= 9; token += 'S') {
- addParseToken(token, parseMs);
-}
-// MOMENTS
-
-var getSetMillisecond = makeGetSet('Milliseconds', false);
-
-// FORMATTING
-
-addFormatToken('z', 0, 0, 'zoneAbbr');
-addFormatToken('zz', 0, 0, 'zoneName');
-
-// MOMENTS
-
-function getZoneAbbr () {
- return this._isUTC ? 'UTC' : '';
-}
-
-function getZoneName () {
- return this._isUTC ? 'Coordinated Universal Time' : '';
-}
-
-var proto = Moment.prototype;
-
-proto.add = add;
-proto.calendar = calendar$1;
-proto.clone = clone;
-proto.diff = diff;
-proto.endOf = endOf;
-proto.format = format;
-proto.from = from;
-proto.fromNow = fromNow;
-proto.to = to;
-proto.toNow = toNow;
-proto.get = stringGet;
-proto.invalidAt = invalidAt;
-proto.isAfter = isAfter;
-proto.isBefore = isBefore;
-proto.isBetween = isBetween;
-proto.isSame = isSame;
-proto.isSameOrAfter = isSameOrAfter;
-proto.isSameOrBefore = isSameOrBefore;
-proto.isValid = isValid$1;
-proto.lang = lang;
-proto.locale = locale;
-proto.localeData = localeData;
-proto.max = prototypeMax;
-proto.min = prototypeMin;
-proto.parsingFlags = parsingFlags;
-proto.set = stringSet;
-proto.startOf = startOf;
-proto.subtract = subtract;
-proto.toArray = toArray;
-proto.toObject = toObject;
-proto.toDate = toDate;
-proto.toISOString = toISOString;
-proto.inspect = inspect;
-proto.toJSON = toJSON;
-proto.toString = toString;
-proto.unix = unix;
-proto.valueOf = valueOf;
-proto.creationData = creationData;
-
-// Year
-proto.year = getSetYear;
-proto.isLeapYear = getIsLeapYear;
-
-// Week Year
-proto.weekYear = getSetWeekYear;
-proto.isoWeekYear = getSetISOWeekYear;
-
-// Quarter
-proto.quarter = proto.quarters = getSetQuarter;
-
-// Month
-proto.month = getSetMonth;
-proto.daysInMonth = getDaysInMonth;
-
-// Week
-proto.week = proto.weeks = getSetWeek;
-proto.isoWeek = proto.isoWeeks = getSetISOWeek;
-proto.weeksInYear = getWeeksInYear;
-proto.isoWeeksInYear = getISOWeeksInYear;
-
-// Day
-proto.date = getSetDayOfMonth;
-proto.day = proto.days = getSetDayOfWeek;
-proto.weekday = getSetLocaleDayOfWeek;
-proto.isoWeekday = getSetISODayOfWeek;
-proto.dayOfYear = getSetDayOfYear;
-
-// Hour
-proto.hour = proto.hours = getSetHour;
-
-// Minute
-proto.minute = proto.minutes = getSetMinute;
-
-// Second
-proto.second = proto.seconds = getSetSecond;
-
-// Millisecond
-proto.millisecond = proto.milliseconds = getSetMillisecond;
-
-// Offset
-proto.utcOffset = getSetOffset;
-proto.utc = setOffsetToUTC;
-proto.local = setOffsetToLocal;
-proto.parseZone = setOffsetToParsedOffset;
-proto.hasAlignedHourOffset = hasAlignedHourOffset;
-proto.isDST = isDaylightSavingTime;
-proto.isLocal = isLocal;
-proto.isUtcOffset = isUtcOffset;
-proto.isUtc = isUtc;
-proto.isUTC = isUtc;
-
-// Timezone
-proto.zoneAbbr = getZoneAbbr;
-proto.zoneName = getZoneName;
-
-// Deprecations
-proto.dates = deprecate('dates accessor is deprecated. Use date instead.', getSetDayOfMonth);
-proto.months = deprecate('months accessor is deprecated. Use month instead', getSetMonth);
-proto.years = deprecate('years accessor is deprecated. Use year instead', getSetYear);
-proto.zone = deprecate('moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/', getSetZone);
-proto.isDSTShifted = deprecate('isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information', isDaylightSavingTimeShifted);
-
-function createUnix (input) {
- return createLocal(input * 1000);
-}
-
-function createInZone () {
- return createLocal.apply(null, arguments).parseZone();
-}
-
-function preParsePostFormat (string) {
- return string;
-}
-
-var proto$1 = Locale.prototype;
-
-proto$1.calendar = calendar;
-proto$1.longDateFormat = longDateFormat;
-proto$1.invalidDate = invalidDate;
-proto$1.ordinal = ordinal;
-proto$1.preparse = preParsePostFormat;
-proto$1.postformat = preParsePostFormat;
-proto$1.relativeTime = relativeTime;
-proto$1.pastFuture = pastFuture;
-proto$1.set = set;
-
-// Month
-proto$1.months = localeMonths;
-proto$1.monthsShort = localeMonthsShort;
-proto$1.monthsParse = localeMonthsParse;
-proto$1.monthsRegex = monthsRegex;
-proto$1.monthsShortRegex = monthsShortRegex;
-
-// Week
-proto$1.week = localeWeek;
-proto$1.firstDayOfYear = localeFirstDayOfYear;
-proto$1.firstDayOfWeek = localeFirstDayOfWeek;
-
-// Day of Week
-proto$1.weekdays = localeWeekdays;
-proto$1.weekdaysMin = localeWeekdaysMin;
-proto$1.weekdaysShort = localeWeekdaysShort;
-proto$1.weekdaysParse = localeWeekdaysParse;
-
-proto$1.weekdaysRegex = weekdaysRegex;
-proto$1.weekdaysShortRegex = weekdaysShortRegex;
-proto$1.weekdaysMinRegex = weekdaysMinRegex;
-
-// Hours
-proto$1.isPM = localeIsPM;
-proto$1.meridiem = localeMeridiem;
-
-function get$1 (format, index, field, setter) {
- var locale = getLocale();
- var utc = createUTC().set(setter, index);
- return locale[field](utc, format);
-}
-
-function listMonthsImpl (format, index, field) {
- if (isNumber(format)) {
- index = format;
- format = undefined;
- }
-
- format = format || '';
-
- if (index != null) {
- return get$1(format, index, field, 'month');
- }
-
- var i;
- var out = [];
- for (i = 0; i < 12; i++) {
- out[i] = get$1(format, i, field, 'month');
- }
- return out;
-}
-
-// ()
-// (5)
-// (fmt, 5)
-// (fmt)
-// (true)
-// (true, 5)
-// (true, fmt, 5)
-// (true, fmt)
-function listWeekdaysImpl (localeSorted, format, index, field) {
- if (typeof localeSorted === 'boolean') {
- if (isNumber(format)) {
- index = format;
- format = undefined;
- }
-
- format = format || '';
- } else {
- format = localeSorted;
- index = format;
- localeSorted = false;
-
- if (isNumber(format)) {
- index = format;
- format = undefined;
- }
-
- format = format || '';
- }
-
- var locale = getLocale(),
- shift = localeSorted ? locale._week.dow : 0;
-
- if (index != null) {
- return get$1(format, (index + shift) % 7, field, 'day');
- }
-
- var i;
- var out = [];
- for (i = 0; i < 7; i++) {
- out[i] = get$1(format, (i + shift) % 7, field, 'day');
- }
- return out;
-}
-
-function listMonths (format, index) {
- return listMonthsImpl(format, index, 'months');
-}
-
-function listMonthsShort (format, index) {
- return listMonthsImpl(format, index, 'monthsShort');
-}
-
-function listWeekdays (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdays');
-}
-
-function listWeekdaysShort (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdaysShort');
-}
-
-function listWeekdaysMin (localeSorted, format, index) {
- return listWeekdaysImpl(localeSorted, format, index, 'weekdaysMin');
-}
-
-getSetGlobalLocale('en', {
- ordinalParse: /\d{1,2}(th|st|nd|rd)/,
- ordinal : function (number) {
- var b = number % 10,
- output = (toInt(number % 100 / 10) === 1) ? 'th' :
- (b === 1) ? 'st' :
- (b === 2) ? 'nd' :
- (b === 3) ? 'rd' : 'th';
- return number + output;
- }
-});
-
-// Side effect imports
-hooks.lang = deprecate('moment.lang is deprecated. Use moment.locale instead.', getSetGlobalLocale);
-hooks.langData = deprecate('moment.langData is deprecated. Use moment.localeData instead.', getLocale);
-
-var mathAbs = Math.abs;
-
-function abs () {
- var data = this._data;
-
- this._milliseconds = mathAbs(this._milliseconds);
- this._days = mathAbs(this._days);
- this._months = mathAbs(this._months);
-
- data.milliseconds = mathAbs(data.milliseconds);
- data.seconds = mathAbs(data.seconds);
- data.minutes = mathAbs(data.minutes);
- data.hours = mathAbs(data.hours);
- data.months = mathAbs(data.months);
- data.years = mathAbs(data.years);
-
- return this;
-}
-
-function addSubtract$1 (duration, input, value, direction) {
- var other = createDuration(input, value);
-
- duration._milliseconds += direction * other._milliseconds;
- duration._days += direction * other._days;
- duration._months += direction * other._months;
-
- return duration._bubble();
-}
-
-// supports only 2.0-style add(1, 's') or add(duration)
-function add$1 (input, value) {
- return addSubtract$1(this, input, value, 1);
-}
-
-// supports only 2.0-style subtract(1, 's') or subtract(duration)
-function subtract$1 (input, value) {
- return addSubtract$1(this, input, value, -1);
-}
-
-function absCeil (number) {
- if (number < 0) {
- return Math.floor(number);
- } else {
- return Math.ceil(number);
- }
-}
-
-function bubble () {
- var milliseconds = this._milliseconds;
- var days = this._days;
- var months = this._months;
- var data = this._data;
- var seconds, minutes, hours, years, monthsFromDays;
-
- // if we have a mix of positive and negative values, bubble down first
- // check: https://github.com/moment/moment/issues/2166
- if (!((milliseconds >= 0 && days >= 0 && months >= 0) ||
- (milliseconds <= 0 && days <= 0 && months <= 0))) {
- milliseconds += absCeil(monthsToDays(months) + days) * 864e5;
- days = 0;
- months = 0;
- }
-
- // The following code bubbles up values, see the tests for
- // examples of what that means.
- data.milliseconds = milliseconds % 1000;
-
- seconds = absFloor(milliseconds / 1000);
- data.seconds = seconds % 60;
-
- minutes = absFloor(seconds / 60);
- data.minutes = minutes % 60;
-
- hours = absFloor(minutes / 60);
- data.hours = hours % 24;
-
- days += absFloor(hours / 24);
-
- // convert days to months
- monthsFromDays = absFloor(daysToMonths(days));
- months += monthsFromDays;
- days -= absCeil(monthsToDays(monthsFromDays));
-
- // 12 months -> 1 year
- years = absFloor(months / 12);
- months %= 12;
-
- data.days = days;
- data.months = months;
- data.years = years;
-
- return this;
-}
-
-function daysToMonths (days) {
- // 400 years have 146097 days (taking into account leap year rules)
- // 400 years have 12 months === 4800
- return days * 4800 / 146097;
-}
-
-function monthsToDays (months) {
- // the reverse of daysToMonths
- return months * 146097 / 4800;
-}
-
-function as (units) {
- var days;
- var months;
- var milliseconds = this._milliseconds;
-
- units = normalizeUnits(units);
-
- if (units === 'month' || units === 'year') {
- days = this._days + milliseconds / 864e5;
- months = this._months + daysToMonths(days);
- return units === 'month' ? months : months / 12;
- } else {
- // handle milliseconds separately because of floating point math errors (issue #1867)
- days = this._days + Math.round(monthsToDays(this._months));
- switch (units) {
- case 'week' : return days / 7 + milliseconds / 6048e5;
- case 'day' : return days + milliseconds / 864e5;
- case 'hour' : return days * 24 + milliseconds / 36e5;
- case 'minute' : return days * 1440 + milliseconds / 6e4;
- case 'second' : return days * 86400 + milliseconds / 1000;
- // Math.floor prevents floating point math errors here
- case 'millisecond': return Math.floor(days * 864e5) + milliseconds;
- default: throw new Error('Unknown unit ' + units);
- }
- }
-}
-
-// TODO: Use this.as('ms')?
-function valueOf$1 () {
- return (
- this._milliseconds +
- this._days * 864e5 +
- (this._months % 12) * 2592e6 +
- toInt(this._months / 12) * 31536e6
- );
-}
-
-function makeAs (alias) {
- return function () {
- return this.as(alias);
- };
-}
-
-var asMilliseconds = makeAs('ms');
-var asSeconds = makeAs('s');
-var asMinutes = makeAs('m');
-var asHours = makeAs('h');
-var asDays = makeAs('d');
-var asWeeks = makeAs('w');
-var asMonths = makeAs('M');
-var asYears = makeAs('y');
-
-function get$2 (units) {
- units = normalizeUnits(units);
- return this[units + 's']();
-}
-
-function makeGetter(name) {
- return function () {
- return this._data[name];
- };
-}
-
-var milliseconds = makeGetter('milliseconds');
-var seconds = makeGetter('seconds');
-var minutes = makeGetter('minutes');
-var hours = makeGetter('hours');
-var days = makeGetter('days');
-var months = makeGetter('months');
-var years = makeGetter('years');
-
-function weeks () {
- return absFloor(this.days() / 7);
-}
-
-var round = Math.round;
-var thresholds = {
- s: 45, // seconds to minute
- m: 45, // minutes to hour
- h: 22, // hours to day
- d: 26, // days to month
- M: 11 // months to year
-};
-
-// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
-function substituteTimeAgo(string, number, withoutSuffix, isFuture, locale) {
- return locale.relativeTime(number || 1, !!withoutSuffix, string, isFuture);
-}
-
-function relativeTime$1 (posNegDuration, withoutSuffix, locale) {
- var duration = createDuration(posNegDuration).abs();
- var seconds = round(duration.as('s'));
- var minutes = round(duration.as('m'));
- var hours = round(duration.as('h'));
- var days = round(duration.as('d'));
- var months = round(duration.as('M'));
- var years = round(duration.as('y'));
-
- var a = seconds < thresholds.s && ['s', seconds] ||
- minutes <= 1 && ['m'] ||
- minutes < thresholds.m && ['mm', minutes] ||
- hours <= 1 && ['h'] ||
- hours < thresholds.h && ['hh', hours] ||
- days <= 1 && ['d'] ||
- days < thresholds.d && ['dd', days] ||
- months <= 1 && ['M'] ||
- months < thresholds.M && ['MM', months] ||
- years <= 1 && ['y'] || ['yy', years];
-
- a[2] = withoutSuffix;
- a[3] = +posNegDuration > 0;
- a[4] = locale;
- return substituteTimeAgo.apply(null, a);
-}
-
-// This function allows you to set the rounding function for relative time strings
-function getSetRelativeTimeRounding (roundingFunction) {
- if (roundingFunction === undefined) {
- return round;
- }
- if (typeof(roundingFunction) === 'function') {
- round = roundingFunction;
- return true;
- }
- return false;
-}
-
-// This function allows you to set a threshold for relative time strings
-function getSetRelativeTimeThreshold (threshold, limit) {
- if (thresholds[threshold] === undefined) {
- return false;
- }
- if (limit === undefined) {
- return thresholds[threshold];
- }
- thresholds[threshold] = limit;
- return true;
-}
-
-function humanize (withSuffix) {
- var locale = this.localeData();
- var output = relativeTime$1(this, !withSuffix, locale);
-
- if (withSuffix) {
- output = locale.pastFuture(+this, output);
- }
-
- return locale.postformat(output);
-}
-
-var abs$1 = Math.abs;
-
-function toISOString$1() {
- // for ISO strings we do not use the normal bubbling rules:
- // * milliseconds bubble up until they become hours
- // * days do not bubble at all
- // * months bubble up until they become years
- // This is because there is no context-free conversion between hours and days
- // (think of clock changes)
- // and also not between days and months (28-31 days per month)
- var seconds = abs$1(this._milliseconds) / 1000;
- var days = abs$1(this._days);
- var months = abs$1(this._months);
- var minutes, hours, years;
-
- // 3600 seconds -> 60 minutes -> 1 hour
- minutes = absFloor(seconds / 60);
- hours = absFloor(minutes / 60);
- seconds %= 60;
- minutes %= 60;
-
- // 12 months -> 1 year
- years = absFloor(months / 12);
- months %= 12;
-
-
- // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js
- var Y = years;
- var M = months;
- var D = days;
- var h = hours;
- var m = minutes;
- var s = seconds;
- var total = this.asSeconds();
-
- if (!total) {
- // this is the same as C#'s (Noda) and python (isodate)...
- // but not other JS (goog.date)
- return 'P0D';
- }
-
- return (total < 0 ? '-' : '') +
- 'P' +
- (Y ? Y + 'Y' : '') +
- (M ? M + 'M' : '') +
- (D ? D + 'D' : '') +
- ((h || m || s) ? 'T' : '') +
- (h ? h + 'H' : '') +
- (m ? m + 'M' : '') +
- (s ? s + 'S' : '');
-}
-
-var proto$2 = Duration.prototype;
-
-proto$2.abs = abs;
-proto$2.add = add$1;
-proto$2.subtract = subtract$1;
-proto$2.as = as;
-proto$2.asMilliseconds = asMilliseconds;
-proto$2.asSeconds = asSeconds;
-proto$2.asMinutes = asMinutes;
-proto$2.asHours = asHours;
-proto$2.asDays = asDays;
-proto$2.asWeeks = asWeeks;
-proto$2.asMonths = asMonths;
-proto$2.asYears = asYears;
-proto$2.valueOf = valueOf$1;
-proto$2._bubble = bubble;
-proto$2.get = get$2;
-proto$2.milliseconds = milliseconds;
-proto$2.seconds = seconds;
-proto$2.minutes = minutes;
-proto$2.hours = hours;
-proto$2.days = days;
-proto$2.weeks = weeks;
-proto$2.months = months;
-proto$2.years = years;
-proto$2.humanize = humanize;
-proto$2.toISOString = toISOString$1;
-proto$2.toString = toISOString$1;
-proto$2.toJSON = toISOString$1;
-proto$2.locale = locale;
-proto$2.localeData = localeData;
-
-// Deprecations
-proto$2.toIsoString = deprecate('toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)', toISOString$1);
-proto$2.lang = lang;
-
-// Side effect imports
-
-// FORMATTING
-
-addFormatToken('X', 0, 0, 'unix');
-addFormatToken('x', 0, 0, 'valueOf');
-
-// PARSING
-
-addRegexToken('x', matchSigned);
-addRegexToken('X', matchTimestamp);
-addParseToken('X', function (input, array, config) {
- config._d = new Date(parseFloat(input, 10) * 1000);
-});
-addParseToken('x', function (input, array, config) {
- config._d = new Date(toInt(input));
-});
-
-// Side effect imports
-
-
-hooks.version = '2.17.1';
-
-setHookCallback(createLocal);
-
-hooks.fn = proto;
-hooks.min = min;
-hooks.max = max;
-hooks.now = now;
-hooks.utc = createUTC;
-hooks.unix = createUnix;
-hooks.months = listMonths;
-hooks.isDate = isDate;
-hooks.locale = getSetGlobalLocale;
-hooks.invalid = createInvalid;
-hooks.duration = createDuration;
-hooks.isMoment = isMoment;
-hooks.weekdays = listWeekdays;
-hooks.parseZone = createInZone;
-hooks.localeData = getLocale;
-hooks.isDuration = isDuration;
-hooks.monthsShort = listMonthsShort;
-hooks.weekdaysMin = listWeekdaysMin;
-hooks.defineLocale = defineLocale;
-hooks.updateLocale = updateLocale;
-hooks.locales = listLocales;
-hooks.weekdaysShort = listWeekdaysShort;
-hooks.normalizeUnits = normalizeUnits;
-hooks.relativeTimeRounding = getSetRelativeTimeRounding;
-hooks.relativeTimeThreshold = getSetRelativeTimeThreshold;
-hooks.calendarFormat = getCalendarFormat;
-hooks.prototype = proto;
-
-return hooks;
-
-})));
-
-},{}],7:[function(require,module,exports){
-/**
- * @namespace Chart
- */
-var Chart = require(28)();
-
-require(26)(Chart);
-require(42)(Chart);
-require(22)(Chart);
-require(31)(Chart);
-require(25)(Chart);
-require(21)(Chart);
-require(23)(Chart);
-require(24)(Chart);
-require(29)(Chart);
-require(33)(Chart);
-require(34)(Chart);
-require(32)(Chart);
-require(35)(Chart);
-require(30)(Chart);
-require(27)(Chart);
-require(36)(Chart);
-
-require(37)(Chart);
-require(38)(Chart);
-require(39)(Chart);
-require(40)(Chart);
-
-require(45)(Chart);
-require(43)(Chart);
-require(44)(Chart);
-require(46)(Chart);
-require(47)(Chart);
-require(48)(Chart);
-
-// Controllers must be loaded after elements
-// See Chart.core.datasetController.dataElementType
-require(15)(Chart);
-require(16)(Chart);
-require(17)(Chart);
-require(18)(Chart);
-require(19)(Chart);
-require(20)(Chart);
-
-require(8)(Chart);
-require(9)(Chart);
-require(10)(Chart);
-require(11)(Chart);
-require(12)(Chart);
-require(13)(Chart);
-require(14)(Chart);
-
-window.Chart = module.exports = Chart;
-
-},{"10":10,"11":11,"12":12,"13":13,"14":14,"15":15,"16":16,"17":17,"18":18,"19":19,"20":20,"21":21,"22":22,"23":23,"24":24,"25":25,"26":26,"27":27,"28":28,"29":29,"30":30,"31":31,"32":32,"33":33,"34":34,"35":35,"36":36,"37":37,"38":38,"39":39,"40":40,"42":42,"43":43,"44":44,"45":45,"46":46,"47":47,"48":48,"8":8,"9":9}],8:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.Bar = function(context, config) {
- config.type = 'bar';
-
- return new Chart(context, config);
- };
-
-};
-
-},{}],9:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.Bubble = function(context, config) {
- config.type = 'bubble';
- return new Chart(context, config);
- };
-
-};
-
-},{}],10:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.Doughnut = function(context, config) {
- config.type = 'doughnut';
-
- return new Chart(context, config);
- };
-
-};
-
-},{}],11:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.Line = function(context, config) {
- config.type = 'line';
-
- return new Chart(context, config);
- };
-
-};
-
-},{}],12:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.PolarArea = function(context, config) {
- config.type = 'polarArea';
-
- return new Chart(context, config);
- };
-
-};
-
-},{}],13:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- Chart.Radar = function(context, config) {
- config.type = 'radar';
-
- return new Chart(context, config);
- };
-
-};
-
-},{}],14:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- var defaultConfig = {
- hover: {
- mode: 'single'
- },
-
- scales: {
- xAxes: [{
- type: 'linear', // scatter should not use a category axis
- position: 'bottom',
- id: 'x-axis-1' // need an ID so datasets can reference the scale
- }],
- yAxes: [{
- type: 'linear',
- position: 'left',
- id: 'y-axis-1'
- }]
- },
-
- tooltips: {
- callbacks: {
- title: function() {
- // Title doesn't make sense for scatter since we format the data as a point
- return '';
- },
- label: function(tooltipItem) {
- return '(' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ')';
- }
- }
- }
- };
-
- // Register the default config for this type
- Chart.defaults.scatter = defaultConfig;
-
- // Scatter charts use line controllers
- Chart.controllers.scatter = Chart.controllers.line;
-
- Chart.Scatter = function(context, config) {
- config.type = 'scatter';
- return new Chart(context, config);
- };
-
-};
-
-},{}],15:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- var helpers = Chart.helpers;
-
- Chart.defaults.bar = {
- hover: {
- mode: 'label'
- },
-
- scales: {
- xAxes: [{
- type: 'category',
-
- // Specific to Bar Controller
- categoryPercentage: 0.8,
- barPercentage: 0.9,
-
- // grid line settings
- gridLines: {
- offsetGridLines: true
- }
- }],
- yAxes: [{
- type: 'linear'
- }]
- }
- };
-
- Chart.controllers.bar = Chart.DatasetController.extend({
-
- dataElementType: Chart.elements.Rectangle,
-
- initialize: function(chart, datasetIndex) {
- Chart.DatasetController.prototype.initialize.call(this, chart, datasetIndex);
-
- var me = this;
- var meta = me.getMeta();
- var dataset = me.getDataset();
-
- meta.stack = dataset.stack;
- // Use this to indicate that this is a bar dataset.
- meta.bar = true;
- },
-
- // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible
- getStackCount: function() {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
-
- var stacks = [];
- helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
- var dsMeta = me.chart.getDatasetMeta(datasetIndex);
- if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) &&
- (yScale.options.stacked === false ||
- (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
- (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
- stacks.push(dsMeta.stack);
- }
- }, me);
-
- return stacks.length;
- },
-
- update: function(reset) {
- var me = this;
- helpers.each(me.getMeta().data, function(rectangle, index) {
- me.updateElement(rectangle, index, reset);
- }, me);
- },
-
- updateElement: function(rectangle, index, reset) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var yScale = me.getScaleForId(meta.yAxisID);
- var scaleBase = yScale.getBasePixel();
- var rectangleElementOptions = me.chart.options.elements.rectangle;
- var custom = rectangle.custom || {};
- var dataset = me.getDataset();
-
- rectangle._xScale = xScale;
- rectangle._yScale = yScale;
- rectangle._datasetIndex = me.index;
- rectangle._index = index;
-
- var ruler = me.getRuler(index); // The index argument for compatible
- rectangle._model = {
- x: me.calculateBarX(index, me.index, ruler),
- y: reset ? scaleBase : me.calculateBarY(index, me.index),
-
- // Tooltip
- label: me.chart.data.labels[index],
- datasetLabel: dataset.label,
-
- // Appearance
- horizontal: false,
- base: reset ? scaleBase : me.calculateBarBase(me.index, index),
- width: me.calculateBarWidth(ruler),
- backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
- borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
- borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
- borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
- };
-
- rectangle.pivot();
- },
-
- calculateBarBase: function(datasetIndex, index) {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
- var base = yScale.getBaseValue();
- var original = base;
-
- if ((yScale.options.stacked === true) ||
- (yScale.options.stacked === undefined && meta.stack !== undefined)) {
- var chart = me.chart;
- var datasets = chart.data.datasets;
- var value = Number(datasets[datasetIndex].data[index]);
-
- for (var i = 0; i < datasetIndex; i++) {
- var currentDs = datasets[i];
- var currentDsMeta = chart.getDatasetMeta(i);
- if (currentDsMeta.bar && currentDsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i) &&
- meta.stack === currentDsMeta.stack) {
- var currentVal = Number(currentDs.data[index]);
- base += value < 0 ? Math.min(currentVal, original) : Math.max(currentVal, original);
- }
- }
-
- return yScale.getPixelForValue(base);
- }
-
- return yScale.getBasePixel();
- },
-
- getRuler: function() {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var stackCount = me.getStackCount();
-
- var tickWidth = xScale.width / xScale.ticks.length;
- var categoryWidth = tickWidth * xScale.options.categoryPercentage;
- var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
- var fullBarWidth = categoryWidth / stackCount;
-
- var barWidth = fullBarWidth * xScale.options.barPercentage;
- var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
-
- return {
- stackCount: stackCount,
- tickWidth: tickWidth,
- categoryWidth: categoryWidth,
- categorySpacing: categorySpacing,
- fullBarWidth: fullBarWidth,
- barWidth: barWidth,
- barSpacing: barSpacing
- };
- },
-
- calculateBarWidth: function(ruler) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- if (xScale.options.barThickness) {
- return xScale.options.barThickness;
- }
- return ruler.barWidth;
- },
-
- // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible
- getStackIndex: function(datasetIndex) {
- var me = this;
- var meta = me.chart.getDatasetMeta(datasetIndex);
- var yScale = me.getScaleForId(meta.yAxisID);
- var dsMeta, j;
- var stacks = [meta.stack];
-
- for (j = 0; j < datasetIndex; ++j) {
- dsMeta = this.chart.getDatasetMeta(j);
- if (dsMeta.bar && this.chart.isDatasetVisible(j) &&
- (yScale.options.stacked === false ||
- (yScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
- (yScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
- stacks.push(dsMeta.stack);
- }
- }
-
- return stacks.length - 1;
- },
-
- calculateBarX: function(index, datasetIndex, ruler) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var stackIndex = me.getStackIndex(datasetIndex);
- var leftTick = xScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
- leftTick -= me.chart.isCombo ? (ruler.tickWidth / 2) : 0;
-
- return leftTick +
- (ruler.barWidth / 2) +
- ruler.categorySpacing +
- (ruler.barWidth * stackIndex) +
- (ruler.barSpacing / 2) +
- (ruler.barSpacing * stackIndex);
- },
-
- calculateBarY: function(index, datasetIndex) {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
- var value = Number(me.getDataset().data[index]);
-
- if (yScale.options.stacked ||
- (yScale.options.stacked === undefined && meta.stack !== undefined)) {
- var base = yScale.getBaseValue();
- var sumPos = base,
- sumNeg = base;
-
- for (var i = 0; i < datasetIndex; i++) {
- var ds = me.chart.data.datasets[i];
- var dsMeta = me.chart.getDatasetMeta(i);
- if (dsMeta.bar && dsMeta.yAxisID === yScale.id && me.chart.isDatasetVisible(i) &&
- meta.stack === dsMeta.stack) {
- var stackedVal = Number(ds.data[index]);
- if (stackedVal < 0) {
- sumNeg += stackedVal || 0;
- } else {
- sumPos += stackedVal || 0;
- }
- }
- }
-
- if (value < 0) {
- return yScale.getPixelForValue(sumNeg + value);
- }
- return yScale.getPixelForValue(sumPos + value);
- }
-
- return yScale.getPixelForValue(value);
- },
-
- draw: function(ease) {
- var me = this;
- var easingDecimal = ease || 1;
- var metaData = me.getMeta().data;
- var dataset = me.getDataset();
- var i, len;
-
- Chart.canvasHelpers.clipArea(me.chart.chart.ctx, me.chart.chartArea);
- for (i = 0, len = metaData.length; i < len; ++i) {
- var d = dataset.data[i];
- if (d !== null && d !== undefined && !isNaN(d)) {
- metaData[i].transition(easingDecimal).draw();
- }
- }
- Chart.canvasHelpers.unclipArea(me.chart.chart.ctx);
- },
-
- setHoverStyle: function(rectangle) {
- var dataset = this.chart.data.datasets[rectangle._datasetIndex];
- var index = rectangle._index;
-
- var custom = rectangle.custom || {};
- var model = rectangle._model;
- model.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));
- model.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));
- model.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);
- },
-
- removeHoverStyle: function(rectangle) {
- var dataset = this.chart.data.datasets[rectangle._datasetIndex];
- var index = rectangle._index;
- var custom = rectangle.custom || {};
- var model = rectangle._model;
- var rectangleElementOptions = this.chart.options.elements.rectangle;
-
- model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);
- model.borderColor = custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);
- model.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);
- }
-
- });
-
-
- // including horizontalBar in the bar file, instead of a file of its own
- // it extends bar (like pie extends doughnut)
- Chart.defaults.horizontalBar = {
- hover: {
- mode: 'label'
- },
-
- scales: {
- xAxes: [{
- type: 'linear',
- position: 'bottom'
- }],
- yAxes: [{
- position: 'left',
- type: 'category',
-
- // Specific to Horizontal Bar Controller
- categoryPercentage: 0.8,
- barPercentage: 0.9,
-
- // grid line settings
- gridLines: {
- offsetGridLines: true
- }
- }]
- },
- elements: {
- rectangle: {
- borderSkipped: 'left'
- }
- },
- tooltips: {
- callbacks: {
- title: function(tooltipItems, data) {
- // Pick first xLabel for now
- var title = '';
-
- if (tooltipItems.length > 0) {
- if (tooltipItems[0].yLabel) {
- title = tooltipItems[0].yLabel;
- } else if (data.labels.length > 0 && tooltipItems[0].index < data.labels.length) {
- title = data.labels[tooltipItems[0].index];
- }
- }
-
- return title;
- },
- label: function(tooltipItem, data) {
- var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
- return datasetLabel + ': ' + tooltipItem.xLabel;
- }
- }
- }
- };
-
- Chart.controllers.horizontalBar = Chart.controllers.bar.extend({
-
- // Correctly calculate the bar width accounting for stacks and the fact that not all bars are visible
- getStackCount: function() {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
-
- var stacks = [];
- helpers.each(me.chart.data.datasets, function(dataset, datasetIndex) {
- var dsMeta = me.chart.getDatasetMeta(datasetIndex);
- if (dsMeta.bar && me.chart.isDatasetVisible(datasetIndex) &&
- (xScale.options.stacked === false ||
- (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
- (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
- stacks.push(dsMeta.stack);
- }
- }, me);
-
- return stacks.length;
- },
-
- updateElement: function(rectangle, index, reset) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var yScale = me.getScaleForId(meta.yAxisID);
- var scaleBase = xScale.getBasePixel();
- var custom = rectangle.custom || {};
- var dataset = me.getDataset();
- var rectangleElementOptions = me.chart.options.elements.rectangle;
-
- rectangle._xScale = xScale;
- rectangle._yScale = yScale;
- rectangle._datasetIndex = me.index;
- rectangle._index = index;
-
- var ruler = me.getRuler(index); // The index argument for compatible
- rectangle._model = {
- x: reset ? scaleBase : me.calculateBarX(index, me.index),
- y: me.calculateBarY(index, me.index, ruler),
-
- // Tooltip
- label: me.chart.data.labels[index],
- datasetLabel: dataset.label,
-
- // Appearance
- horizontal: true,
- base: reset ? scaleBase : me.calculateBarBase(me.index, index),
- height: me.calculateBarHeight(ruler),
- backgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.getValueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor),
- borderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleElementOptions.borderSkipped,
- borderColor: custom.borderColor ? custom.borderColor : helpers.getValueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor),
- borderWidth: custom.borderWidth ? custom.borderWidth : helpers.getValueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth)
- };
-
- rectangle.pivot();
- },
-
- calculateBarBase: function(datasetIndex, index) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var base = xScale.getBaseValue();
- var originalBase = base;
-
- if (xScale.options.stacked ||
- (xScale.options.stacked === undefined && meta.stack !== undefined)) {
- var chart = me.chart;
- var datasets = chart.data.datasets;
- var value = Number(datasets[datasetIndex].data[index]);
-
- for (var i = 0; i < datasetIndex; i++) {
- var currentDs = datasets[i];
- var currentDsMeta = chart.getDatasetMeta(i);
- if (currentDsMeta.bar && currentDsMeta.xAxisID === xScale.id && chart.isDatasetVisible(i) &&
- meta.stack === currentDsMeta.stack) {
- var currentVal = Number(currentDs.data[index]);
- base += value < 0 ? Math.min(currentVal, originalBase) : Math.max(currentVal, originalBase);
- }
- }
-
- return xScale.getPixelForValue(base);
- }
-
- return xScale.getBasePixel();
- },
-
- getRuler: function() {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
- var stackCount = me.getStackCount();
-
- var tickHeight = yScale.height / yScale.ticks.length;
- var categoryHeight = tickHeight * yScale.options.categoryPercentage;
- var categorySpacing = (tickHeight - (tickHeight * yScale.options.categoryPercentage)) / 2;
- var fullBarHeight = categoryHeight / stackCount;
-
- var barHeight = fullBarHeight * yScale.options.barPercentage;
- var barSpacing = fullBarHeight - (fullBarHeight * yScale.options.barPercentage);
-
- return {
- stackCount: stackCount,
- tickHeight: tickHeight,
- categoryHeight: categoryHeight,
- categorySpacing: categorySpacing,
- fullBarHeight: fullBarHeight,
- barHeight: barHeight,
- barSpacing: barSpacing
- };
- },
-
- calculateBarHeight: function(ruler) {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
- if (yScale.options.barThickness) {
- return yScale.options.barThickness;
- }
- return ruler.barHeight;
- },
-
- // Get stack index from the given dataset index accounting for stacks and the fact that not all bars are visible
- getStackIndex: function(datasetIndex) {
- var me = this;
- var meta = me.chart.getDatasetMeta(datasetIndex);
- var xScale = me.getScaleForId(meta.xAxisID);
- var dsMeta, j;
- var stacks = [meta.stack];
-
- for (j = 0; j < datasetIndex; ++j) {
- dsMeta = this.chart.getDatasetMeta(j);
- if (dsMeta.bar && this.chart.isDatasetVisible(j) &&
- (xScale.options.stacked === false ||
- (xScale.options.stacked === true && stacks.indexOf(dsMeta.stack) === -1) ||
- (xScale.options.stacked === undefined && (dsMeta.stack === undefined || stacks.indexOf(dsMeta.stack) === -1)))) {
- stacks.push(dsMeta.stack);
- }
- }
-
- return stacks.length - 1;
- },
-
- calculateBarX: function(index, datasetIndex) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var value = Number(me.getDataset().data[index]);
-
- if (xScale.options.stacked ||
- (xScale.options.stacked === undefined && meta.stack !== undefined)) {
- var base = xScale.getBaseValue();
- var sumPos = base,
- sumNeg = base;
-
- for (var i = 0; i < datasetIndex; i++) {
- var ds = me.chart.data.datasets[i];
- var dsMeta = me.chart.getDatasetMeta(i);
- if (dsMeta.bar && dsMeta.xAxisID === xScale.id && me.chart.isDatasetVisible(i) &&
- meta.stack === dsMeta.stack) {
- var stackedVal = Number(ds.data[index]);
- if (stackedVal < 0) {
- sumNeg += stackedVal || 0;
- } else {
- sumPos += stackedVal || 0;
- }
- }
- }
-
- if (value < 0) {
- return xScale.getPixelForValue(sumNeg + value);
- }
- return xScale.getPixelForValue(sumPos + value);
- }
-
- return xScale.getPixelForValue(value);
- },
-
- calculateBarY: function(index, datasetIndex, ruler) {
- var me = this;
- var meta = me.getMeta();
- var yScale = me.getScaleForId(meta.yAxisID);
- var stackIndex = me.getStackIndex(datasetIndex);
- var topTick = yScale.getPixelForValue(null, index, datasetIndex, me.chart.isCombo);
- topTick -= me.chart.isCombo ? (ruler.tickHeight / 2) : 0;
-
- return topTick +
- (ruler.barHeight / 2) +
- ruler.categorySpacing +
- (ruler.barHeight * stackIndex) +
- (ruler.barSpacing / 2) +
- (ruler.barSpacing * stackIndex);
- }
- });
-};
-
-},{}],16:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- var helpers = Chart.helpers;
-
- Chart.defaults.bubble = {
- hover: {
- mode: 'single'
- },
-
- scales: {
- xAxes: [{
- type: 'linear', // bubble should probably use a linear scale by default
- position: 'bottom',
- id: 'x-axis-0' // need an ID so datasets can reference the scale
- }],
- yAxes: [{
- type: 'linear',
- position: 'left',
- id: 'y-axis-0'
- }]
- },
-
- tooltips: {
- callbacks: {
- title: function() {
- // Title doesn't make sense for scatter since we format the data as a point
- return '';
- },
- label: function(tooltipItem, data) {
- var datasetLabel = data.datasets[tooltipItem.datasetIndex].label || '';
- var dataPoint = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
- return datasetLabel + ': (' + tooltipItem.xLabel + ', ' + tooltipItem.yLabel + ', ' + dataPoint.r + ')';
- }
- }
- }
- };
-
- Chart.controllers.bubble = Chart.DatasetController.extend({
-
- dataElementType: Chart.elements.Point,
-
- update: function(reset) {
- var me = this;
- var meta = me.getMeta();
- var points = meta.data;
-
- // Update Points
- helpers.each(points, function(point, index) {
- me.updateElement(point, index, reset);
- });
- },
-
- updateElement: function(point, index, reset) {
- var me = this;
- var meta = me.getMeta();
- var xScale = me.getScaleForId(meta.xAxisID);
- var yScale = me.getScaleForId(meta.yAxisID);
-
- var custom = point.custom || {};
- var dataset = me.getDataset();
- var data = dataset.data[index];
- var pointElementOptions = me.chart.options.elements.point;
- var dsIndex = me.index;
-
- helpers.extend(point, {
- // Utility
- _xScale: xScale,
- _yScale: yScale,
- _datasetIndex: dsIndex,
- _index: index,
-
- // Desired view properties
- _model: {
- x: reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex, me.chart.isCombo),
- y: reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex),
- // Appearance
- radius: reset ? 0 : custom.radius ? custom.radius : me.getRadius(data),
-
- // Tooltip
- hitRadius: custom.hitRadius ? custom.hitRadius : helpers.getValueAtIndexOrDefault(dataset.hitRadius, index, pointElementOptions.hitRadius)
- }
- });
-
- // Trick to reset the styles of the point
- Chart.DatasetController.prototype.removeHoverStyle.call(me, point, pointElementOptions);
-
- var model = point._model;
- model.skip = custom.skip ? custom.skip : (isNaN(model.x) || isNaN(model.y));
-
- point.pivot();
- },
-
- getRadius: function(value) {
- return value.r || this.chart.options.elements.point.radius;
- },
-
- setHoverStyle: function(point) {
- var me = this;
- Chart.DatasetController.prototype.setHoverStyle.call(me, point);
-
- // Radius
- var dataset = me.chart.data.datasets[point._datasetIndex];
- var index = point._index;
- var custom = point.custom || {};
- var model = point._model;
- model.radius = custom.hoverRadius ? custom.hoverRadius : (helpers.getValueAtIndexOrDefault(dataset.hoverRadius, index, me.chart.options.elements.point.hoverRadius)) + me.getRadius(dataset.data[index]);
- },
-
- removeHoverStyle: function(point) {
- var me = this;
- Chart.DatasetController.prototype.removeHoverStyle.call(me, point, me.chart.options.elements.point);
-
- var dataVal = me.chart.data.datasets[point._datasetIndex].data[point._index];
- var custom = point.custom || {};
- var model = point._model;
-
- model.radius = custom.radius ? custom.radius : me.getRadius(dataVal);
- }
- });
-};
-
-},{}],17:[function(require,module,exports){
-'use strict';
-
-module.exports = function(Chart) {
-
- var helpers = Chart.helpers,
- defaults = Chart.defaults;
-
- defaults.doughnut = {
- animation: {
- // Boolean - Whether we animate the rotation of the Doughnut
- animateRotate: true,
- // Boolean - Whether we animate scaling the Doughnut from the centre
- animateScale: false
- },
- aspectRatio: 1,
- hover: {
- mode: 'single'
- },
- legendCallback: function(chart) {
- var text = [];
- text.push('
');
-
- var data = chart.data;
- var datasets = data.datasets;
- var labels = data.labels;
-
- if (datasets.length) {
- for (var i = 0; i < datasets[0].data.length; ++i) {
- text.push('
You can download the latest version of Chart.js from the GitHub releases or use a Chart.js CDN. Detailed installation instructions can be found on the installation page.
-
Creating a Chart
-
It's easy to get started with Chart.js. All that's required is the script included in your page along with a single <canvas> node to render the chart.
-
In this example, we create a bar chart for a single dataset and render that in our page. You can see all the ways to use Chart.js in the usage documentation