first commit

This commit is contained in:
caiyu 2025-02-22 18:13:51 +08:00
commit 87309628fc
45 changed files with 21800 additions and 0 deletions

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

677
Bandscope.ino Normal file
View File

@ -0,0 +1,677 @@
uint32_t colourTest;
/*
* Initialise variables and SI4432 for the low frequency sweep
*/
void initBandscope()
{
// set up checkerboard sizes
waterfallHeight = WATERFALL_HEIGHT;
gridHeight = SCREEN_HEIGHT - waterfallHeight - 10;
gridWidth = SCREEN_WIDTH;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = 0;
yOrigin = 0;
displayPoints = setting.BandscopePoints;
xDelta = SCREEN_WIDTH / xGrid;
ClearDisplay ();
/*
* Set up the "img" Sprite. This is the image for the graph. It makes for faster display
* updates and less flicker.
*
* 16 bit colour depth is faster than 8 and much faster than 4 bit! BUT - sprites
* pushed to it do not have correct colour - 8 bit and it is fine.
*
* All marker sprites are WHITE for now.
*/
tft.unloadFont();
img.unloadFont();
img.deleteSprite();
img.setTextSize ( 1 );
img.setColorDepth ( 16 );
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
img.createSprite ( 2, gridHeight + 1 ); // Only 2 columns wide
/*
* The "tSprite" is used for displaying the data above the scan grid - we don't use it in this mode
* The "sSprite" is for displaying the sidebar stuff, but reused here for the waterfall
*/
tSprite.deleteSprite();
sSprite.deleteSprite();
sSprite.setColorDepth (16); // we don't need 16bit but its faster
sSprite.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
sSprite.createSprite ( gridWidth, waterfallHeight ); // Full width
sSprite.setScrollRect(0, 0, gridWidth, waterfallHeight, TFT_BLACK);
/*
* Create and draw the sprite for the gain scale
*/
CreateGridScale ();
// Make sure everything will be reset
old_settingAttenuate = -1000;
old_settingPowerGrid = -1000;
old_settingMax = -1;
old_settingMin = -1;
old_startFreq = -1;
old_stopFreq = -1;
old_requiredRBW10 = -1;
old_vbw = -1;
old_settingAverage = -1;
old_settingSpur = -100;
old_bandwidth = 0;
SetRX ( 0 ); // LO to transmit, RX to receive
xmit.SetDrive ( setting.Drive ); // Set transmitter power level
rcvr.SetPreampGain ( setting.PreampGain );
sweepStartDone = false; // Make sure this initialize is only done once per sweep
initSweep = true;
tinySA_mode = BANDSCOPE;
setting.Mode = tinySA_mode;
ResetBandscopeMenuStack(); // Put menu stack back to root level
}
/*
* This function section handles the fast bandscope sweep
* The display is split and shows a waterfall.
* The number of points is reduced, and frequency change is done using an offset to allow the
* delay time between changing frequency and taking a reading to be reduced.
*
* Frequency scheme:
* When the LO frequency is < 480MHz the LO can be adjusted +- 80kHz from the
* nominal frequency in 156.25Hz steps, ie +- 512 steps.
* Actually this is not possible! +-511 steps is
*
*
* If the LO is above 480MHz the the adjustment is +-160kHz in 312.5Hz steps.
* If the IF is 434MHz then 480MHz -> 46Mhz for the signal being analysed, so fine
* for most of the HF bands.
*
* In bandscope mode the RBW is fixed at the minimum 2.6kHz, span at 200kHz and
* there are 80 data points
*
* 200kHz -> 2.5kHz steps between each reading or 16 * 156.25Hz if in low band
*
* Start by setting the LO to the frequency for the start of the sweep plus 80kHz
* and set the offset value at -80kHz.
* At each reading increment the offset value by 16 (8 in high band).
* In this mode the delay time between reading is set at a shorter value than
* normally used by the RBW as the LO does not turn off at each change in offset, unlike
* a normal frequency change.
* When the offset value reaches +80kHz then we need to reset the LO (using normal delaytime)
* and continue until we get to the end of the sweep.
*
* Due to the limitation of not being able to do +-512, or +-16 stesp, we will use +-31 steps per frequency
* jump instead
*
*/
void doBandscope()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static unsigned long bandscopeDelay;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint16_t minRSSI = 255; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
static bool resetAverage; // Flag to indicate a setting has changed and average valuesneeds to be reset
static bool jsonDocInitialised = false;
static uint16_t chunkIndex;
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
/*
* If the "sweepStartDone" flag is false or if the "initSweep" flag is true, we need
* to set things up for the sweep.
*/
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{
sweepPoints = setting.BandscopePoints;
autoSweepFreqStep = ( setting.BandscopeSpan ) / sweepPoints;
offsetFreqIncrement = autoSweepFreqStep; // 2500 Hz for 200kHz span, 80 points per sweep
bandwidth = rcvr.SetRBW ( setting.BandscopeRBW10, &delaytime, &bpfIndex ); // Set it in the receiver Si4432
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( setting.IF_Freq + 1300 ); // Set the RX Si4432 to the IF frequency, offset a bit
// sweepFreqStep = autoSweepFreqStep; // Step for each reading
if ( setting.Attenuate != old_settingAttenuate )
{
if ( !att.SetAtten ( setting.Attenuate )) // Set the internal attenuator
setting.Attenuate = att.GetAtten (); // Read back if limited (setting.Attenuate was outside range)
old_settingAttenuate = setting.Attenuate;
}
// pre-calculate adjustment for RSSI values
dBadjust = (double)setting.Attenuate - 120.0 + setting.LevelOffset - setting.ExternalGain + bpfCalibrations[bpfIndex];
Serial.printf("Bandscope dBadjust = %f; leveloffset = %f; attenuate = %i, ext gain = %f\n",
dBadjust, setting.LevelOffset, setting.Attenuate, setting.ExternalGain);
resetAverage = changedSetting;
maxGrid = setting.BandscopeMaxGrid;
minGrid = setting.BandscopeMinGrid;
#if USE_WIFI
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > setting.BandscopePoints)
wiFiPoints = setting.BandscopePoints;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 )
pushBandscopeSettings ();
#endif // #if USE_WIFI
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
autoSweepFreq = setting.BandscopeStart; // Set the start frequency.
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
resetOffsets();
// set the offset value in the SI4432
xmit.SetOffset(offsetValue);
setFreqMicros = micros(); // Store the time the frequency was changed
// set the LO frequency (offsetFreq is -ve at start!)
uint32_t xmitFreq = setting.IF_Freq + autoSweepFreq - offsetFreq;
// Serial.printf("XmitFreq %i\n", xmitFreq);
xmit.SetFrequency ( xmitFreq );
// delay will vary depending on whether or not the nominal frequency is changed
bandscopeDelay = delaytime; // long delay
#if USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
chunkIndex = 0;
initChunkSweepDoc (autoSweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #if USE_WIFI
// sweepStep = 0;
startFreq = setting.BandscopeStart + setting.IF_Freq; // Start freq for the LO
stopFreq = setting.BandscopeSpan + startFreq; // Stop freq for the LO
// Serial.printf(" start %i; stop %i; points %i \n", startFreq, stopFreq, sweepPoints );
lastMinRSSI = minRSSI;
minRSSI = 255; // real value should always be less
DisplayBandscopeInfo (); // Display axis and other info
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
uint16_t oldSweepStep = autoSweepStep;
uint32_t oldSweepFreq = autoSweepFreq;
/*
* Wait until time to take the next reading. If a long wait then check the touchscreen
* and Websockets while we are waiting to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < bandscopeDelay )
{
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
nowMicros = micros();
}
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Freq: %s - RSSI: %03d\n",
FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += autoSweepFreqStep; // Increment the frequency
autoSweepStep++; // and increment the step count
offsetFreq += offsetFreqIncrement;
offsetValue += offsetIncrement;
/*
* Change the local oscillator frequency for the next reading and record the time for
* the RBW required settling delay.
*/
setFreqMicros = micros(); // Store the time the LO frequency was changed
if (offsetValue >= 512) // reached offset limits
{
resetOffsets();
xmit.SetOffset(offsetValue);
uint32_t f = setting.IF_Freq + autoSweepFreq - offsetFreq;
// Serial.printf("Sweep setFreq %i\n", f);
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
bandscopeDelay = delaytime;
}
else
{
xmit.SetOffset(offsetValue);
bandscopeDelay = offsetDelayTime;
}
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
{
if ( jsonDocInitialised )
{
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
chunkIndex++; // increment no of data points in current WiFi chunk
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
{
String wsBuffer;
if ( wsBuffer )
{
// Serial.print("D");
serializeJson ( jsonDocument, wsBuffer );
// Serial.printf("J%u", wsBuffer.length() );
unsigned long s = millis();
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
if (millis() - s > 1000)
{
Serial.println("webSocketTimeout");
Serial.println(wsBuffer);
numberOfWebsocketClients = 0;
}
// Serial.print("j");
}
else
Serial.println("No buffer :(");
}
}
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
{
chunkIndex = 0;
initChunkSweepDoc (autoSweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
}
#endif // #if USE_WIFI
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
uint16_t pixelsPerPoint = SCREEN_WIDTH / displayPoints;
for (uint16_t i = 0; i< pixelsPerPoint; i++) {
uint16_t tmp = oldSweepStep * pixelsPerPoint + i;
myActual[tmp] = rxRSSI;
myGain[tmp] = gainReading;
DrawCheckerBoard ( tmp ); // Draw the grid
if ( resetAverage || setting.Average == AV_OFF ) // Store data, either as read or as rolling average
myData[tmp] = myActual[oldSweepStep];
else
{
switch ( setting.Average )
{
case AV_MIN:
if ( myData[tmp] > myActual[oldSweepStep] )
myData[tmp] = myActual[oldSweepStep];
break;
case AV_MAX:
if ( myData[tmp] < myActual[oldSweepStep] )
myData[tmp] = myActual[oldSweepStep];
break;
case AV_2:
myData[tmp] = ( myData[tmp] + myActual[oldSweepStep] ) / 2;
break;
case AV_4:
myData[tmp] = ( myData[tmp]*3 + myActual[oldSweepStep] ) / 4;
break;
case AV_8:
myData[tmp] = ( myData[tmp]*7 + myActual[oldSweepStep] ) / 8;
break;
}
DisplayPoint ( myData, tmp, AVG_COLOR );
}
if ( setting.ShowSweep )
DisplayPoint ( myActual, tmp, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, tmp, GAIN_COLOR );
if ( setting.ShowStorage )
DisplayPoint ( myStorage, tmp, STORAGE_COLOR );
// If in the first few points show the scale
if ( ( tmp < 4 * CHAR_WIDTH ) && (tmp > 0) )
{
int16_t scaleX = -tmp + 1; // relative to the img sprite
img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
}
if ( tmp > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin + tmp - 1 , yOrigin );
/*
* put data into the top row of the waterfall
* 16 bit colours have 5 bits for Red, 6 bits for Green, 5 bits for Blue
* We will just change the green level here for first test
*/
uint32_t level = (uint32_t)( (float)(rxRSSI - setting.WaterfallMin) * setting.WaterfallGain) ; // testing colours
if (rxRSSI < setting.WaterfallMin)
level = 0;
uint32_t green = level;
uint32_t red = 0;
uint32_t blue = 0;
if (green > 63)
{
green = 63;
red = level - 63;
if ( red > 31 )
{
red = 31;
blue = level - 63 - 31;
if ( blue > 31 )
blue = 31;
}
}
uint32_t pixelColour = (red << 11) + (green << 5) + blue;
if (colourTest > 0) // delete at some stage
pixelColour = colourTest;
// Serial.printf("rxRSSI %i; red %i; green %i; blue %i; colour %i \n", rxRSSI, red, green, blue, pixelColour);
sSprite.drawPixel ( tmp, 0, pixelColour );
}
// myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
if ( autoSweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
resetAverage = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
if ( myActual[setting.BandscopePoints-1] == 0 ) // Ensure a value in last data point
{
myActual[setting.BandscopePoints-1] = rxRSSI; // Yes, save it
myGain[setting.BandscopePoints-1] = gainReading;
// myFreq[setting.BandscopePoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#if USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{
String wsBuffer;
if (wsBuffer)
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
}
#endif // #if USE_WIFI
// scroll the waterfall down one pixel
sSprite.scroll( 0, 1 );
sSprite.pushSprite( 0, gridHeight + 1 );
} // End of "if ( autoSweepStep >= sweepPoints )"
} // End of "doBandscope"
/*
* "DisplayBandscopeInfo" - Draws the frequency info below the checkerboard. Called
* when a setting is changed to set axis labels
*/
void DisplayBandscopeInfo ()
{
const char *averageText[] = { " OFF", " MIN", " MAX", " 2", " 4", " 8" };
const char *referenceOutText[] = { " 30", " 15", " 10", " 4", " 3", " 2", " 1" };
double fStart;
double fCenter;
double fStop;
// enum { SA_LOW_RANGE, SA_HIGH_RANGE, SIG_GEN_LOW_RANGE, SIG_GEN_HIGH_RANGE, IF_SWEEP, ZERO_SPAN_LOW_RANGE, ZERO_SPAN_HIGH_RANGE, TRACKING_GENERATOR };
tSprite.fillSprite ( BLACK );
tSprite.setTextColor ( WHITE );
/*
* Update frequency labels at bottom if changed
*/
tft.setTextColor ( WHITE,BLACK );
tft.setTextSize ( 1 );
fStart = (double)( setting.BandscopeStart / 1000000.0 ); // Start freq
fCenter = (double)( ( setting.BandscopeStart + setting.BandscopeSpan/2.0 ) / 1000000.0 );
fStop = (double)( (setting.BandscopeStart + setting.BandscopeSpan ) / 1000000.0 ) ; // Stop freq
if ( old_startFreq != fStart || old_stopFreq != fStop )
{
// Serial.printf("DisplayBandscopeInfo fStart %f; old_startFreq %f \n", fStart, old_startFreq);
// Serial.printf("DisplayBandscopeInfo fStop %f; old_stopFreq %f \n", fStop, old_stopFreq);
tft.fillRect ( xOrigin, SCREEN_HEIGHT -
CHAR_HEIGHT, SCREEN_WIDTH - xOrigin - 1, SCREEN_HEIGHT - 1, BLACK );
// Show operating mode
tft.setCursor ( xOrigin + 50, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.setTextColor ( DB_COLOR );
tft.printf ( "Mode:%s", modeText[setting.Mode] );
tft.setTextColor ( WHITE );
tft.setCursor ( xOrigin + 2, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.print ( fStart );
tft.setCursor ( SCREEN_WIDTH - 25, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.print ( fStop );
/*
* Show the center frequency:
*/
tft.setCursor ( SCREEN_WIDTH / 2 - 20 + xOrigin, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.print ( fCenter );
tft.print ( "(MHz)" );
old_startFreq = fStart; // Save current frequency range
old_stopFreq = fStop; // For next time
}
tft.setCursor ( 220, SCREEN_HEIGHT - CHAR_HEIGHT ); // Show sweep time
tft.printf ( "%6ums", sweepMicros / 1000 );
/*
* We use the "tSprite" to paint the data at the top of the screen to avoid
* flicker.
*/
/*
* Show marker values:
*
* The "xPos" and "yPos" arrays are the coordinates of where to place the marker data.
*
* The "posIndex" variable keeps track of the next available position for the marker
* data. If we want fixed positions for each marker, then change the "xPos" and "yPos"
* indicies to use "m".
*/
int xPos[MARKER_COUNT] = { 20, 20, 160, 160 };
int yPos[MARKER_COUNT] = { 0, CHAR_HEIGHT, 0, CHAR_HEIGHT };
int posIndex = 0;
for ( int m = 0; m < MARKER_COUNT; m++ )
{
tSprite.setCursor ( xPos[m], yPos[m] );
if (( marker[m].isEnabled()) && ( setting.ShowSweep || setting.Average != AV_OFF ))
{
tSprite.setTextColor ( WHITE );
tSprite.printf ( "%u:%5.1fdBm %8.4fMHz", marker[m].Index()+1,
rssiTodBm ( oldPeaks[m].Level ), oldPeaks[m].Freq / 1000000.0 );
}
else
{
tSprite.setTextColor ( DARKGREY );
tSprite.printf ( "%u:", marker[m].Index()+1 );
}
posIndex++;
}
int x = tSprite.width () - 45;
tSprite.setTextColor ( WHITE );
tSprite.pushSprite ( xOrigin, 0 ); // Write sprite to the display
updateSidebar = false;
} // End of "DisplayBandscopeInfo"
void resetOffsets ()
{
if (setting.BandscopeStart < 480000000) // low range. Assume never change range mid sweep!
{
offsetStep = -31;
offsetIncrement = 16; // 16 * 156.25 = 2500
}
else // high range
{
offsetStep = -63;
offsetIncrement = 8; // 8 * 312.5 = 2500
}
if (setting.BandscopeSpan == 400000) // wider span, same no of points
offsetIncrement = offsetIncrement * 2;
offsetFreq = offsetStep * offsetFreqIncrement;
offsetValue = offsetStep * offsetIncrement;
}

495
IFsweep.ino Normal file
View File

@ -0,0 +1,495 @@
/*
* IF sweep sweeps the IF Frequency from a start value to an stop value to
* measure the SAW and RX low pass filter response.
* It requires a fixed strength and frequency signal, and this is provided
* by setting the reference output to 30MHz.
* The input should be connected to the reference signal output with an external cable
*/
void initIF_Sweep()
{
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep();
tinySA_mode = IF_SWEEP;
setting.Mode = tinySA_mode;
ResetIFsweepMenuStack(); // Put menu stack back to root level
}
void doIF_Sweep()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint16_t currentPointRSSI;
static uint16_t peakRSSI;
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
static bool jsonDocInitialised = false;
static uint16_t chunkIndex;
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to work out some basic things
{
Serial.println("Init IFSweep or changedSetting");
autoSweepFreqStep = ( stopFreq_IF - startFreq_IF ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
bandwidth = rcvr.SetRBW ( 106.0, &delaytime, &bpfIndex ); // Set it in the receiver Si4432. delaytime is returned
sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( stopFreq_IF - startFreq_IF ) / sweepPoints; // Step for each reading
att.SetAtten ( 0 ); // Set the internal attenuator
// pre-calculate adjustment for RSSI values
dBadjust = -120.0 + setting.LevelOffset - setting.ExternalGain + bpfCalibrations[bpfIndex] ;
Serial.printf("IFSweep dBadjust = %f; leveloffset = %f; attenuate = %i, ext gain = %f\n",
dBadjust, setting.LevelOffset, setting.Attenuate, setting.ExternalGain);
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#if USE_WIFI
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
pushIFSweepSettings();
#endif // #if USE_WIFI
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
sweepStep = 0;
autoSweepFreq = startFreq_IF; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the IF frequency
setFreqMicros = micros(); // Store the time the frequency was changed
xmit.SetFrequency ( sigFreq_IF + autoSweepFreq ); // set the LO frequency to the IF plus 30Mhz ref out
// Serial.printf("autoSweepFreq init: %u\n", autoSweepFreq);
#if USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #if USE_WIFI
startFreq = startFreq_IF + sigFreq_IF; // Start freq for the LO
stopFreq = stopFreq_IF + sigFreq_IF; // Stop freq for the LO
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
uint16_t oldSweepStep = autoSweepStep;
uint32_t oldSweepFreq = autoSweepFreq;
/*
* Wait until time to take the next reading. If a long wait then check the touchscreen
* and Websockets while we are waiting to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime )
{
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
// webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
// Serial.println("w");
nowMicros = micros();
}
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Freq: %s - RSSI: %03d\n",
FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
// Serial.printf("autoSweepFreq: %u Step: %u\n", autoSweepFreq, sweepStep);
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*/
uint32_t f = sigFreq_IF + autoSweepFreq;
setFreqMicros = micros(); // Store the time the LO frequency was changed
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the IF frequency
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.GetFrequency());
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
{
if ( jsonDocInitialised )
{
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
// Serial.printf ( "Add point chunkIndex %u, sweepStep %u of %u \n", chunkIndex, sweepStep, sweepPoints);
chunkIndex++; // increment no of data points in current WiFi chunk
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
{
String wsBuffer;
if ( wsBuffer )
{
// Serial.print("D");
serializeJson ( jsonDocument, wsBuffer );
// Serial.printf("J%u", wsBuffer.length() );
unsigned long s = millis();
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
if (millis() - s > 1000)
{
Serial.println("webSocketTimeout");
Serial.println(wsBuffer);
numberOfWebsocketClients = 0;
}
// Serial.print("j");
}
else
Serial.println("No buffer :(");
}
}
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
}
#endif // #if USE_WIFI
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximim
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read
DisplayPoint ( myActual, oldSweepStep, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR );
/*
* Record the peak values
*/
if ( oldSweepStep > 0)
{
if ( peakLevel < myActual[oldSweepStep] )
{
peakIndex = oldSweepStep;
peakLevel = myActual[oldSweepStep];
peakFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myActual[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
} // if (( autoSweepFreq > 1000000 ) && (oldSweepStep > 0))
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
for ( int p = 0; p < MARKER_COUNT; p++ )
{
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
}
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
// myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
// myFreq[displayPoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#if USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{
String wsBuffer;
if (wsBuffer)
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
}
#endif // #if USE_WIFI
} // End of "if ( sweepStep >= sweepPoints )"
}

674
LICENSE Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

124
NS2009.cpp Normal file
View File

@ -0,0 +1,124 @@
#include <Wire.h>
#include <Arduino.h>
#include "NS2009.h"
int Map_Data (int Data, int InMin, int InMax, int OutMin, int OutMax)
{
if (Data < InMin)
Data = InMin;
if (Data > InMax)
Data = InMax;
return ((Data-InMin)*(OutMax-OutMin))/(InMax-InMin)+OutMin;
}
NS2009::NS2009 (void)
{
Address = DEFAULT_NS2009_ADDR;
FlipX = false;
FlipY = false;
}
NS2009::NS2009 (unsigned char _Address)
{
Address = _Address;
FlipX = false;
FlipY = false;
}
NS2009::NS2009 (bool _FlipX, bool _FlipY)
{
Address = DEFAULT_NS2009_ADDR;
FlipX = _FlipX;
FlipY = _FlipY;
}
NS2009::NS2009 (unsigned char _Address, bool _FlipX, bool _FlipY)
{
Address = _Address;
FlipX = _FlipX;
FlipY = _FlipY;
}
unsigned int NS2009::ReadRegister (unsigned char Command)
{
unsigned char ReadData[2], i=0;
Wire.beginTransmission(Address);
Wire.write(&Command, 1);
Wire.requestFrom(Address, 2);
while (Wire.available())
ReadData[i++] = Wire.read();
return (ReadData[0] << 4) | (ReadData[1] >> 4);
delay(10);
}
void NS2009::Calibrate ()
{
int P1X, P1Y, P2X, P2Y;
Serial.println ("Touch corner of the screen\n\r");
ScanBlocking ();
P1X = RawX;
P1Y = RawY;
Serial.println ("Touch registered! Touch an opposite corner!\n\r");
while (CheckTouched ());
// opposite corner
ScanBlocking ();
P2X = RawX;
P2Y = RawY;
Serial.println ("Second touch registered!\n\r");
while (CheckTouched ());
MinX = MIN(P1X, P2X);
MaxX = MAX(P1X, P2X);
MinY = MIN(P1Y, P2Y);
MaxY = MAX(P1Y, P2Y);
}
void NS2009::Calibrate (int _MinX, int _MaxX, int _MinY, int _MaxY)
{
MinX = _MinX;
MaxX = _MaxX;
MinY = _MinY;
MaxY = _MaxY;
}
bool NS2009::CheckTouched ()
{
do
{
RawZ = ReadRegister(NS2009_READ_Z);
}
while (RawZ == 0xFFF); // sometimes the I2C reading gives a false positive by returning only ones ==> 0xFFF = 4095
Touched = (RawZ > THRESHOLD_Z);
return Touched;
}
void NS2009::Scan ()
{
CheckTouched ();
RawX = ReadRegister(NS2009_READ_X);
X = Map_Data (RawX, MinX, MaxX, 0, SCREEN_SIZE_X);
RawY = ReadRegister(NS2009_READ_Y);
Y = Map_Data (RawY, MinY, MaxY, 0, SCREEN_SIZE_Y);
if (FlipX)
X = SCREEN_SIZE_X - X;
if (FlipY)
Y = SCREEN_SIZE_Y - Y;
}
void NS2009::ScanBlocking ()
{
do
{
CheckTouched ();
}
while (!Touched);
RawX = ReadRegister(NS2009_READ_X);
X = Map_Data (RawX, MinX, MaxX, 0, SCREEN_SIZE_X);
RawY = ReadRegister(NS2009_READ_Y);
Y = Map_Data (RawY, MinY, MaxY, 0, SCREEN_SIZE_Y);
if (FlipX)
X = SCREEN_SIZE_X - X;
if (FlipY)
Y = SCREEN_SIZE_Y - Y;
}

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# simpleSA
Simple Spectrum Analyser, based on 2 off SI4432 modules, an ADE25 mixer,
a programmable attenuator and some filters.
This version is intended for homebrew by those experienced in amateur radio or
similar techniques, or those wishing to explore these and learn the hard way!
No support is available for the code or the hardware.
The code was initially based on Arduino code for STM "Blue Pill" developed by
Erik PD0EK and ported to run on an ESP32 by Dave M0WID.
Subsequently the code has been extensively reorganised, modified and developed,
with much of the work done by John WA2FZW.
Additional features have been added, including the ability to view the
trace and change settings from web clients, and later additional modes
such as signal generator, IFSweep and the ability to control a tracking
generator.
Glenn VK3PE has developed some boards for this version - see his website.
Erik has since gone on to produce a commercial version called TinySA.
The commercial version has quite a lot of additional features, but does not
include the wifi features of this version, or the integrated tracking
generator.
Dave M0WID

551
RXsweep.ino Normal file
View File

@ -0,0 +1,551 @@
/*
* RX sweep fixes the LO and sweeps the receiver from a start value to an stop value to
* measure the RX FIR bandpass pass filter response. This eliminates any effect from the SAW
* and low pass filters before the receiver.
* It requires a fixed strength and frequency signal, and this is provided
* by setting the reference output to say 15MHz. Avoid 30Mhz due to some stray clock signal
* The input should be connected to the reference signal output with an external cable
*/
void initRX_Sweep()
{
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep();
SetRXsweepSpan (setting.Bandwidth10 * 1000); // 10 * bandwidth
tinySA_mode = RX_SWEEP;
setting.Mode = tinySA_mode;
ResetRXsweepMenuStack(); // Put menu stack back to root level
}
void doRX_Sweep()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static uint16_t currentRBW10;
static uint16_t bpFilterIndex; // used to loop through the SI4432 bandpass filters when calibrating
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint16_t currentPointRSSI;
static uint16_t peakRSSI;
static uint16_t centreRSSI; // RSSI at centre of sweep (ie at IF)
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
static bool jsonDocInitialised = false;
static uint16_t chunkIndex;
static uint16_t bpfCalFirstSweepDone;
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting || (bpfCalibrate && !sweepStartDone) ) // Something has changed, or a first start, so need to work out some basic things
{
//Serial.println("Init RXSweep or changedSetting");
if (bpfCalibrate)
{
setting.LevelOffset = 0;
if (bpfCalFirstSweepDone)
{
// max value for this bpFilter index (previous sweep) is in peaks[0].Level
// value at the IF is in myActual[displayPoints/2]
// use the average of the two values
uint16_t rssi_average = ( peaks[0].Level + myActual[displayPoints/2] ) / 2;
bpfCalibrations[bpFilterIndex] = CAL_POWER - rssiTodBm( rssi_average );
Serial.printf("bpfCalibration: filter: %i, rbw10=%i , cal=%f, rssi av = %i\n",
bpFilterIndex, currentRBW10, bpfCalibrations[bpFilterIndex], rssi_average);
bpFilterIndex ++;
if (bpFilterIndex >= bpfCount) // end of calibration
{
bpfCalibrate = false;
currentRBW10 = setting.Bandwidth10;
SaveBpfCalibration();
WriteSettings();
Serial.println("bpfCalibration done");
}
else // not reached end of filters
{
currentRBW10 = rcvr.GetBandpassFilter10(bpFilterIndex);
}
}
else // first sweep not done
{
bpfCalFirstSweepDone = true;
currentRBW10 = rcvr.GetBandpassFilter10(bpFilterIndex);
}
}
else // not in calibrate
{
currentRBW10 = setting.Bandwidth10;
bpFilterIndex = 0;
bpfCalFirstSweepDone = false;
}
SetRXsweepSpan (currentRBW10 * 1000); // 10 * bandwidth - sets up stopFreq_RX and startFreq_RX
autoSweepFreqStep = ( stopFreq_RX - startFreq_RX ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
/*
* Use the RBW setting. If zero then it is meaningless, use smallest instead
*/
bandwidth = rcvr.SetRBW ( currentRBW10, &delaytime, &bpfIndex ); // Set it in the receiver Si4432. delaytime is returned
sweepPoints = displayPoints;
sweepFreqStep = ( stopFreq_RX - startFreq_RX ) / sweepPoints; // Step for each reading
att.SetAtten ( 0 ); // Set the internal attenuator
// pre-calculate adjustment for RSSI values
dBadjust = -120.0 - setting.ExternalGain; // No Level Offset correction applied for this mode
//Serial.printf("RXSweep dBadjust = %f; ext gain = %f\n", dBadjust, setting.ExternalGain);
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#if USE_WIFI
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
pushRXSweepSettings();
#endif // #if USE_WIFI
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
sweepStep = 0;
autoSweepFreq = startFreq_RX; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the start of the sweep
setFreqMicros = micros(); // Store the time the frequency was changed
xmit.SetFrequency ( sigFreq_RX + setting.IF_Freq ); // set the LO frequency to the IF plus reference
// Serial.printf("autoSweepFreq init: %u\n", autoSweepFreq);
#if USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #if USE_WIFI
startFreq = startFreq_RX; // Start freq for the RX
stopFreq = stopFreq_RX; // Stop freq for the RX
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
uint16_t oldSweepStep = autoSweepStep;
uint32_t oldSweepFreq = autoSweepFreq;
/*
* Wait until time to take the next reading. If a long wait then check the touchscreen
* and Websockets while we are waiting to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime )
{
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
// webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
// Serial.println("w");
nowMicros = micros();
}
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Freq: %s - RSSI: %03d\n",
FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
// Serial.printf("autoSweepFreq: %u Step: %u\n", autoSweepFreq, sweepStep);
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*
* Not we also set the lo here to simulate the effect on the filters in a real sweep
*/
setFreqMicros = micros(); // Store the time the LO frequency was changed
xmit.SetFrequency ( sigFreq_RX + setting.IF_Freq ); // set the LO frequency to the IF plus reference
rcvr.SetFrequency ( autoSweepFreq ); // Set the RX Si4432 to the IF frequency
// Serial.printf("Step: %i Required: %i Actual %i\n", sweepStep, autoSweepFreq, rcvr.GetFrequency());
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
{
if ( jsonDocInitialised )
{
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
// Serial.printf ( "Add point chunkIndex %u, sweepStep %u of %u \n", chunkIndex, sweepStep, sweepPoints);
chunkIndex++; // increment no of data points in current WiFi chunk
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
{
String wsBuffer;
if ( wsBuffer )
{
// Serial.print("D");
serializeJson ( jsonDocument, wsBuffer );
// Serial.printf("J%u", wsBuffer.length() );
unsigned long s = millis();
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
if (millis() - s > 1000)
{
Serial.println("webSocketTimeout");
Serial.println(wsBuffer);
numberOfWebsocketClients = 0;
}
// Serial.print("j");
}
else
Serial.println("No buffer :(");
}
}
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
}
#endif // #if USE_WIFI
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximum
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read
DisplayPoint ( myActual, oldSweepStep, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR );
/*
* Record the peak values
*/
if ( oldSweepStep > 0)
{
if ( peakLevel < myActual[oldSweepStep] )
{
peakIndex = oldSweepStep;
peakLevel = myActual[oldSweepStep];
peakFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myActual[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
} // if (( autoSweepFreq > 1000000 ) && (oldSweepStep > 0))
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
for ( int p = 0; p < MARKER_COUNT; p++ )
{
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
}
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
// myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
oldPeakLevel = peakLevel; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
// myFreq[displayPoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#if USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{
String wsBuffer;
if (wsBuffer)
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
}
#endif // #if USE_WIFI
} // End of "if ( sweepStep >= sweepPoints )"
}

334
SigLo.ino Normal file
View File

@ -0,0 +1,334 @@
/*
* ########################################################################
*
* Initialise variables and SI4432 for sig gen mode
*
* ########################################################################
*/
void initSigLow()
{
// Use the TFT_eSPI buttons for now.
// This could be changed to use something similar to the main menu
tft.fillScreen(SIG_BACKGROUND_COLOR);
//img.unloadFont(); // Free up memory from any previous incarnation of img
img.deleteSprite();
img.setColorDepth ( 16 );
img.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
img.createSprite ( 320, 55 ); // used for frequency display
img.loadFont(SA_FONT_LARGE);
SetRX ( 1 ); // LO and RX both in receive until turned on by UI
xmit.SetOffset ( 0 ); // make sure frequency offset registers are zero
rcvr.SetOffset ( 0 );
int showUpDownButtons = 0;
#ifdef SHOW_FREQ_UP_DOWN_BUTTONS
showUpDownButtons = 1;
#endif
xmit.SetDrive ( sigGenSetting.LO_Drive ); // Set Local Oscillator power level
rcvr.SetDrive ( sigGenSetting.RX_Drive ); // Set receiver SI4432 power level
tinySA_mode = SIG_GEN_LOW_RANGE;
setting.Mode = tinySA_mode;
tft.unloadFont();
tft.setCursor ( xOrigin + 50, SCREEN_HEIGHT - CHAR_HEIGHT );
tft.setTextSize(1);
tft.setTextColor ( YELLOW );
tft.printf ( "Mode:%s", modeText[setting.Mode] );
tft.setTextColor ( WHITE );
tft.loadFont(KEY_FONT);
// draw the buttons
for (int i = 0; i < SIG_KEY_COUNT; i++)
{
if ( showUpDownButtons || ( i > 13 ))
{
key[i].initButton(&tft,
// x, y, w, h, outline, fill, text
sig_keys[i].x,
sig_keys[i].y,
sig_keys[i].width,
sig_keys[i].height,
DARKGREY, // outline colour
sig_keys[i].color, // fill
TFT_BLACK, // Text colour
"", // 10 Byte Label
2); // font size multiplier (not used when font loaded)
// setLabelDatum(uint16_t x_delta, uint16_t y_delta, uint8_t datum)
key[i].setLabelDatum(1, 1, MC_DATUM);
// Draw button and specify label string
// Specifying label string here will allow more than the default 10 byte label
key[i].drawButton(false, sig_keys[i].text);
}
}
// draw the slider to control output level
// we re-purpose the sSprite for this
sSprite.deleteSprite();
sSprite.setColorDepth ( 16 );
sSprite.setAttribute ( PSRAM_ENABLE, false ); // Don't use the PSRAM on the WROVERs
sSprite.createSprite ( SLIDER_WIDTH + 2 * SLIDER_KNOB_RADIUS + 60, 2 * SLIDER_KNOB_RADIUS ); // used for slider and value
sSprite.setTextColor(TFT_ORANGE);
sSprite.loadFont(KEY_FONT);
// Slider range will be something like -60 to -10dBm for low frequency range
// (to be changed once I have worked out what the real values should be)
// Parameter passed in are x, y and slider knob position in %
float sPercent = (float)(sigGenSetting.Power - sigGenSetting.Calibration + ATTENUATOR_RANGE) * 100.0
/ (float)(ATTENUATOR_RANGE);
drawSlider(SLIDER_X, SLIDER_Y, sPercent, sigGenSetting.Power, "dBm");
att.SetAtten ( sigGenSetting.Calibration - sigGenSetting.Power ); // set attenuator to give required output
oldFreq = 0; // Force write of frequency on first loop
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
pushSigGenSettings ();
#endif
}
/* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
* Low frequency range signal generator
* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
*/
void doSigGenLow ()
{
static uint32_t oldIF; // to store the current IF
static uint16_t oldSigGenOutputOn;
float p; // temporary variable for slider percent
static uint16_t sliderRange = ATTENUATOR_RANGE + RX_SI4432_MAX_DRIVE * 3;
uint16_t t_x = 0, t_y = 0; // To store the touch coordinates
int showUpDownButtons = 0;
#ifdef SHOW_FREQ_UP_DOWN_BUTTONS
showUpDownButtons = 1;
#endif
if (changedSetting) {
// calculate required drive and attenuator settings
// keep drive as high as possible so nasties are attenuated
uint16_t sgRXDrive = RX_SI4432_MAX_DRIVE;
uint16_t attenuation = sigGenSetting.Calibration - sigGenSetting.Power;
if (attenuation > ATTENUATOR_RANGE )
{
int16_t diff = attenuation - ATTENUATOR_RANGE;
sgRXDrive = RX_SI4432_MAX_DRIVE - (int16_t)( (diff-1)/3 ) - 1 ;
attenuation = ATTENUATOR_RANGE - 2 + (diff-1)%3;
}
SetSGRxDrive(sgRXDrive);
att.SetAtten(attenuation);
Serial.printf("sigGenLow - rxDrive set to %i, attenuation set to %i, cal is %i\n", sgRXDrive, attenuation, sigGenSetting.Calibration);
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
pushSigGenSettings ();
#endif
changedSetting = false;
}
// Get current touch state and coordinates
boolean pressed = tft.getTouch(&t_x, &t_y); // Just uses standard TFT_eSPI function as not bothered about speed
// Adjust press state of each key appropriately
for (uint8_t b = 0; b < SIG_KEY_COUNT; b++) {
if (pressed && key[b].contains(t_x, t_y))
key[b].press(true); // tell the button it is pressed
else
key[b].press(false); // tell the button it is NOT pressed
}
// Check if any key has changed state
for (uint8_t b = 0; b < SIG_KEY_COUNT; b++)
{
if ( showUpDownButtons || ( b > 13 ))
{
if ( key[b].justPressed() ) {
switch (b) {
case 0: // Increment buttons
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
incrementFreq( pow(10, 8-b) );
changedSetting=true;
break;
case 7: // Decrement buttons
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
decrementFreq( pow(10, 15-b) );
changedSetting=true;
break;
case 14: // Return to SAlo mode
key[b].drawButton(true, sig_keys[b].activeText);
break;
case 15: // toggle the output on/off
sigGenOutputOn = !sigGenOutputOn;
break;
case 16: // launch menu
key[b].drawButton(true, sig_keys[b].activeText);
break;
default:
Serial.printf("Button %i press not handled", b );
break;
}
}
}
if (key[b].isPressed()) { // button held
return;
}
// // If button was just released
if (key[b].justReleased())
{
switch (b) {
case 14: // Return to SAlo mode
WriteSigGenSettings ();
initSweepLow();
break;
case 16: // launch signal generator menu
StartSigGenMenu ( );
return;
case 17: // launch frequency keypad
StartSigGenFreq ( );
return;
}
}
// check if state changed - can change from command or from wifi
switch (b) {
case 15: // On/Off button
if (oldSigGenOutputOn != sigGenOutputOn)
{
if (sigGenOutputOn) {
SetRX(3); // both LO and RX in transmit. Output levels have been set in the init function
key[b].drawButton(true, sig_keys[b].activeText);
tft.setCursor(sig_keys[b].x + 30, sig_keys[b].y);
tft.fillRect(sig_keys[b].x + 30, sig_keys[b].y, sig_keys[b].x + 60, sig_keys[b].y + 10, SIG_BACKGROUND_COLOR);
tft.setTextColor(TFT_GREEN, SIG_BACKGROUND_COLOR );
tft.print(" ON");
} else {
SetRX(1); // Both in receive
key[b].drawButton(false, sig_keys[b].text);
tft.setCursor(sig_keys[b].x + 30, sig_keys[b].y);
tft.fillRect(sig_keys[b].x + 30, sig_keys[b].y, sig_keys[b].x + 60, sig_keys[b].y + 10, SIG_BACKGROUND_COLOR);
tft.setTextColor(TFT_WHITE, SIG_BACKGROUND_COLOR );
tft.print("OFF");
}
changedSetting = true;
}
break;
} // on of state change switch
} // end of keys loop
// Check if slider touched
if ( sliderPressed( pressed, t_x, t_y) )
{
p = sliderPercent( t_x ); // position of slider in %
float pwr = p * (sliderRange)/100.0 + sigGenSetting.Calibration - sliderRange;
drawSlider ( SLIDER_X, SLIDER_Y, p, pwr, "dBm" );
sigGenSetting.Power = pwr;
changedSetting = true;
} else {
p = ( sigGenSetting.Power - (sigGenSetting.Calibration - sliderRange) ) * 100.0 / sliderRange;
drawSlider ( SLIDER_X, SLIDER_Y, p, sigGenSetting.Power, "dBm" );
}
// draw frequency. Uses a sprite to avoid flicker
img.fillSprite(SIG_BACKGROUND_COLOR);
img.setCursor(5,5);
img.setTextColor(TFT_ORANGE);
img.printf("%s",DisplayFrequency ( sigGenSetting.Frequency ) );
img.pushSprite(0,80);
/*
* set RX to IF_Frequency and LO to IF plus required frequency
*
* but only if value has changed to avoid the SI4432 running its state change sequencer
* The mixer will produce IF + LO and IF - LO
* The Low pass filter will filter out the higher frequency and LO leakage
* The IF SAW filter will smooth out the waveform produced by the SI4432
*/
if ( (oldFreq != sigGenSetting.Frequency) || (oldIF != setting.IF_Freq) )
{
rcvr.SetFrequency ( setting.IF_Freq );
xmit.SetFrequency ( setting.IF_Freq + sigGenSetting.Frequency );
Serial.println("set frequency");
if (sigGenOutputOn)
{
delayMicroseconds(300);
SetRX(3); // both LO and RX in tx mode
}
changedSetting = true;
}
oldFreq = sigGenSetting.Frequency;
oldIF = setting.IF_Freq;
oldSigGenOutputOn = sigGenOutputOn;
}
void incrementFreq(uint32_t amount) {
sigGenSetting.Frequency += amount;
if (sigGenSetting.Frequency > MAX_SIGLO_FREQ)
sigGenSetting.Frequency = MAX_SIGLO_FREQ;
}
void decrementFreq(uint32_t amount) {
if (sigGenSetting.Frequency > amount) {
sigGenSetting.Frequency -= amount;
if (sigGenSetting.Frequency < MIN_SIGLO_FREQ)
sigGenSetting.Frequency = MIN_SIGLO_FREQ;
} else {
sigGenSetting.Frequency = MIN_SIGLO_FREQ;
}
}

394
SweepHi.ino Normal file
View File

@ -0,0 +1,394 @@
// Version: 1.0
#ifdef HI_RANGE
/*
* Initialise variables and SI4432 for the high frequency sweep
*/
void initSweepHigh()
{
#ifdef RF_SWITCH
digitalWrite (RF_SWITCH, RF_SWITCH_HIGH_RANGE); // LO RF Switch to mixer
#endif
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
scaleX = displayPoints - 2 * CHAR_WIDTH;
xDelta = displayPoints / xGrid;
startFreqMinLimit = START_MIN_HIGH;
stopFreqMaxLimit = STOP_MAX_HIGH;
simpleSA_mode = SA_HIGH_RANGE;
setting.Mode = simpleSA_mode;
setSettingChangeMillis();
init_sweep();
SetRefOutput ( -1 ); // Turn off the ref output
ResetSAMenuStack(); // Put menu stack back to root level
quickSelectionSelected = false;
validateQuickSelect ( 1 ); // make sure quick select is valid
resetMarkers();
// Serial.printf("InitSweepHigh Done ");
// Serial.printf("lo reg 2= %x\n", xmit.ReadByte(2));
}
/*
* This function section handles the high freq range sweep
*/
void doSweepHigh()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long nowMicros;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint32_t actualFreq; // actual frequency
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
/*
* If the "sweepStartDone" flag is false or if the "initSweep" flag is true, we need
* to set things up for the sweep.
*/
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{
//Serial.println("InitSweep or changedSetting");
autoSweepFreqStep = ( settingHigh.ScanStop - settingHigh.ScanStart ) / displayPoints; // MHz
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution (kHz)
requiredRBW10 = settingHigh.Bandwidth10; // and the resolution bandwidth (kHz * 10)
if ( requiredRBW10 == 0 ) // If the bandwidth is on "Auto" work out the required RBW
requiredRBW10 = (( settingHigh.ScanStop - settingHigh.ScanStart )) / 29000; // 290 points on display, kHz
if ( requiredRBW10 < 26 ) // If it's less than 2.6KHz
requiredRBW10 = 26; // set it to 2.6KHz
if ( requiredRBW10 > 6207 )
requiredRBW10 = 6207;
if ( requiredRBW10 != old_requiredRBW10 )
{
bandwidth = xmit.SetRBW ( requiredRBW10, &delaytime, &bpfIndex ); // Set it in the receiver Si4432
old_requiredRBW10 = requiredRBW10;
}
/*
* Need multiple readings for each pixel in the display to avoid missing signals.
* Work out how many points needed for the whole sweep:
*/
sweepPoints = (uint32_t)(( settingHigh.ScanStop - settingHigh.ScanStart ) / bandwidth / 1000.0 * OVERLAP + 0.5); // allow for some overlap (filters will have 3dB roll off at edge) and round up
if ( sweepPoints < displayPoints )
sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( settingHigh.ScanStop - settingHigh.ScanStart ) / sweepPoints; // Step for each reading
// pre-calculate adjustment for RSSI values
dBadjust = - 120.0 + settingHigh.LevelOffset - settingHigh.ExternalGain + bpfCalibrations[bpfIndex];
// Serial.printf("SweepHi dBadjust = %f; leveloffset = %f; ext gain = %f, bpfCal = %f\n",
// dBadjust, settingHigh.LevelOffset, settingHigh.ExternalGain, bpfCalibrations[bpfIndex]);
resetAverage = changedSetting;
maxGrid = settingHigh.MaxGrid;
minGrid = settingHigh.MinGrid;
#if ( USE_WIFI )
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
// Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 )
pushSettings ();
#endif // #if ( USE_WIFI )
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
sweepStep = 0;
autoSweepFreq = settingHigh.ScanStart; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
xmit.SetFrequency ( autoSweepFreq ); // set the LO frequency
setFreqMicros = micros(); // Store the time the frequency was changed
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency(); // Used for next RSSI command and JSON entry
#if ( USE_WIFI )
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
initChunkSweepDoc (sweepStep);
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #if ( USE_WIFI )
startFreq = settingHigh.ScanStart; // Start freq for the LO
stopFreq = settingHigh.ScanStop; // Stop freq for the LO
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
/*
* If an adjustment of the level is requested to obtain correct dBm
*/
if ( setActualPowerRequested )
{
SetPowerLevel ( actualPower );
setActualPowerRequested = false;
// Serial.printf ( "Setting actual Power %f \n", actualPower );
}
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
marker[i].SetValue(peaks[i].Index, peaks[i].Freq, peaks[i].Level);
peaks[i].Level = 0;
}
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
maxRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
oldSweepStep = autoSweepStep;
oldSweepFreq = actualFreq;
/*
* Wait until time to take the next reading. If a long enough wait left
* then check the touchscreen and Websockets while we are waiting
* to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime )
{
#if ( USE_WIFI )
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
#endif
UiProcess(); // Check the touch screen and encoder
if ( ui_mode != UI_NORMAL )
return; // avoid risk of drawing vertical stripe over menu during remainder of scan
// Serial.println("w");
nowMicros = micros();
}
rxRSSI = xmit.GetRSSI (); // Read the RSSI from the LO SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( " LO Freq: %s", FormatFrequency ( xmit.GetFrequency() ) );
Serial.printf ( " Sweep Freq: %s (%u)", FormatFrequency ( autoSweepFreq), autoSweepFreq );
Serial.printf ( " Actual Freq %s - RSSI: %03d\n",
FormatFrequency ( actualFreq ), rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (settingHigh.ShowGain) )
gainReading = GetPreampGainHigh ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*/
xmit.SetFrequency ( autoSweepFreq ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("LO Required: %i Actual %i\n", f, xmit.GetFrequency());
setFreqMicros = micros(); // Store the time the LO frequency was changed
#if ( USE_WIFI )
addJsonDataPoint ();
#endif
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency(); // Used for next RSSI command and JSON entry
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximim
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for the markers
DrawTraces ( oldSweepStep ); // Plot the data points (pass in freq of zero so no low pass filter compensation)
detectPeaks();
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for the markers
if ( settingHigh.SubtractStorage )
rxRSSI = 128 + rxRSSI - myStorage[oldSweepStep];
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
resetAverage = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
// Serial.printf("MaxRSSI = %i, freq = %i\n", maxRSSI, maxFreq);
oldPeakLevel = maxRSSI; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
myFreq[displayPoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#if ( USE_WIFI )
sendJsonChunk();
#endif
} // End of "if ( sweepStep >= sweepPoints )"
} // End of "doSweepHigh"
#endif // #ifdef HI_RANGE

736
SweepLo.ino Normal file
View File

@ -0,0 +1,736 @@
/*
* Initialise variables and SI4432 for the low frequency sweep
*/
void initSweepLow()
{
// set up checkerboard sizes
gridHeight = GRID_HEIGHT;
gridWidth = DISPLAY_POINTS;
yGrid = Y_GRID; // no of grid divisions
yDelta = gridHeight / yGrid; // no of points/division
xGrid = X_GRID;
xOrigin = X_ORIGIN;
yOrigin = Y_ORIGIN;
displayPoints = DISPLAY_POINTS;
xDelta = displayPoints / xGrid;
init_sweep();
tinySA_mode = SA_LOW_RANGE;
setting.Mode = tinySA_mode;
ResetSAMenuStack(); // Put menu stack back to root level
}
/*
* This function section handles the low freq range sweep
*/
void doSweepLow()
{
static uint32_t autoSweepStep = 0;
static uint32_t autoSweepFreq = 0;
static uint32_t autoSweepFreqStep = 0;
static uint32_t nextPointFreq = 0; // Frequency for the next display point. Used for substeps
static unsigned long setFreqMicros;
static unsigned long nowMicros;
static uint32_t sweepStep; // Step count
static uint32_t sweepFreqStep;
static int16_t pointMinGain; // to record minimum gain for the current display point
static int16_t pointMaxRSSI; // to record max RSSI of the samples in the current display point
static uint32_t pointMaxFreq; // record frequency where maximum occurred
static int16_t lastMode; // Record last operating mode (sig gen, normal)
static uint32_t lastIF;
static bool spurToggle;
static uint32_t actualFreq; // actual frequency
static uint32_t actualIF; // actual IF (Rx frequency)
static uint16_t currentPointRSSI;
static uint16_t peakRSSI;
static uint16_t prevPointRSSI;
static uint32_t peakFreq;
static uint16_t peakIndex;
static uint16_t maxRSSI;
static uint32_t maxFreq;
static uint16_t maxIndex;
static uint16_t pointsPastPeak;
static uint16_t pointsPastDip;
static uint16_t minRSSI; // Minimum level for the sweep
static uint16_t lastMinRSSI; // Minimum level for the previous sweep
static bool resetAverage; // Flag to indicate a setting has changed and average valuesneeds to be reset
static bool jsonDocInitialised = false;
static uint16_t chunkIndex;
static uint32_t offsetIF; // IF frequency offset by half the bandwidth to position in the centre of the filter
static uint32_t tgIF; // Track gen IF - SA IF plus any offset if both SI4432 defined
#ifdef RF_SWITCH
digitalWrite (RF_SWITCH, 1); // LO RF Switch to mixer
#endif
/*
* If paused and at the start of a sweep then do nothing
*/
if (!sweepStartDone && paused)
return;
/*
* If the "sweepStartDone" flag is false or if the "initSweep" flag is true, we need
* to set things up for the sweep.
*/
if (( !sweepStartDone || initSweep || changedSetting ) )
{
if ( initSweep || changedSetting ) // Something has changed, or a first start, so need to owrk out some basic things
{
//Serial.println("InitSweep or changedSetting");
autoSweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / displayPoints;
vbw = autoSweepFreqStep / 1000.0; // Set the video resolution
requiredRBW10 = setting.Bandwidth10; // and the resolution bandwidth
if ( requiredRBW10 == 0 ) // If the bandwidth is on "Auto" work out the required RBW
requiredRBW10 = (( setting.ScanStop - setting.ScanStart )) / 29000; // 290 points on display, kHz
if ( requiredRBW10 < 26 ) // If it's less than 2.6KHz
requiredRBW10 = 26; // set it to 2.6KHz
if ( requiredRBW10 > 6207 )
requiredRBW10 = 6207;
if ( requiredRBW10 != old_requiredRBW10 )
{
bandwidth = rcvr.SetRBW ( requiredRBW10, &delaytime, &bpfIndex ); // Set it in the receiver Si4432
old_requiredRBW10 = requiredRBW10;
}
/*
* The FIR filters in the SI4432 are centred above the nominal IF frequency as that is where the signals are meant to be.
* To make sure we are not on the edge of the filters offset the IF frequency down by half the bandwidth
* This needs to be optimised *********
*/
// offsetIF = setting.IF_Freq + setting.Bandwidth10 * 50; // bW10 is in kHz * 10, so * 100-> kHz, halved
offsetIF = setting.IF_Freq + RX_PASSBAND_OFFSET; // half of narrowest RBW
/*
* Need multiple readings for each pixel in the display to avoid missing signals.
* Work out how many points needed for the whole sweep:
*/
sweepPoints = (uint32_t)(( setting.ScanStop - setting.ScanStart ) / bandwidth / 1000.0 * OVERLAP + 0.5); // allow for some overlap (filters will have 3dB roll off at edge) and round up
if ( sweepPoints < displayPoints )
sweepPoints = displayPoints; // At least the right number of points for the display
sweepFreqStep = ( setting.ScanStop - setting.ScanStart ) / sweepPoints; // Step for each reading
if ( setting.Attenuate != old_settingAttenuate )
{
if ( !att.SetAtten ( setting.Attenuate )) // Set the internal attenuator
setting.Attenuate = att.GetAtten (); // Read back if limited (setting.Attenuate was outside range)
old_settingAttenuate = setting.Attenuate;
}
// pre-calculate adjustment for RSSI values
dBadjust = (double)setting.Attenuate - 120.0 + setting.LevelOffset - setting.ExternalGain + bpfCalibrations[bpfIndex];
Serial.printf("SweepLo dBadjust = %f; leveloffset = %f; attenuate = %i, ext gain = %f, bpfCal = %f\n",
dBadjust, setting.LevelOffset, setting.Attenuate, setting.ExternalGain, bpfCalibrations[bpfIndex]);
resetAverage = changedSetting;
xmit.SetPowerReference ( setting.ReferenceOut ); // Set the GPIO reference output if wanted
maxGrid = setting.MaxGrid;
minGrid = setting.MinGrid;
#if USE_WIFI
// Vary number of points to send in each chunk depending on delaytime
// A chunk is sent at the end of each sweep regardless
wiFiPoints = wiFiTargetTime / delaytime;
if (wiFiPoints > MAX_WIFI_POINTS)
wiFiPoints = MAX_WIFI_POINTS;
if (wiFiPoints > displayPoints*OVERLAP)
wiFiPoints = displayPoints*OVERLAP;
//Serial.printf("No of wifiPoints set to %i\n", wiFiPoints);
if ( numberOfWebsocketClients > 0 )
pushSettings ();
#endif // #if USE_WIFI
#if defined( TG_IF_INSTALLED ) && !defined( TG_LO_INSTALLED )
if (tgIF_OK && (trackGenSetting.Mode == 1) )
{
tg_if.TxMode ( trackGenSetting.IF_Drive ); // Set tracking generator IF on
Serial.println("tgif turned on");
delayMicroseconds(300);
}
else
tg_if.RxMode();
#endif
#if defined(TG_IF_INSTALLED) && defined(TG_LO_INSTALLED)
if (tgLO_OK && tgIF_OK)
{
switch ( trackGenSetting.Mode )
{
case 0: // off
tg_if.RxMode();
tg_lo.RxMode();
break;
case 1: // tracking
tg_if.TxMode ( trackGenSetting.IF_Drive ); // Set tracking generator IF on
tg_lo.TxMode ( trackGenSetting.LO_Drive ); // Set tracking generator LO on
break;
case 2: // generator mode
tg_lo.TxMode ( trackGenSetting.LO_Drive ); // Set tracking generator LO on
tg_if.TxMode ( trackGenSetting.IF_Drive ); // Set tracking generator IF on
delayMicroseconds(300);
tg_lo.SetFrequency ( setting.IF_Freq + trackGenSetting.Offset + trackGenSetting.Frequency );
break;
default:
Serial.println("Invalid track gen mode in Sweeplo");
}
}
#endif
} // initSweep || changedSetting
autoSweepStep = 0; // Set the step counter to zero
sweepStep = 0;
autoSweepFreq = setting.ScanStart; // Set the start frequency.
nextPointFreq = autoSweepFreq + autoSweepFreqStep;
/* Spur reduction offsets the IF from its normal value. LO also has to be offset same amount
* Offset should be more than half the RX bandwidth to ensure spur is still not in the RX filter passband
* but not so big that the frequencies fall outside the SAW filter passband.
* Use the average trace set to minimum to see the result. Spurs if any will be visible
* at different frequencies.
* Real signals will be present at the same frequency, so a min trace will show only real signals
* How well this works depends on how flat the SAW filter (and SI4432 filter) response is
*/
if (setting.Spur && spurToggle) {
uint32_t IF_Shift = requiredRBW10 * 100; // bandwidth in Hz
if (IF_Shift > MAX_IF_SHIFT)
IF_Shift = MAX_IF_SHIFT;
tempIF = offsetIF - IF_Shift;
} else {
tempIF = offsetIF;
}
// track gen IF follows LO if only one SI4432, otherwise its offset by an amount to reduce blow by
if (tgLO_OK)
tgIF = tempIF + trackGenSetting.Offset;
else
tgIF = tempIF;
spurToggle = !spurToggle;
//Serial.printf("tempIF %u, spurOffset=%i, spur:%i, Toggle:%i\n", tempIF, tempIF - setting.IF_Freq, setting.Spur, spurToggle);
while (( micros() - setFreqMicros ) < delaytime ) // Make sure enough time has elasped since previous frequency write
{
}
if ( ( lastIF != tempIF ) || initSweep || changedSetting )
{
//Serial.printf("set rcvr Freq get:%u, tempIF:%u\n", rcvr.GetFrequency(), tempIF);
rcvr.SetFrequency ( tempIF ); // Set the RX Si4432 to the IF frequency
lastIF = tempIF;
actualIF = rcvr.GetFrequency();
#ifdef TG_IF_INSTALLED
if (tgIF_OK && (trackGenSetting.Mode == 1) )
{
tg_if.SetFrequency ( tgIF ); // Set tracking generator IF for the sweep
//Serial.printf("tgif set to %i Hz\n", tgIF);
}
#endif
}
xmit.SetFrequency ( tempIF + autoSweepFreq ); // set the LO frequency, tempIF is offset if spur reduction on
#ifdef TG_LO_INSTALLED
if (tgLO_OK && (trackGenSetting.Mode == 1) )
{
tg_lo.SetFrequency ( tgIF + autoSweepFreq ); // Set tracking generator LO
//Serial.printf("tglo set to %i Hz at start of sweep\n", tgIF + autoSweepFreq);
}
#endif
setFreqMicros = micros(); // Store the time the frequency was changed
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency() - actualIF + RX_PASSBAND_OFFSET; // Used for next RSSI command and JSON entry
#if USE_WIFI
if ( numberOfWebsocketClients > 0 ) // Start off the json document for the scan
{
jsonDocument.clear ();
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
else
jsonDocInitialised = false;
#endif // #if USE_WIFI
startFreq = setting.ScanStart + tempIF; // Start freq for the LO
stopFreq = setting.ScanStop + tempIF; // Stop freq for the LO
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
/*
* Copy the values for the peaks (marker positions) to the old versions. No need to
* reset the indicies or frequencies; just the "Level".
*/
for ( int i = 0; i < MARKER_COUNT; i++ )
{
oldPeaks[i].Level = peaks[i].Level;
oldPeaks[i].Index = peaks[i].Index;
oldPeaks[i].Freq = peaks[i].Freq;
peaks[i].Level = 0;
}
DisplayInfo (); // Display axis, top and side bar text
peakLevel = 0; // Reset the peak values for the sweep
peakFreq = 0.0;
peakGain = 100; // Set to higher than gain can ever be
lastMinRSSI = minRSSI;
minRSSI = 300; // Higher than it can be
pointsPastPeak = 0; // Avoid possible peak detection at start of sweep
peakRSSI = 0;
maxRSSI = 0;
sweepStartDone = true; // Make sure this initialize is only done once per sweep
initSweep = false;
changedSetting = false;
if ( setActualPowerRequested )
{
SetPowerLevel ( actualPower );
setActualPowerRequested = false;
// Serial.printf ( "Setting actual Power %f \n", actualPower );
}
lastSweepStartMicros = sweepStartMicros; // Set last time we got here
sweepStartMicros = micros(); // Current time
sweepMicros = sweepStartMicros - lastSweepStartMicros; // Calculate sweep time (no rollover handling)
} // End of "if ( !sweepStartDone ) || initSweep || changedSetting )"
/*
* Here we do the actual sweep. Save the current step and frequencies for the next time
* through, then wait the required amount of time based on the RBW before taking the
* signal strength reading and changing the transmitter (LO) frequency.
*/
uint16_t oldSweepStep = autoSweepStep;
uint32_t oldSweepFreq = actualFreq;
/*
* Wait until time to take the next reading. If a long enough wait left
* then check the touchscreen and Websockets while we are waiting
* to improve response
*/
nowMicros = micros();
while (( nowMicros - setFreqMicros ) < delaytime )
{
if ( ( nowMicros - setFreqMicros + delaytime > MIN_DELAY_WEBSOCKETS ) &&
( (nowMicros - lastWebsocketMicros > websocketInterval) || (numberOfWebsocketClients > 0) ) )
{
// Serial.print("W");
// webSocket.loop (); // Check websockets - includes Yield() to allow other events to run
// Serial.println("w");
lastWebsocketMicros = nowMicros;
}
if ( nowMicros - setFreqMicros > 100 ) // Wait some time to allow DMA sprite write to finish!
UiProcessTouch (); // Check the touch screen
// Serial.println("w");
nowMicros = micros();
}
int rxRSSI = rcvr.GetRSSI (); // Read the RSSI from the RX SI4432
/*
* Note that there are two different versions of the print statement to send the
* RSSI readings to the serial output. You can change which one is commented out.
*
* The first one produces a tab separated list of just the frequency and RSSI
* reading. That format can be easily read inte something like Excel.
*
* The second one produces a listing more fit for human consumption!
*/
if ( showRSSI ) // Displaying RSSI?
{
// Serial.printf ( "%s\t%03d\n",
// FormatFrequency ( autoSweepFreq) , rxRSSI ); // Send it to the serial output
Serial.printf ( "Actual IF: %s", FormatFrequency ( rcvr.GetFrequency() ) );
Serial.printf ( " LO Freq: %s", FormatFrequency ( xmit.GetFrequency() ) );
Serial.printf ( " Sweep Freq: %s", FormatFrequency ( autoSweepFreq) );
Serial.printf ( " Actual Freq %s - RSSI: %03d\n",
FormatFrequency ( actualFreq ), rxRSSI ); // Send it to the serial output
}
if ( (numberOfWebsocketClients > 0) || (setting.ShowGain) )
gainReading = GetPreampGain ( &AGC_On, &AGC_Reg ); // Record the preamp/lna gains
autoSweepFreq += sweepFreqStep; // Increment the frequency
sweepStep++; // and increment the step count
/*
* Change the transmitter frequency for the next reading and record the time for
* the RBW required settling delay.
*/
uint32_t f = tempIF + autoSweepFreq;
xmit.SetFrequency ( f ); // Set the new LO frequency as soon as RSSI read
// Serial.printf("LO Required: %i Actual %i\n", tempIF+autoSweepFreq, xmit.GetFrequency());
#ifdef TG_LO_INSTALLED
if (tgLO_OK && (trackGenSetting.Mode == 1) )
{
tg_lo.SetFrequency ( f + trackGenSetting.Offset ); // Set tracking generator LO
}
#endif
setFreqMicros = micros(); // Store the time the LO frequency was changed
#ifdef TG_LO_INSTALLED
// if (trackGenSetting.Mode == 1)
// Serial.printf("tglo %i f=%i, lo=%02X, if=%02X\n", tg_lo.ReadFrequency()- tg_if.ReadFrequency(), autoSweepFreq, tg_lo.ReadByte(REG_OFC1) & 0x0F, tg_if.ReadByte(REG_OFC1) & 0x0F );
#endif
#if USE_WIFI
if ( numberOfWebsocketClients > 0 )
{
if ( jsonDocInitialised )
{
JsonObject dataPoint = Points.createNestedObject (); // Add an object to the Json array to be pushed to the client
dataPoint["x"] = oldSweepFreq/1000000.0; // Set the x(frequency) value
dataPoint["y"] = rxRSSI; // Set the y (RSSI) value
dataPoint["g"] = gainReading; // Set the y (gain) value
// Serial.printf ( "Add point chunkIndex %u, sweepStep %u of %u \n", chunkIndex, sweepStep, sweepPoints);
chunkIndex++; // increment no of data points in current WiFi chunk
if ( chunkIndex >= wiFiPoints ) // Send the chunk of data and start new jSon document
{
String wsBuffer;
if ( wsBuffer )
{
// Serial.print("D");
serializeJson ( jsonDocument, wsBuffer );
// Serial.printf("J%u", wsBuffer.length() );
// Serial.println(wsBuffer);
unsigned long s = millis();
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
if (millis() - s > 1000)
{
Serial.println("webSocketTimeout");
Serial.println(wsBuffer);
websocketFailCount++;
if (websocketFailCount > 2)
numberOfWebsocketClients = 0;
} else {
websocketFailCount = 0; // reset if OK
}
// Serial.print("j");
}
else
Serial.println("No buffer :(");
}
}
if ( ( chunkIndex >= wiFiPoints ) || !jsonDocInitialised ) // Start new jSon document
{
chunkIndex = 0;
initChunkSweepDoc (sweepStep);
Points = jsonDocument.createNestedArray ( "Points" ); // Add Points array
jsonDocInitialised = true;
}
}
/*
* Actual frequency in the SI4432 is rounded and is limited by the possible resolution
*/
actualFreq = xmit.GetFrequency() - actualIF + RX_PASSBAND_OFFSET; // Used for next RSSI command and JSON entry
#endif // #if USE_WIFI
if ( rxRSSI > pointMaxRSSI ) // RSSI > maximum value for this point so far?
{
myActual[autoSweepStep] = rxRSSI; // Yes, save it
pointMaxRSSI = rxRSSI; // Added by G3ZQC - Remember new maximim
pointMaxFreq = oldSweepFreq;
}
if ( gainReading < pointMinGain ) // Gain < minimum gain for this point so far?
{
myGain[autoSweepStep] = gainReading; // Yes, save it
pointMinGain = gainReading; // Added by G3ZQC - Remember new minimum
}
if (rxRSSI < minRSSI) // Detect minimum for sweep
minRSSI = rxRSSI;
/*
* Have we enough readings for this display point? If yes, so do any averaging etc, reset
* the values so peak in the frequency step is recorded and update the display.
*/
if ( autoSweepFreq >= nextPointFreq )
{
nextPointFreq = nextPointFreq + autoSweepFreqStep; // Next display point frequency
autoSweepStep++; // Increment the index
pointMinGain = 100; // Reset min/max values
pointMaxRSSI = 0;
DrawCheckerBoard ( oldSweepStep ); // Draw the grid for the point in the sweep we have just read
if ( resetAverage || setting.Average == AV_OFF ) // Store data, either as read or as rolling average
myData[oldSweepStep] = myActual[oldSweepStep];
else
{
switch ( setting.Average )
{
case AV_MIN:
if ( myData[oldSweepStep] > myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep];
break;
case AV_MAX:
if ( myData[oldSweepStep] < myActual[oldSweepStep] )
myData[oldSweepStep] = myActual[oldSweepStep];
break;
case AV_2:
myData[oldSweepStep] = ( myData[oldSweepStep] + myActual[oldSweepStep] ) / 2;
break;
case AV_4:
myData[oldSweepStep] = ( myData[oldSweepStep]*3 + myActual[oldSweepStep] ) / 4;
break;
case AV_8:
myData[oldSweepStep] = ( myData[oldSweepStep]*7 + myActual[oldSweepStep] ) / 8;
break;
}
DisplayPoint ( myData, oldSweepStep, AVG_COLOR );
}
if ( setting.ShowSweep )
DisplayPoint ( myActual, oldSweepStep, DB_COLOR );
if ( setting.ShowGain )
displayGainPoint ( myGain, oldSweepStep, GAIN_COLOR );
if ( setting.ShowStorage )
DisplayPoint ( myStorage, oldSweepStep, STORAGE_COLOR );
if ( setting.SubtractStorage )
rxRSSI = 128 + rxRSSI - myStorage[oldSweepStep];
/*
* Record the peak values but not if freq low enough to detect the LO
*/
if (( autoSweepFreq > MARKER_MIN_FREQUENCY ) && (oldSweepStep > 0))
{
if ( maxRSSI <= myActual[oldSweepStep] )
{
maxIndex = oldSweepStep;
maxRSSI = myActual[oldSweepStep];
maxFreq = oldSweepFreq;
// Serial.printf( "peakLevel set %i, index %i\n", peakLevel, oldSweepStep);
// displayPeakData ();
}
/*
* Save values used by peak detection. Need to save the previous value as we only
* know we have a peak once past it!
*/
prevPointRSSI = currentPointRSSI;
currentPointRSSI = myData[oldSweepStep];
/*
* Peak point detection. Four peaks, used to position the markers
*/
if ( currentPointRSSI >= prevPointRSSI ) // Level or ascending
{
pointsPastDip ++;
if ( pointsPastDip == PAST_PEAK_LIMIT )
{
pointsPastPeak = 0;
}
if ( currentPointRSSI > peakRSSI )
{
peakRSSI = currentPointRSSI; // Store values
peakFreq = oldSweepFreq;
peakIndex = oldSweepStep;
}
}
else
{
pointsPastPeak ++; // only a true peak if value decreased for a number of consecutive points
if ( pointsPastPeak == PAST_PEAK_LIMIT ) // We have a peak
{
pointsPastDip = 0;
/*
* Is this peak bigger than previous ones? Only check if bigger than smallest peak so far
*/
if ( peakRSSI > peaks[MARKER_COUNT-1].Level )
{
for ( uint16_t p = 0; p < MARKER_COUNT; p++ )
{
if ( peakRSSI > peaks[p].Level )
{
for ( uint16_t n = 3; n > p; n-- ) // Shuffle lower level peaks down
memcpy ( &peaks[n], &peaks[n-1], sizeof ( peak_t ));
peaks[p].Level = peakRSSI; // Save the peak values
peaks[p].Freq = peakFreq;
peaks[p].Index = peakIndex;
break;
}
}
}
peakRSSI = 0; // Reset peak values ready for next peak
} // We have a peak
} // Descending
} // if (( autoSweepFreq > 1000000 ) && (oldSweepStep > 0))
/*
* Draw the markers if main sweep is displayed. The markers know if they are enabled or not
* Only paint if sweep step is in range where there will be a marker
*/
if ( setting.ShowSweep || setting.Average != AV_OFF )
{
for ( int p = 0; p < MARKER_COUNT; p++ )
if (( abs ( oldSweepStep - oldPeaks[p].Index )
<= MARKER_SPRITE_HEIGHT / 2 ) && ( oldPeaks[p].Level > (lastMinRSSI + MARKER_NOISE_LIMIT) ))
marker[p].Paint ( &img, oldPeaks[p].Index - oldSweepStep,
rssiToImgY ( oldPeaks[p].Level ) );
}
// If in the last few points and gain trace is displayed show the gain scale
if ( setting.ShowGain && (oldSweepStep > displayPoints - 2 * CHAR_WIDTH) )
{
int16_t scaleX = displayPoints - 2 * CHAR_WIDTH - oldSweepStep + 1; // relative to the img sprite
img.setPivot( scaleX, 0);
gainScaleSprite.pushRotated ( &img, 0, TFT_BLACK ); // Send the sprite to the target sprite, with transparent colour
}
if ( oldSweepStep > 0 ) // Only push if not first point (two pixel wide img)
img.pushSprite ( xOrigin+oldSweepStep-1, yOrigin );
// myFreq[oldSweepStep] = oldSweepFreq; // Store the frequency for XML file creation
} // End of "if ( autoSweepFreq >= nextPointFreq )"
if ( sweepStep >= sweepPoints ) // If we have got to the end of the sweep
{
// autoSweepStep = 0;
sweepStartDone = false;
resetAverage = false;
if ( sweepCount < 2 )
sweepCount++; // Used to disable wifi at start
// Serial.printf("MaxRSSI = %i, freq = %i\n", maxRSSI, maxFreq);
oldPeakLevel = maxRSSI; //Save value of peak level for use by the "SetPowerLevel" function
if ( myActual[displayPoints-1] == 0 ) // Ensure a value in last data point
{
myActual[displayPoints-1] = rxRSSI; // Yes, save it
myGain[displayPoints-1] = gainReading;
// myFreq[displayPoints-1] = oldSweepFreq;
}
if ( showRSSI == 1 ) // Only show it once?
showRSSI = 0; // Then turn it off
#if USE_WIFI
if (( numberOfWebsocketClients > 0) && jsonDocInitialised && (chunkIndex > 0) )
{
String wsBuffer;
if (wsBuffer)
{
serializeJson ( jsonDocument, wsBuffer );
webSocket.broadcastTXT ( wsBuffer ); // Send to all connected websocket clients
}
else
Serial.println ( "No buffer :(");
}
#endif // #if USE_WIFI
} // End of "if ( sweepStep >= sweepPoints )"
} // End of "doSweepLow"

View File

@ -0,0 +1,139 @@
// This header file contains a list of user setup files and defines which one the
// compiler uses when the IDE performs a Verify/Compile or Upload.
//
// Users can create configurations for different Espressif boards and TFT displays.
// This makes selecting between hardware setups easy by "uncommenting" one line.
// The advantage of this hardware configuration method is that the examples provided
// with the library should work with different setups immediately without any other
// changes being needed. It also improves the portability of users sketches to other
// hardware configurations and compatible libraries.
//
// Create a shortcut to this file on your desktop to permit quick access for editing.
// Re-compile and upload after making and saving any changes to this file.
// Customised User_Setup files are stored in the "User_Setups" folder.
#ifndef USER_SETUP_LOADED // Lets PlatformIO users define settings in
// platformio.ini, see notes in "Tools" folder.
// Only ONE line below should be uncommented. Add extra lines and files as needed.
//#include <User_Setup.h> // Default setup is root library folder
//#include <User_Setups/WA2FZW_Setup_ILI9341_TinySA.h> // Setup file configured for ILI9341 on TinySA
#include <User_Setups/simpleSA_Setup_ILI9341.h> // Setup file configured for ILI9341 on TinySA
//#include <User_Setups/Setup1_ILI9341.h> // Setup file configured for my ILI9341
//#include <User_Setups/Setup2_ST7735.h> // Setup file configured for my ST7735
//#include <User_Setups/Setup3_ILI9163.h> // Setup file configured for my ILI9163
//#include <User_Setups/Setup4_S6D02A1.h> // Setup file configured for my S6D02A1
//#include <User_Setups/Setup5_RPi_ILI9486.h> // Setup file configured for my stock RPi TFT
//#include <User_Setups/Setup6_RPi_Wr_ILI9486.h> // Setup file configured for my modified RPi TFT
//#include <User_Setups/Setup7_ST7735_128x128.h> // Setup file configured for my ST7735 128x128 display
//#include <User_Setups/Setup8_ILI9163_128x128.h> // Setup file configured for my ILI9163 128x128 display
//#include <User_Setups/Setup9_ST7735_Overlap.h> // Setup file configured for my ST7735
//#include <User_Setups/Setup10_RPi_touch_ILI9486.h> // Setup file configured for ESP8266 and RPi TFT with touch
//#include <User_Setups/Setup11_RPi_touch_ILI9486.h> // Setup file configured for ESP32 and RPi TFT with touch
//#include <User_Setups/Setup12_M5Stack.h> // Setup file for the ESP32 based M5Stack
//#include <User_Setups/Setup13_ILI9481_Parallel.h> // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup14_ILI9341_Parallel.h> // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup15_HX8357D.h> // Setup file configured for HX8357D (untested)
//#include <User_Setups/Setup16_ILI9488_Parallel.h> // Setup file for the ESP32 with parallel bus TFT
//#include <User_Setups/Setup17_ePaper.h> // Setup file for any Waveshare ePaper display
//#include <User_Setups/Setup18_ST7789.h> // Setup file configured for HX8357D (untested)
//#include <User_Setups/Setup20_ILI9488.h> // Setup file for ESP8266 and ILI9488 SPI bus TFT
//#include <User_Setups/Setup21_ILI9488.h> // Setup file for ESP32 and ILI9488 SPI bus TFT
//#include <User_Setups/Setup22_TTGO_T4.h> // Setup file for ESP32 and TTGO T4 (BTC) ILI9341 SPI bus TFT
//#include <User_Setups/Setup23_TTGO_TM.h> // Setup file for ESP32 and TTGO TM ST7789 SPI bus TFT
//#include <User_Setups/Setup43_ST7735.h> // Setup file configured for my ST7735S 80x160
//#include <User_Setups/SetupX_Template.h>
#endif // USER_SETUP_LOADED
/////////////////////////////////////////////////////////////////////////////////////
// //
// DON'T TINKER WITH ANY OF THE FOLLOWING LINES, THESE ADD THE TFT DRIVERS //
// AND ESP8266 PIN DEFINITONS THEY ARE HERE FOR BODMER'S CONVENIENCE! //
// //
/////////////////////////////////////////////////////////////////////////////////////
// Identical looking TFT displays may have a different colour ordering in the 16 bit colour
#define TFT_BGR 0 // Colour order Blue-Green-Red
#define TFT_RGB 1 // Colour order Red-Green-Blue
// Load the right driver definition - do not tinker here !
#if defined (ILI9341_DRIVER)
#include <TFT_Drivers/ILI9341_Defines.h>
#define TFT_DRIVER 0x9341
#elif defined (ST7735_DRIVER)
#include <TFT_Drivers/ST7735_Defines.h>
#define TFT_DRIVER 0x7735
#elif defined (ILI9163_DRIVER)
#include <TFT_Drivers/ILI9163_Defines.h>
#define TFT_DRIVER 0x9163
#elif defined (S6D02A1_DRIVER)
#include <TFT_Drivers/S6D02A1_Defines.h>
#define TFT_DRIVER 0x6D02
#elif defined (RPI_ILI9486_DRIVER)
#include <TFT_Drivers/ILI9486_Defines.h>
#define TFT_DRIVER 0x9486
#elif defined (ILI9486_DRIVER)
#include <TFT_Drivers/ILI9486_Defines.h>
#define TFT_DRIVER 0x9486
#elif defined (ILI9481_DRIVER)
#include <TFT_Drivers/ILI9481_Defines.h>
#define TFT_DRIVER 0x9481
#elif defined (ILI9488_DRIVER)
#include <TFT_Drivers/ILI9488_Defines.h>
#define TFT_DRIVER 0x9488
#elif defined (HX8357D_DRIVER)
#include "TFT_Drivers/HX8357D_Defines.h"
#define TFT_DRIVER 0x8357
#elif defined (EPD_DRIVER)
#include "TFT_Drivers/EPD_Defines.h"
#define TFT_DRIVER 0xE9D
#elif defined (ST7789_DRIVER)
#include "TFT_Drivers/ST7789_Defines.h"
#define TFT_DRIVER 0x7789
#elif defined (R61581_DRIVER)
#include "TFT_Drivers/R61581_Defines.h"
#define TFT_DRIVER 0x6158
#elif defined (XYZZY_DRIVER) // <<<<<<<<<<<<<<<<<<<<<<<< ADD NEW DRIVER HERE
#include "TFT_Drivers/XYZZY_Defines.h"
#define TFT_DRIVER 0x0000
#else
#define TFT_DRIVER 0x0000
#endif
// These are the pins for all ESP8266 boards
// Name GPIO Function
#define PIN_D0 16 // WAKE
#define PIN_D1 5 // User purpose
#define PIN_D2 4 // User purpose
#define PIN_D3 0 // Low on boot means enter FLASH mode
#define PIN_D4 2 // TXD1 (must be high on boot to go to UART0 FLASH mode)
#define PIN_D5 14 // HSCLK
#define PIN_D6 12 // HMISO
#define PIN_D7 13 // HMOSI RXD2
#define PIN_D8 15 // HCS TXD0 (must be low on boot to enter UART0 FLASH mode)
#define PIN_D9 44 // RXD0
#define PIN_D10 43 // TXD0
#define PIN_MOSI 8 // SD1
#define PIN_MISO 7 // SD0
#define PIN_SCLK 6 // CLK
#define PIN_HWCS 0 // D3
#define PIN_D11 9 // SD2
#define PIN_D12 10 // SD4

View File

@ -0,0 +1,37 @@
// See SetupX_Template.h for all options available
#define ILI9341_DRIVER
#define USE_HSPI_PORT // default is VSPI
#define TFT_DC 27
#define TFT_CS 15
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_MISO 12
#define TFT_LED 25
#define TOUCH_CS 26
#define TFT_RST 2
//#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to NodeMCU RST or 3.3V
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
#define SPI_FREQUENCY 27000000
// #define SPI_FREQUENCY 40000000
// #define SPI_FREQUENCY 80000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
// #define SUPPORT_TRANSACTIONS

View File

@ -0,0 +1,37 @@
// See SetupX_Template.h for all options available
#define ILI9341_DRIVER
#define USE_HSPI_PORT // default is VSPI
#define TFT_DC 27
#define TFT_CS 15
#define TFT_MOSI 13
#define TFT_SCLK 14
#define TFT_MISO 12
//#define TFT_LED 25
#define TOUCH_CS 26
//#define TFT_RST 2
#define TFT_RST -1 // Set TFT_RST to -1 if the display RESET is connected to ESP32 RST or 3.3V
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts
#define SMOOTH_FONT
//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000
// #define SPI_FREQUENCY 80000000
#define SPI_READ_FREQUENCY 20000000
#define SPI_TOUCH_FREQUENCY 2500000
// #define SUPPORT_TRANSACTIONS

3520
cmd.cpp Normal file

File diff suppressed because it is too large Load Diff

239
cmd.h Normal file
View File

@ -0,0 +1,239 @@
/*
* "Cmd.h" was added in Version 2.0 by John Price (WA2FZW)
*
* This file contains definitions and function prototypes associated with
* the "Cmd.cpp" module.
*
* In Version 2.0 of the software, all of the serial input command handling
* was removed from the main program sile into these modules.
*/
#ifndef _CMD_H_
#define _CMD_H_ // Prevent double inclusion
#include "simpleSA.h" // General program definitions
#include "pE4302.h" // Class definition for thePE4302 attenuator
#include "si4432.h" // Class definition for the Si4432 transceiver
#include "preferencesd.h" // For saving the "setting" structure
#include <SPI.h>
/*
* These are numerical values which define the message being processed. We could just
* work off the ASCII string, but translating the strings to a numerical value makes
* the processing a bit easier and also keeps open the possibility of using more tha
* one ASCII string to implement a give command.
*/
#define MSG_NONE 0 // Unrecognized command
#define MSG_START 1 // Set sweep start frequency
#define MSG_STOP 2 // Set sweep stop frequency
#define MSG_CENTER 3 // Set sweep center frequency
#define MSG_SPAN 4 // Set sweep range
#define MSG_FOCUS 5 // Center frequency with narrow sweep
#define MSG_DRIVE 6 // Set transmitter (LO) output level
#define MSG_VFO_FREQ 7 // Set the frequency for the selected VFO
#define MSG_ATTEN 8 // Set the PE4301 attenuation
#define MSG_HELP 9 // Display the command menu
#define MSG_STEPS 10 // Set or get number of sweep points (not used)
#define MSG_DELAY 11 // Set or get delay time between sweep readings
#define MSG_VFO 12 // Set or get the currently selected VFO
#define MSG_RBW 13 // Set or get the current resolution bandwidth
#define MSG_REG_DUMP 14 // Print register values for the selected VFO
#define MSG_RSSI 15 // Show RSSI values
#define MSG_QUIT 16 // Stop RSSI readings
#define MSG_SET_REG 17 // Set or get the value of a specifix register for the selected VFO
#define MSG_SAVE 18 // Save scan configuration
#define MSG_RECALL 19 // Recall saved scan configuration
#define MSG_GPIO2 20 // Set transmitter GPIO2 reference frequency
#define MSG_TUNE 21 // Tune selected Si4432 (VFO)
#define MSG_CONFIG 22 // Save "config" structure
#define MSG_ACT_PWR 23 // Calibrate the observed power level
#define MSG_IF_FREQ 24 // Set the IF frequency
#define MSG_TRACES 25 // Turn things on the display on or off
#define MSG_PREAMP 26 // Set the receiver preamp gain
#define MSG_GRID 27 // Set the dB value for top line of the grid
#define MSG_SCALE 28 // Set the dB/horizontal line for the grid
#define MSG_PAUSE 29 // Pause (or resume) the sweep
#define MSG_SWEEPLO 30 // Set Analyse low range mode
#define MSG_SIGLO 31 // Signal generate low range mode
#define MSG_IF_SWEEP 32 // Set IF Sweep mode
#define MSG_MARKER 33 // Configure Markers
#define MSG_SPUR 34 // Set Spur reduction on or off
#define MSG_WIFI_UPDATE 35 // Set WiFi update target time in us
#define MSG_WIFI_POINTS 36 // Set WiFi chunk size
#define MSG_WEBSKT_INTERVAL 37 // Set interval between checking websocket for events if no client connected
#define MSG_SG_RX_DRIVE 38 // Set Signal Generator RX (IF) Drive level - don't go above the 10dBm rating of the SAW filters
#define MSG_SG_LO_DRIVE 39 // Set Signal Generator LO Drive level - limited by mixer/attenuators fitted
#define MSG_TG_IF_DRIVE 40 // Set Tracking Generator RX (IF) Drive level - don't go above the 10dBm rating of the SAW filters
#define MSG_TG_LO_DRIVE 41 // Set Tracking Generator LO Drive level - limited by mixer/attenuators fitted
#define MSG_IFSIGNAL 42 // Set frequency of injected signal for IF Sweep
#define MSG_TGOFFSET 43 // Offset TG IF frequency from SA IF
#define MSG_BANDSCOPE 44 // Set Bandscope Mode
#define MSG_IFSWEEP 45 // Set IF Sweep Mode
#define MSG_OFFDELAY 46 // Adjust offset tuning delay time before taking reading
#define MSG_WFMIN 47 // set the min level for the waterfall colours
#define MSG_WFGAIN 48 // set the gain for waterfall colouring
#define MSG_RX_SWEEP 49 // Set the RX Sweep Mode
#define MSG_RXSIGNAL 50 // Set frequency of injected signal for IF Sweep
#define MSG_EXTGAIN 51 // Set external gain or attenuation
#define MSG_SGON 52 // Turn Signal Generator output on
#define MSG_SGOFF 53 // Turn Signal Generator output off
#define MSG_SGFREQ 54 // Set Signal Generator Frequency
#define MSG_TGON 55 // Turn Tracking Generator output on
#define MSG_TGOFF 56 // Turn Tracking Generator output off
#define MSG_TGFREQ 57 // Set Track Gen frequency for sig gen mode
#define MSG_TGSIG 58 // Set Track Gen sig gen mode
#define MSG_BPFCAL 59 // Start bandpass filter calibration
#define MSG_OTA 60 // OTA Update mode
#define MSG_COLOURTEST 99 // test of waterfall colours - remove this when done!
typedef struct // Keeps everything together
{
char Name[20]; // ASCII Command string
uint8_t ID; // The internal message number
} msg_t;
/*
* This "enum" is used by the "SetSweepCenter" function to designate whether we want a
* "WIDE" span when setting the normal center frequency, or a "NARROW" span when setting
* a focus frequency.
*/
enum { WIDE, NARROW };
/*
* Function prototypes for the help menu display and main command processing:
*/
void ShowMenu (); // Displays the command menu
bool CheckCommand (); // Checks for a new command
bool ParseCommand ( char* inBuff ); // Separate command and data
bool ProcessCommand ( uint8_t command, char* dataBuff );
/*
* Function prototypes for general support functions:
*/
uint32_t ParseFrequency ( char* freqString ); // Handles various frequency input formats
int32_t ParseSignedFrequency ( char* freqString ); // Handles various frequency input formats
char* FormatFrequency ( uint32_t freq ); // Neatly formats frequencies for output
char* FormatSignedFrequency ( int32_t freq ); // Neatly formats signed frequencies for output
char* DisplayFrequency ( uint32_t freq ); // Neatly formats frequencies for sig gen display
uint16_t xtoi ( char* hexString ); // Converts hexadecimal strings into integers
bool isHex ( char c ); // Tests if a character is a hexadecimal digit
/*
* These functions all support the main command processing functions and are used to
* set the values of things either in the appropriate internal variables and/or in
* the PE4302 or Si4432 modules themselves.
*/
void SetRefOutput ( int freq ); // Sets the GPIO2 frequency for the LO
void SetRefLevel ( int ref ); // Sets the decibel level for the top line of the graph
void SetPowerGrid ( int g ); // Sets the dB/vertical divison on the grid
void SetGenerate ( int8_t g ); // Puts the unit into or out of signal generator mode
bool SetIFFrequency ( int32_t f ); // Sets the IF frequency
void SetLoDrive ( uint8_t level ); // Sets LO Si4432 output level
void SetSGState (uint16_t s);
bool SetSGFreq ( uint32_t freq); // set signal generator frequency - returns false if invalid
void SetSGLoDrive ( uint8_t level );
void SetSGRxDrive ( uint8_t level );
void SetSGPower ( int16_t dBm ); // Set signal generator attenuator and drive levels
void SetTracking ( int8_t m ); // set tracking generator mode
void SetTGLoDrive ( uint8_t level ); // set tracking generator drive
void SetTGIfDrive ( uint8_t level );
bool SetTGOffset ( int32_t offset); // set tracking generator offset - returns false if invalid
bool SetTGFreq ( int32_t freq); // set tracking generator freq for sig gen mode - returns false if invalid
int32_t GetTGOffset (void );
void SetTGPower ( int16_t dBm ); // Set TG attenuator and drive levels
void SetAttenuation ( int a ); // Sets the PE4302 attenuation
void SetExtGain ( double a ); // Sets the external gain or attenuation
double GetExtGain (void );
void SetStorage ( void ); // Saves the results of a scan
void SetClearStorage ( void ); // Logically erases the saved scan
void SetSubtractStorage(void); // Sets the "setting.SubtractStorage" flag
void RequestSetPowerLevel ( float o ); // Power level calibration
void SetPowerLevel ( double o ); // ???
void SetRBW ( int v ); // Sets the resolution bandwidth
void SetSpur (int v ); // Turns spurious signal supression on or off
void SetAverage ( int v ); // Sets the number of readings to average
void SetPreampGain ( uint8_t gain ); // Set and get the receiver preamp gain
uint8_t GetPreampGain ( bool* agc, uint8_t* reg );
void SetSweepStart ( uint32_t freq ); // Added in Version 2.3
uint32_t GetSweepStart ( void );
void SetSweepStop ( uint32_t freq ); // Added in Version 2.3
uint32_t GetSweepStop ( void );
void SetBandscopeStart ( uint32_t freq ); // Added in Version 3.0f
uint32_t GetBandscopeStart ( void );
void SetBandscopeSpan ( uint32_t freq ); // Added in Version 3.0f
uint32_t GetBandscopeSpan ( void );
void SetBandscopeRBW ( int v ); // Sets the resolution bandwidth
void SetBandscopeLevel ( int ref ); // Sets the decibel level for the top line of the graph
void SetIFsweepStart ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepStart ( void );
void SetIFsweepStop ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepStop ( void );
void SetIFsweepSigFreq ( uint32_t freq ); // Added in Version 3.0c
uint32_t GetIFsweepSigFreq ( void );
void SetRXsweepStart ( uint32_t freq );
uint32_t GetRXsweepStart ( void );
void SetRXsweepStop ( uint32_t freq );
uint32_t GetRXsweepStop ( void );
void SetRXsweepSpan ( uint32_t freq );
uint32_t GetRXsweepSpan ( void );
void SetRXsweepSigFreq ( uint32_t freq );
uint32_t GetRXsweepSigFreq ( void );
boolean StartBpfCal ( void );
void SetSweepCenter ( uint32_t freq, uint8_t span );
uint32_t GetSweepCenter ( void );
void SetSweepSpan ( uint32_t spanRange );
uint32_t GetSweepSpan ( void );
void SetFreq ( int vfo, uint32_t freq );
bool UpdateMarker ( uint8_t mkr, char action );
void SetWFMin (int16_t level);
void SetWFGain (float gain);
#endif // End of "Cmd.h"

BIN
data/NotoSansBold56.vlw Normal file

Binary file not shown.

BIN
data/NotoSansSCM14.vlw Normal file

Binary file not shown.

63
data/about.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<title>[simpleSA]</title>
<link rel="stylesheet" type="text/css" href="styles.css" media="all" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="support.js"></script>
<script type="text/javascript">
// At this point the page is running
$(document).ready(function () {
// Update the details
updateNameVersion();
});
</script>
</head>
<body>
<div class='norm'>
<h1>
<span id="programName">[programName]</span>&nbsp;<span id="programVersion">[programVersion]</span>
</h1>
<button id="homeButton" onclick="window.location.href='index.html';">Home</button>
<!-- <button id="settingsButton" onclick="window.location.href='settings.html';">Settings</button>
<button id="helpButton" onclick="window.location.href='help.html';">Help</button>
<button id="aboutButton" onclick="window.location.href='about.html';">About</button> -->
<br />
<br />
<!-- About Div -->
<div id="aboutDiv">
<hr />
<h1>About</h1>
<p style="max-width:700px;">
This simpleSA running on an ESP32 has been adapted by Dave M0WID and John WA2FZW.<br />
It was based on an early arduino prototype of the TinySA by Erik PD0EK.<br />
Information can be found at <a href = "https://groups.io/g/HBTE">https://groups.io/g/HBTE</a>
</p>
<hr/>
<form method="get" action="/doReboot">
To reboot click <button style="background-color: red; color: white;"
onclick="return confirm('Are you sure?')" type="submit">here</button>
</form>
</div>
</div>
<div id=footer>Copyright &copy; <span id="programCopyright">M0WID and WA2FZW</span></div>
</body>
</html>

215
data/backupRestore.html Normal file
View File

@ -0,0 +1,215 @@
<!DOCTYPE html>
<html>
<head>
<title>[RBNSpyBox]</title>
<link rel="stylesheet" type="text/css" href="styles.css" media="all" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="support.js"></script>
<script type="text/javascript">
// https://stackoverflow.com/questions/133310/how-can-i-get-jquery-to-perform-a-synchronous-rather-than-asynchronous-ajax-re
// At this point the page is running
$(document).ready(function () {
updateNameVersion();
});
function allowDrop(ev) {
ev.preventDefault();
}
async function foo(x) {
var res = await fetch(x)
console.log(res.ok)
}
function processFile(file) {
// Build a FileReader object to read and process the file
var reader = new FileReader();
reader.onload = function(e) {
// At this point we have the file loaded so we can turn it into
// an XML object
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(e.target.result, "application/xml");
// Try and locate the RBNSpyBox element
var rbnSpyBoxElements = xmlDoc.getElementsByTagName("RBNSpyBox");
if (1 != rbnSpyBoxElements.length) {
// This is not one of our files!
$('#progress').html(file.name + " is not recognised as valid backup!");
}
else {
// Pull out the Major and Minor version numbers
var major = rbnSpyBoxElements[0].getAttribute('Major');
var minor = rbnSpyBoxElements[0].getAttribute('Minor');
// Start off the backup
foo("/doRestoreFriendsBegin");
// Loop through all the friends
var friends = xmlDoc.getElementsByTagName("Friend");
for (i = 0; i < friends.length; i++) {
// Pull out the friend data
var callsign = friends[i].getAttribute('Callsign');
var name = friends[i].getAttribute('Name');
var comment = friends[i].getAttribute('Comment');
var soundEnabled = friends[i].getAttribute('SoundEnabled');
var soundIndex = friends[i].getAttribute('SoundIndex');
var sound = friends[i].getAttribute('Sound');
var lastSeenAs = friends[i].getAttribute('LastSeenAs');
var lastSeen = friends[i].getAttribute('LastSeen');
var lastSeenSeconds = friends[i].getAttribute('LastSeenSeconds');
var lastSpeed = friends[i].getAttribute('LastSpeed');
var lastSpeedUnits = friends[i].getAttribute('LastSpeedUnits');
var lastFrequency = friends[i].getAttribute('LastFrequency');
var lastMode = friends[i].getAttribute('LastMode');
// Build the URL
var url = "doRestoreFriend?MemorySlot=" + i
+ "&Callsign=" + encodeURIComponent(callsign)
+ "&Name=" + encodeURIComponent(name)
+ "&Comment=" + encodeURIComponent(comment)
+ "&SoundEnabled=" + encodeURIComponent(soundEnabled)
+ "&SoundIndex=" + encodeURIComponent(soundIndex)
+ "&Sound=" + encodeURIComponent(sound)
+ "&LastSeenAs=" + encodeURIComponent(lastSeenAs)
+ "&LastSeen=" + encodeURIComponent(lastSeen)
+ "&LastSeenSeconds=" + encodeURIComponent(lastSeenSeconds)
+ "&LastSpeed=" + encodeURIComponent(lastSpeed)
+ "&LastSpeedUnits=" + encodeURIComponent(lastSpeedUnits)
+ "&LastFrequency=" + encodeURIComponent(lastFrequency)
+ "&LastMode=" + encodeURIComponent(lastMode);
//alert(url);
foo(url);
}
// Commit the restore
foo("/doRestoreFriendsCommit");
}
};
reader.readAsText(file);
}
function drop(ev) {
// Prevent default behavior (Prevent file from being opened)
ev.preventDefault();
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.items.length; i++) {
// If dropped items aren't files, reject them
if (ev.dataTransfer.items[i].kind === 'file') {
var file = ev.dataTransfer.items[i].getAsFile();
console.log('... file[' + i + '].name = ' + file.name);
processFile(file);
}
}
} else {
// Use DataTransfer interface to access the file(s)
for (var i = 0; i < ev.dataTransfer.files.length; i++) {
console.log('... file[' + i + '].name = ' + ev.dataTransfer.files[i].name);
processFile(file);
}
}
}
</script>
</head>
<body>
<div class='norm'>
<h1>
<span id="programName">[programName]</span>&nbsp;<span id="programVersion">[programVersion]</span>
</h1>
<button id="homeButton" onclick="window.location.href='index.html';">Home</button>
<button id="friendsButton" onclick="window.location.href='friends.html';">Friends</button>
<button id="settingsButton" onclick="window.location.href='settings.html';">Settings</button>
<button id="backupRestoreButton" onclick="window.location.href='backupRestore.html';">Backup/Restore</button>
<button id="helpButton" onclick="window.location.href='help.html';">Help</button>
<button id="aboutButton" onclick="window.location.href='about.html';">About</button>
<br />
<br />
<!-- Backup/Restore Div -->
<div id="backupRestoreDiv">
<hr />
<h1>Backup</h1>
<p>
<form method="get" action="/getBackup">
To backup your friends click <button type="submit">here</button> and save the resulting
download safely.
</form>
</p>
<hr />
<h1>Restore</h1>
<p>
To restore your friends drag and drop your data file below.
</p>
<div style="
border-style: solid;
border-width:1px;
color: gray;
display: table-cell;
font-size: 20pt;
height: 100px;
text-align:center;
vertical-align: middle;
width: 400px;"
ondrop="drop(event)" ondragover="allowDrop(event)">
Here...
</div>
<p>
<span id="progress"></span>
</p>
<div>
</div>
<div id=footer>Copyright &copy; <span id="programCopyright"></span></div>
</body>
</html>

911
data/canvasjs.min.js vendored Normal file
View File

@ -0,0 +1,911 @@
/*
CanvasJS HTML5 & JavaScript Charts - v2.3.2 GA - https://canvasjs.com/
Copyright 2018 fenopix
--------------------- License Information --------------------
CanvasJS is a commercial product which requires purchase of license. Without a commercial license you can use it for evaluation purposes for upto 30 days. Please refer to the following link for further details.
https://canvasjs.com/license/
*/
/*eslint-disable*/
/*jshint ignore:start*/
(function(){function qa(h,p){h.prototype=eb(p.prototype);h.prototype.constructor=h;h.base=p.prototype}function eb(h){function p(){}p.prototype=h;return new p}function Ya(h,p,D){"millisecond"===D?h.setMilliseconds(h.getMilliseconds()+1*p):"second"===D?h.setSeconds(h.getSeconds()+1*p):"minute"===D?h.setMinutes(h.getMinutes()+1*p):"hour"===D?h.setHours(h.getHours()+1*p):"day"===D?h.setDate(h.getDate()+1*p):"week"===D?h.setDate(h.getDate()+7*p):"month"===D?h.setMonth(h.getMonth()+1*p):"year"===D&&h.setFullYear(h.getFullYear()+
1*p);return h}function $(h,p){var D=!1;0>h&&(D=!0,h*=-1);h=""+h;for(p=p?p:1;h.length<p;)h="0"+h;return D?"-"+h:h}function Ia(h){if(!h)return h;h=h.replace(/^\s\s*/,"");for(var p=/\s/,D=h.length;p.test(h.charAt(--D)););return h.slice(0,D+1)}function Ea(h){h.roundRect=function(h,D,r,u,H,F,z,v){z&&(this.fillStyle=z);v&&(this.strokeStyle=v);"undefined"===typeof H&&(H=5);this.lineWidth=F;this.beginPath();this.moveTo(h+H,D);this.lineTo(h+r-H,D);this.quadraticCurveTo(h+r,D,h+r,D+H);this.lineTo(h+r,D+u-H);
this.quadraticCurveTo(h+r,D+u,h+r-H,D+u);this.lineTo(h+H,D+u);this.quadraticCurveTo(h,D+u,h,D+u-H);this.lineTo(h,D+H);this.quadraticCurveTo(h,D,h+H,D);this.closePath();z&&this.fill();v&&0<F&&this.stroke()}}function Sa(h,p){return h-p}function Ta(h,p,D){if(h&&p&&D){D=D+"."+p;var r="image/"+p;h=h.toDataURL(r);var u=!1,H=document.createElement("a");H.download=D;H.href=h;if("undefined"!==typeof Blob&&new Blob){for(var F=h.replace(/^data:[a-z\/]*;base64,/,""),F=atob(F),z=new ArrayBuffer(F.length),z=new Uint8Array(z),
v=0;v<F.length;v++)z[v]=F.charCodeAt(v);p=new Blob([z.buffer],{type:"image/"+p});try{window.navigator.msSaveBlob(p,D),u=!0}catch(L){H.dataset.downloadurl=[r,H.download,H.href].join(":"),H.href=window.URL.createObjectURL(p)}}if(!u)try{event=document.createEvent("MouseEvents"),event.initMouseEvent("click",!0,!1,window,0,0,0,0,0,!1,!1,!1,!1,0,null),H.dispatchEvent?H.dispatchEvent(event):H.fireEvent&&H.fireEvent("onclick")}catch(E){p=window.open(),p.document.write("<img src='"+h+"'></img><div>Please right click on the image and save it to your device</div>"),
p.document.close()}}}function N(h){var p=((h&16711680)>>16).toString(16),D=((h&65280)>>8).toString(16);h=((h&255)>>0).toString(16);p=2>p.length?"0"+p:p;D=2>D.length?"0"+D:D;h=2>h.length?"0"+h:h;return"#"+p+D+h}function fb(h,p){var D=this.length>>>0,r=Number(p)||0,r=0>r?Math.ceil(r):Math.floor(r);for(0>r&&(r+=D);r<D;r++)if(r in this&&this[r]===h)return r;return-1}function u(h){return null===h||"undefined"===typeof h}function Fa(h){h.indexOf||(h.indexOf=fb);return h}function gb(h){if(U.fSDec)h[ja("`eeDwdouMhrudods")](ja("e`u`@ohl`uhnoHuds`uhnoDoe"),
function(){U._fTWm&&U._fTWm(h)})}function Za(h,p,D){D=D||"normal";var r=h+"_"+p+"_"+D,u=$a[r];if(isNaN(u)){try{h="position:absolute; left:0px; top:-20000px; padding:0px;margin:0px;border:none;white-space:pre;line-height:normal;font-family:"+h+"; font-size:"+p+"px; font-weight:"+D+";";if(!xa){var H=document.body;xa=document.createElement("span");xa.innerHTML="";var F=document.createTextNode("Mpgyi");xa.appendChild(F);H.appendChild(xa)}xa.style.display="";xa.setAttribute("style",h);u=Math.round(xa.offsetHeight);
xa.style.display="none"}catch(z){u=Math.ceil(1.1*p)}u=Math.max(u,p);$a[r]=u}return u}function R(h,p){var D=[];if(D={solid:[],shortDash:[3,1],shortDot:[1,1],shortDashDot:[3,1,1,1],shortDashDotDot:[3,1,1,1,1,1],dot:[1,2],dash:[4,2],dashDot:[4,2,1,2],longDash:[8,2],longDashDot:[8,2,1,2],longDashDotDot:[8,2,1,2,1,2]}[h||"solid"])for(var r=0;r<D.length;r++)D[r]*=p;else D=[];return D}function O(h,p,D,r,u){r=r||[];u=u||!1;r.push([h,p,D,u]);return h.addEventListener?(h.addEventListener(p,D,u),D):h.attachEvent?
(r=function(p){p=p||window.event;p.preventDefault=p.preventDefault||function(){p.returnValue=!1};p.stopPropagation=p.stopPropagation||function(){p.cancelBubble=!0};D.call(h,p)},h.attachEvent("on"+p,r),r):!1}function ab(h,p,D){h*=W;p*=W;h=D.getImageData(h,p,2,2).data;p=!0;for(D=0;4>D;D++)if(h[D]!==h[D+4]|h[D]!==h[D+8]|h[D]!==h[D+12]){p=!1;break}return p?h[0]<<16|h[1]<<8|h[2]:0}function na(h,p,D){return h in p?p[h]:D[h]}function Oa(h,p,D){if(r&&bb){var u=h.getContext("2d");Pa=u.webkitBackingStorePixelRatio||
u.mozBackingStorePixelRatio||u.msBackingStorePixelRatio||u.oBackingStorePixelRatio||u.backingStorePixelRatio||1;W=Ua/Pa;h.width=p*W;h.height=D*W;Ua!==Pa&&(h.style.width=p+"px",h.style.height=D+"px",u.scale(W,W))}else h.width=p,h.height=D}function hb(h){if(!ib){var p=!1,D=!1;"undefined"===typeof ra.Chart.creditHref?(h.creditHref=ja("iuuqr;..b`ow`rkr/bnl."),h.creditText=ja("B`ow`rKR/bnl")):(p=h.updateOption("creditText"),D=h.updateOption("creditHref"));if(h.creditHref&&h.creditText){h._creditLink||
(h._creditLink=document.createElement("a"),h._creditLink.setAttribute("class","canvasjs-chart-credit"),h._creditLink.setAttribute("title","JavaScript Charts"),h._creditLink.setAttribute("style","outline:none;margin:0px;position:absolute;right:2px;top:"+(h.height-14)+"px;color:dimgrey;text-decoration:none;font-size:11px;font-family: Calibri, Lucida Grande, Lucida Sans Unicode, Arial, sans-serif"),h._creditLink.setAttribute("tabIndex",-1),h._creditLink.setAttribute("target","_blank"));if(0===h.renderCount||
p||D)h._creditLink.setAttribute("href",h.creditHref),h._creditLink.innerHTML=h.creditText;h._creditLink&&h.creditHref&&h.creditText?(h._creditLink.parentElement||h._canvasJSContainer.appendChild(h._creditLink),h._creditLink.style.top=h.height-14+"px"):h._creditLink.parentElement&&h._canvasJSContainer.removeChild(h._creditLink)}}}function ta(h,p){Ja&&(this.canvasCount|=0,window.console.log(++this.canvasCount));var D=document.createElement("canvas");D.setAttribute("class","canvasjs-chart-canvas");Oa(D,
h,p);r||"undefined"===typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(D);return D}function sa(h,p,D){for(var r in D)p.style[r]=D[r]}function ua(h,p,D){p.getAttribute("state")||(p.style.backgroundColor=h.toolbar.backgroundColor,p.style.color=h.toolbar.fontColor,p.style.border="none",sa(h,p,{WebkitUserSelect:"none",MozUserSelect:"none",msUserSelect:"none",userSelect:"none"}));p.getAttribute("state")!==D&&(p.setAttribute("state",D),p.setAttribute("type","button"),sa(h,p,{padding:"5px 12px",
cursor:"pointer","float":"left",width:"40px",height:"25px",outline:"0px",verticalAlign:"baseline",lineHeight:"0"}),p.setAttribute("title",h._cultureInfo[D+"Text"]),p.innerHTML="<img style='height:95%; pointer-events: none;' src='"+jb[D].image+"' alt='"+h._cultureInfo[D+"Text"]+"' />")}function Qa(){for(var h=null,p=0;p<arguments.length;p++)h=arguments[p],h.style&&(h.style.display="inline")}function va(){for(var h=null,p=0;p<arguments.length;p++)(h=arguments[p])&&h.style&&(h.style.display="none")}
function V(h,p,D,r,v){this._defaultsKey=h;this._themeOptionsKey=p;this._index=r;this.parent=v;this._eventListeners=[];h={};this.theme&&u(p)&&u(r)?h=u(ya[this.theme])?ya.light1:ya[this.theme]:this.parent&&(this.parent.themeOptions&&this.parent.themeOptions[p])&&(null===r?h=this.parent.themeOptions[p]:0<this.parent.themeOptions[p].length&&(r=Math.min(this.parent.themeOptions[p].length-1,r),h=this.parent.themeOptions[p][r]));this.themeOptions=h;this.options=D?D:{_isPlaceholder:!0};this.setOptions(this.options,
h)}function Ga(h,p,r,u,v){"undefined"===typeof v&&(v=0);this._padding=v;this._x1=h;this._y1=p;this._x2=r;this._y2=u;this._rightOccupied=this._leftOccupied=this._bottomOccupied=this._topOccupied=this._padding}function ka(h,p){ka.base.constructor.call(this,"TextBlock",null,p,null,null);this.ctx=h;this._isDirty=!0;this._wrappedText=null;this._initialize()}function Va(h,p){Va.base.constructor.call(this,"Toolbar","toolbar",p,null,h);this.chart=h;this.canvas=h.canvas;this.ctx=this.chart.ctx;this.optionsName=
"toolbar"}function Aa(h,p){Aa.base.constructor.call(this,"Title","title",p,null,h);this.chart=h;this.canvas=h.canvas;this.ctx=this.chart.ctx;this.optionsName="title";if(u(this.options.margin)&&h.options.subtitles)for(var r=h.options.subtitles,za=0;za<r.length;za++)if((u(r[za].horizontalAlign)&&"center"===this.horizontalAlign||r[za].horizontalAlign===this.horizontalAlign)&&(u(r[za].verticalAlign)&&"top"===this.verticalAlign||r[za].verticalAlign===this.verticalAlign)&&!r[za].dockInsidePlotArea===!this.dockInsidePlotArea){this.margin=
0;break}"undefined"===typeof this.options.fontSize&&(this.fontSize=this.chart.getAutoFontSize(this.fontSize));this.height=this.width=null;this.bounds={x1:null,y1:null,x2:null,y2:null}}function Ka(h,p,r){Ka.base.constructor.call(this,"Subtitle","subtitles",p,r,h);this.chart=h;this.canvas=h.canvas;this.ctx=this.chart.ctx;this.optionsName="subtitles";this.isOptionsInArray=!0;"undefined"===typeof this.options.fontSize&&(this.fontSize=this.chart.getAutoFontSize(this.fontSize));this.height=this.width=null;
this.bounds={x1:null,y1:null,x2:null,y2:null}}function Wa(){this.pool=[]}function La(h){var p;h&&Ma[h]&&(p=Ma[h]);La.base.constructor.call(this,"CultureInfo",null,p,null,null)}var Ja=!1,U={},r=!!document.createElement("canvas").getContext,ra={Chart:{width:500,height:400,zoomEnabled:!1,zoomType:"x",backgroundColor:"white",theme:"light1",animationEnabled:!1,animationDuration:1200,dataPointWidth:null,dataPointMinWidth:null,dataPointMaxWidth:null,colorSet:"colorSet1",culture:"en",creditHref:"",creditText:"CanvasJS",
interactivityEnabled:!0,exportEnabled:!1,exportFileName:"Chart",rangeChanging:null,rangeChanged:null,publicProperties:{title:"readWrite",subtitles:"readWrite",toolbar:"readWrite",toolTip:"readWrite",legend:"readWrite",axisX:"readWrite",axisY:"readWrite",axisX2:"readWrite",axisY2:"readWrite",data:"readWrite",options:"readWrite",bounds:"readOnly",container:"readOnly"}},Title:{padding:0,text:null,verticalAlign:"top",horizontalAlign:"center",fontSize:20,fontFamily:"Calibri",fontWeight:"normal",fontColor:"black",
fontStyle:"normal",borderThickness:0,borderColor:"black",cornerRadius:0,backgroundColor:r?"transparent":null,margin:5,wrap:!0,maxWidth:null,dockInsidePlotArea:!1,publicProperties:{options:"readWrite",bounds:"readOnly",chart:"readOnly"}},Subtitle:{padding:0,text:null,verticalAlign:"top",horizontalAlign:"center",fontSize:14,fontFamily:"Calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",borderThickness:0,borderColor:"black",cornerRadius:0,backgroundColor:null,margin:2,wrap:!0,maxWidth:null,
dockInsidePlotArea:!1,publicProperties:{options:"readWrite",bounds:"readOnly",chart:"readOnly"}},Toolbar:{backgroundColor:"white",backgroundColorOnHover:"#2196f3",borderColor:"#2196f3",borderThickness:1,fontColor:"black",fontColorOnHover:"white",publicProperties:{options:"readWrite",chart:"readOnly"}},Legend:{name:null,verticalAlign:"center",horizontalAlign:"right",fontSize:14,fontFamily:"calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",cursor:null,itemmouseover:null,itemmouseout:null,
itemmousemove:null,itemclick:null,dockInsidePlotArea:!1,reversed:!1,backgroundColor:r?"transparent":null,borderColor:r?"transparent":null,borderThickness:0,cornerRadius:0,maxWidth:null,maxHeight:null,markerMargin:null,itemMaxWidth:null,itemWidth:null,itemWrap:!0,itemTextFormatter:null,publicProperties:{options:"readWrite",bounds:"readOnly",chart:"readOnly"}},ToolTip:{enabled:!0,shared:!1,animationEnabled:!0,content:null,contentFormatter:null,reversed:!1,backgroundColor:r?"rgba(255,255,255,.9)":"rgb(255,255,255)",
borderColor:null,borderThickness:2,cornerRadius:5,fontSize:14,fontColor:"black",fontFamily:"Calibri, Arial, Georgia, serif;",fontWeight:"normal",fontStyle:"italic",publicProperties:{options:"readWrite",chart:"readOnly"}},Axis:{minimum:null,maximum:null,viewportMinimum:null,viewportMaximum:null,interval:null,intervalType:null,reversed:!1,logarithmic:!1,logarithmBase:10,title:null,titleFontColor:"black",titleFontSize:20,titleFontFamily:"arial",titleFontWeight:"normal",titleFontStyle:"normal",titleWrap:!0,
titleMaxWidth:null,titleBackgroundColor:r?"transparent":null,titleBorderColor:r?"transparent":null,titleBorderThickness:0,titleCornerRadius:0,labelAngle:0,labelFontFamily:"arial",labelFontColor:"black",labelFontSize:12,labelFontWeight:"normal",labelFontStyle:"normal",labelAutoFit:!0,labelWrap:!0,labelMaxWidth:null,labelFormatter:null,labelBackgroundColor:r?"transparent":null,labelBorderColor:r?"transparent":null,labelBorderThickness:0,labelCornerRadius:0,labelPlacement:"outside",prefix:"",suffix:"",
includeZero:!0,tickLength:5,tickColor:"black",tickThickness:1,lineColor:"black",lineThickness:1,lineDashType:"solid",gridColor:"A0A0A0",gridThickness:0,gridDashType:"solid",interlacedColor:r?"transparent":null,valueFormatString:null,margin:2,publicProperties:{options:"readWrite",stripLines:"readWrite",scaleBreaks:"readWrite",crosshair:"readWrite",bounds:"readOnly",chart:"readOnly"}},StripLine:{value:null,startValue:null,endValue:null,color:"orange",opacity:null,thickness:2,lineDashType:"solid",label:"",
labelPlacement:"inside",labelAlign:"far",labelWrap:!0,labelMaxWidth:null,labelBackgroundColor:null,labelBorderColor:r?"transparent":null,labelBorderThickness:0,labelCornerRadius:0,labelFontFamily:"arial",labelFontColor:"orange",labelFontSize:12,labelFontWeight:"normal",labelFontStyle:"normal",labelFormatter:null,showOnTop:!1,publicProperties:{options:"readWrite",axis:"readOnly",bounds:"readOnly",chart:"readOnly"}},ScaleBreaks:{autoCalculate:!1,collapsibleThreshold:"25%",maxNumberOfAutoBreaks:2,spacing:8,
type:"straight",color:"#FFFFFF",fillOpacity:0.9,lineThickness:2,lineColor:"#E16E6E",lineDashType:"solid",publicProperties:{options:"readWrite",customBreaks:"readWrite",axis:"readOnly",autoBreaks:"readOnly",bounds:"readOnly",chart:"readOnly"}},Break:{startValue:null,endValue:null,spacing:8,type:"straight",color:"#FFFFFF",fillOpacity:0.9,lineThickness:2,lineColor:"#E16E6E",lineDashType:"solid",publicProperties:{options:"readWrite",scaleBreaks:"readOnly",bounds:"readOnly",chart:"readOnly"}},Crosshair:{enabled:!1,
snapToDataPoint:!1,color:"grey",opacity:null,thickness:2,lineDashType:"solid",label:"",labelWrap:!0,labelMaxWidth:null,labelBackgroundColor:r?"grey":null,labelBorderColor:r?"grey":null,labelBorderThickness:0,labelCornerRadius:0,labelFontFamily:r?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontSize:12,labelFontColor:"#fff",labelFontWeight:"normal",labelFontStyle:"normal",labelFormatter:null,valueFormatString:null,publicProperties:{options:"readWrite",axis:"readOnly",bounds:"readOnly",
chart:"readOnly"}},DataSeries:{name:null,dataPoints:null,label:"",bevelEnabled:!1,highlightEnabled:!0,cursor:"default",indexLabel:"",indexLabelPlacement:"auto",indexLabelOrientation:"horizontal",indexLabelFontColor:"black",indexLabelFontSize:12,indexLabelFontStyle:"normal",indexLabelFontFamily:"Arial",indexLabelFontWeight:"normal",indexLabelBackgroundColor:null,indexLabelLineColor:"gray",indexLabelLineThickness:1,indexLabelLineDashType:"solid",indexLabelMaxWidth:null,indexLabelWrap:!0,indexLabelFormatter:null,
lineThickness:2,lineDashType:"solid",connectNullData:!1,nullDataLineDashType:"dash",color:null,lineColor:null,risingColor:"white",fallingColor:"red",fillOpacity:null,startAngle:0,radius:null,innerRadius:null,neckHeight:null,neckWidth:null,reversed:!1,valueRepresents:null,linkedDataSeriesIndex:null,whiskerThickness:2,whiskerDashType:"solid",whiskerColor:null,whiskerLength:null,stemThickness:2,stemColor:null,stemDashType:"solid",upperBoxColor:"white",lowerBoxColor:"white",type:"column",xValueType:"number",
axisXType:"primary",axisYType:"primary",axisXIndex:0,axisYIndex:0,xValueFormatString:null,yValueFormatString:null,zValueFormatString:null,percentFormatString:null,showInLegend:null,legendMarkerType:null,legendMarkerColor:null,legendText:null,legendMarkerBorderColor:r?"transparent":null,legendMarkerBorderThickness:0,markerType:"circle",markerColor:null,markerSize:null,markerBorderColor:r?"transparent":null,markerBorderThickness:0,mouseover:null,mouseout:null,mousemove:null,click:null,toolTipContent:null,
visible:!0,publicProperties:{options:"readWrite",axisX:"readWrite",axisY:"readWrite",chart:"readOnly"}},TextBlock:{x:0,y:0,width:null,height:null,maxWidth:null,maxHeight:null,padding:0,angle:0,text:"",horizontalAlign:"center",fontSize:12,fontFamily:"calibri",fontWeight:"normal",fontColor:"black",fontStyle:"normal",borderThickness:0,borderColor:"black",cornerRadius:0,backgroundColor:null,textBaseline:"top"},CultureInfo:{decimalSeparator:".",digitGroupSeparator:",",zoomText:"Zoom",panText:"Pan",resetText:"Reset",
menuText:"More Options",saveJPGText:"Save as JPEG",savePNGText:"Save as PNG",printText:"Print",days:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),shortDays:"Sun Mon Tue Wed Thu Fri Sat".split(" "),months:"January February March April May June July August September October November December".split(" "),shortMonths:"Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" ")}},Ma={en:{}},v=r?"Trebuchet MS, Helvetica, sans-serif":"Arial",Ha=r?"Impact, Charcoal, sans-serif":"Arial",
Ba={colorSet1:"#4F81BC #C0504E #9BBB58 #23BFAA #8064A1 #4AACC5 #F79647 #7F6084 #77A033 #33558B #E59566".split(" "),colorSet2:"#6D78AD #51CDA0 #DF7970 #4C9CA0 #AE7D99 #C9D45C #5592AD #DF874D #52BCA8 #8E7AA3 #E3CB64 #C77B85 #C39762 #8DD17E #B57952 #FCC26C".split(" "),colorSet3:"#8CA1BC #36845C #017E82 #8CB9D0 #708C98 #94838D #F08891 #0366A7 #008276 #EE7757 #E5BA3A #F2990B #03557B #782970".split(" ")},I,fa,Q,ha,ga;fa="#333333";Q="#000000";I="#666666";ga=ha="#000000";var X=20,E=14,Xa={colorSet:"colorSet1",
backgroundColor:"#FFFFFF",title:{fontFamily:Ha,fontSize:32,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5},subtitles:[{fontFamily:Ha,fontSize:E,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5}],data:[{indexLabelFontFamily:v,indexLabelFontSize:E,indexLabelFontColor:fa,indexLabelFontWeight:"normal",indexLabelLineThickness:1}],axisX:[{titleFontFamily:v,titleFontSize:X,titleFontColor:fa,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",
lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisX2:[{titleFontFamily:v,
titleFontSize:X,titleFontColor:fa,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,
lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisY:[{titleFontFamily:v,titleFontSize:X,titleFontColor:fa,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",
thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisY2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:fa,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,
gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],legend:{fontFamily:v,fontSize:14,fontColor:fa,fontWeight:"bold",verticalAlign:"bottom",
horizontalAlign:"center"},toolTip:{fontFamily:v,fontSize:14,fontStyle:"normal",cornerRadius:0,borderThickness:1}};Q=fa="#F5F5F5";I="#FFFFFF";ha="#40BAF1";ga="#F5F5F5";var X=20,E=14,cb={colorSet:"colorSet2",title:{fontFamily:v,fontSize:33,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"top",margin:5},subtitles:[{fontFamily:v,fontSize:E,fontColor:"#3A3A3A",fontWeight:"normal",verticalAlign:"top",margin:5}],data:[{indexLabelFontFamily:v,indexLabelFontSize:E,indexLabelFontColor:"#666666",indexLabelFontWeight:"normal",
indexLabelLineThickness:1}],axisX:[{titleFontFamily:v,titleFontSize:X,titleFontColor:"#666666",titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:"#666666",labelFontWeight:"normal",lineThickness:1,lineColor:"#BBBBBB",tickThickness:1,tickColor:"#BBBBBB",gridThickness:1,gridColor:"#BBBBBB",stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FFA500",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FFA500",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,
labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:"black",color:"black",thickness:1,lineDashType:"dot"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisX2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:"#666666",titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:"#666666",labelFontWeight:"normal",lineThickness:1,lineColor:"#BBBBBB",tickColor:"#BBBBBB",tickThickness:1,gridThickness:1,gridColor:"#BBBBBB",
stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FFA500",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FFA500",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:"black",color:"black",thickness:1,lineDashType:"dot"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisY:[{titleFontFamily:v,titleFontSize:X,titleFontColor:"#666666",titleFontWeight:"normal",
labelFontFamily:v,labelFontSize:E,labelFontColor:"#666666",labelFontWeight:"normal",lineThickness:0,lineColor:"#BBBBBB",tickColor:"#BBBBBB",tickThickness:1,gridThickness:1,gridColor:"#BBBBBB",stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FFA500",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FFA500",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:"black",color:"black",thickness:1,lineDashType:"dot"},
scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],axisY2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:"#666666",titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:"#666666",labelFontWeight:"normal",lineThickness:0,lineColor:"#BBBBBB",tickColor:"#BBBBBB",tickThickness:1,gridThickness:1,gridColor:"#BBBBBB",stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FFA500",labelFontWeight:"normal",labelBackgroundColor:null,
color:"#FFA500",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#EEEEEE",labelFontWeight:"normal",labelBackgroundColor:"black",color:"black",thickness:1,lineDashType:"dot"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#BBBBBB",lineThickness:1,lineDashType:"solid"}}],legend:{fontFamily:v,fontSize:14,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"bottom",horizontalAlign:"center"},toolTip:{fontFamily:v,fontSize:14,fontStyle:"normal",cornerRadius:0,borderThickness:1}};
Q=fa="#F5F5F5";I="#FFFFFF";ha="#40BAF1";ga="#F5F5F5";X=20;E=14;Ha={colorSet:"colorSet12",backgroundColor:"#2A2A2A",title:{fontFamily:Ha,fontSize:32,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5},subtitles:[{fontFamily:Ha,fontSize:E,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5}],toolbar:{backgroundColor:"#666666",backgroundColorOnHover:"#FF7372",borderColor:"#FF7372",borderThickness:1,fontColor:"#F5F5F5",fontColorOnHover:"#F5F5F5"},data:[{indexLabelFontFamily:v,indexLabelFontSize:E,
indexLabelFontColor:Q,indexLabelFontWeight:"normal",indexLabelLineThickness:1}],axisX:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,
labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisX2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,
labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisY:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,
labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",
lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisY2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",
labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],legend:{fontFamily:v,fontSize:14,fontColor:fa,fontWeight:"bold",verticalAlign:"bottom",horizontalAlign:"center"},toolTip:{fontFamily:v,fontSize:14,fontStyle:"normal",cornerRadius:0,borderThickness:1,fontColor:Q,backgroundColor:"rgba(0, 0, 0, .7)"}};I="#FFFFFF";Q=fa="#FAFAFA";ha="#40BAF1";ga="#F5F5F5";
var X=20,E=14,ya={light1:Xa,light2:cb,dark1:Ha,dark2:{colorSet:"colorSet2",backgroundColor:"#32373A",title:{fontFamily:v,fontSize:32,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5},subtitles:[{fontFamily:v,fontSize:E,fontColor:fa,fontWeight:"normal",verticalAlign:"top",margin:5}],toolbar:{backgroundColor:"#666666",backgroundColorOnHover:"#FF7372",borderColor:"#FF7372",borderThickness:1,fontColor:"#F5F5F5",fontColorOnHover:"#F5F5F5"},data:[{indexLabelFontFamily:v,indexLabelFontSize:E,
indexLabelFontColor:Q,indexLabelFontWeight:"normal",indexLabelLineThickness:1}],axisX:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,
labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisX2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:1,lineColor:I,tickThickness:1,tickColor:I,gridThickness:0,gridColor:I,stripLines:[{labelFontFamily:v,
labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisY:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,
labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:0,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",
lineThickness:1,lineDashType:"solid",color:"#111111"}}],axisY2:[{titleFontFamily:v,titleFontSize:X,titleFontColor:Q,titleFontWeight:"normal",labelFontFamily:v,labelFontSize:E,labelFontColor:Q,labelFontWeight:"normal",lineThickness:0,lineColor:I,tickThickness:1,tickColor:I,gridThickness:1,gridColor:I,stripLines:[{labelFontFamily:v,labelFontSize:E,labelFontColor:"#FF7300",labelFontWeight:"normal",labelBackgroundColor:null,color:"#FF7300",thickness:1}],crosshair:{labelFontFamily:v,labelFontSize:E,labelFontColor:"#000000",
labelFontWeight:"normal",labelBackgroundColor:ga,color:ha,thickness:1,lineDashType:"dash"},scaleBreaks:{type:"zigzag",spacing:"2%",lineColor:"#777777",lineThickness:1,lineDashType:"solid",color:"#111111"}}],legend:{fontFamily:v,fontSize:14,fontColor:fa,fontWeight:"bold",verticalAlign:"bottom",horizontalAlign:"center"},toolTip:{fontFamily:v,fontSize:14,fontStyle:"normal",cornerRadius:0,borderThickness:1,fontColor:Q,backgroundColor:"rgba(0, 0, 0, .7)"}},theme1:Xa,theme2:cb,theme3:Xa},S={numberDuration:1,
yearDuration:314496E5,monthDuration:2592E6,weekDuration:6048E5,dayDuration:864E5,hourDuration:36E5,minuteDuration:6E4,secondDuration:1E3,millisecondDuration:1,dayOfWeekFromInt:"Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" ")};(function(){U.fSDec=function(h){for(var p="",r=0;r<h.length;r++)p+=String.fromCharCode(Math.ceil(h.length/57/5)^h.charCodeAt(r));return p};U.obj={trVs:"Ush`m!Wdsrhno",fntStr:"qy!B`mhcsh-!Mtbhe`!Fs`oed-!Mtbhe`!R`or!Tohbned-!@sh`m-!r`or,rdshg",txtBl:"udyuC`rdmhod",
fnt:"gnou",fSy:"ghmmRuxmd",fTx:"ghmmUdyu",grClr:"fsdx",cntx:"buy",tp:"unq"};delete ra[U.fSDec("Bi`su")][U.fSDec("bsdehuIsdg")];U.pro={sCH:ra[U.fSDec("Bi`su")][U.fSDec("bsdehuIsdg")]};U._fTWm=function(h){if("undefined"===typeof U.pro.sCH&&!db)try{var p=h[U.fSDec(U.obj.cntx)];p[U.fSDec(U.obj.txtBl)]=U.fSDec(U.obj.tp);p[U.fSDec(U.obj.fnt)]=11+U.fSDec(U.obj.fntStr);p[U.fSDec(U.obj.fSy)]=U.fSDec(U.obj.grClr);p[U.fSDec(U.obj.fTx)](U.fSDec(U.obj.trVs),2,h.height-11-2)}catch(r){}}})();var $a={},xa=null,kb=
function(){this.ctx.clearRect(0,0,this.width,this.height);this.backgroundColor&&(this.ctx.fillStyle=this.backgroundColor,this.ctx.fillRect(0,0,this.width,this.height))},lb=function(h,p,r){p=Math.min(this.width,this.height);return Math.max("theme4"===this.theme?0:300<=p?12:10,Math.round(p*(h/400)))},Ca=function(){var h=/D{1,4}|M{1,4}|Y{1,4}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|f{1,3}|t{1,2}|T{1,2}|K|z{1,3}|"[^"]*"|'[^']*'/g,p="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),r="Sun Mon Tue Wed Thu Fri Sat".split(" "),
u="January February March April May June July August September October November December".split(" "),v="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),H=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,F=/[^-+\dA-Z]/g;return function(z,E,L){var R=L?L.days:p,I=L?L.months:u,N=L?L.shortDays:r,O=L?L.shortMonths:v;L="";var S=!1;z=z&&z.getTime?z:z?new Date(z):new Date;if(isNaN(z))throw SyntaxError("invalid date");
"UTC:"===E.slice(0,4)&&(E=E.slice(4),S=!0);L=S?"getUTC":"get";var U=z[L+"Date"](),V=z[L+"Day"](),M=z[L+"Month"](),Q=z[L+"FullYear"](),a=z[L+"Hours"](),d=z[L+"Minutes"](),b=z[L+"Seconds"](),c=z[L+"Milliseconds"](),e=S?0:z.getTimezoneOffset();return L=E.replace(h,function(g){switch(g){case "D":return U;case "DD":return $(U,2);case "DDD":return N[V];case "DDDD":return R[V];case "M":return M+1;case "MM":return $(M+1,2);case "MMM":return O[M];case "MMMM":return I[M];case "Y":return parseInt(String(Q).slice(-2));
case "YY":return $(String(Q).slice(-2),2);case "YYY":return $(String(Q).slice(-3),3);case "YYYY":return $(Q,4);case "h":return a%12||12;case "hh":return $(a%12||12,2);case "H":return a;case "HH":return $(a,2);case "m":return d;case "mm":return $(d,2);case "s":return b;case "ss":return $(b,2);case "f":return String(c).slice(0,1);case "ff":return $(String(c).slice(0,2),2);case "fff":return $(String(c).slice(0,3),3);case "t":return 12>a?"a":"p";case "tt":return 12>a?"am":"pm";case "T":return 12>a?"A":
"P";case "TT":return 12>a?"AM":"PM";case "K":return S?"UTC":(String(z).match(H)||[""]).pop().replace(F,"");case "z":return(0<e?"-":"+")+Math.floor(Math.abs(e)/60);case "zz":return(0<e?"-":"+")+$(Math.floor(Math.abs(e)/60),2);case "zzz":return(0<e?"-":"+")+$(Math.floor(Math.abs(e)/60),2)+$(Math.abs(e)%60,2);default:return g.slice(1,g.length-1)}})}}(),ba=function(h,p,r){if(null===h)return"";if(!isFinite(h))return h;h=Number(h);var u=0>h?!0:!1;u&&(h*=-1);var v=r?r.decimalSeparator:".",H=r?r.digitGroupSeparator:
",",F="";p=String(p);var F=1,z=r="",E=-1,L=[],R=[],I=0,N=0,S=0,O=!1,U=0,z=p.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|\u2030|./g);p=null;for(var Q=0;z&&Q<z.length;Q++)if(p=z[Q],"."===p&&0>E)E=Q;else{if("%"===p)F*=100;else if("\u2030"===p){F*=1E3;continue}else if(","===p[0]&&"."===p[p.length-1]){F/=Math.pow(1E3,p.length-1);E=Q+p.length-1;continue}else"E"!==p[0]&&"e"!==p[0]||"0"!==p[p.length-1]||(O=!0);0>E?(L.push(p),"#"===p||"0"===p?I++:","===p&&S++):(R.push(p),"#"!==p&&"0"!==p||N++)}O&&(p=Math.floor(h),
z=-Math.floor(Math.log(h)/Math.LN10+1),U=0===h?0:0===p?-(I+z):String(p).length-I,F/=Math.pow(10,U));0>E&&(E=Q);F=(h*F).toFixed(N);p=F.split(".");F=(p[0]+"").split("");h=(p[1]+"").split("");F&&"0"===F[0]&&F.shift();for(O=z=Q=N=E=0;0<L.length;)if(p=L.pop(),"#"===p||"0"===p)if(E++,E===I){var M=F,F=[];if("0"===p)for(p=I-N-(M?M.length:0);0<p;)M.unshift("0"),p--;for(;0<M.length;)r=M.pop()+r,O++,0===O%z&&(Q===S&&0<M.length)&&(r=H+r)}else 0<F.length?(r=F.pop()+r,N++,O++):"0"===p&&(r="0"+r,N++,O++),0===O%
z&&(Q===S&&0<F.length)&&(r=H+r);else"E"!==p[0]&&"e"!==p[0]||"0"!==p[p.length-1]||!/[eE][+-]*[0]+/.test(p)?","===p?(Q++,z=O,O=0,0<F.length&&(r=H+r)):r=1<p.length&&('"'===p[0]&&'"'===p[p.length-1]||"'"===p[0]&&"'"===p[p.length-1])?p.slice(1,p.length-1)+r:p+r:(p=0>U?p.replace("+","").replace("-",""):p.replace("-",""),r+=p.replace(/[0]+/,function(h){return $(U,h.length)}));H="";for(L=!1;0<R.length;)p=R.shift(),"#"===p||"0"===p?0<h.length&&0!==Number(h.join(""))?(H+=h.shift(),L=!0):"0"===p&&(H+="0",L=
!0):1<p.length&&('"'===p[0]&&'"'===p[p.length-1]||"'"===p[0]&&"'"===p[p.length-1])?H+=p.slice(1,p.length-1):"E"!==p[0]&&"e"!==p[0]||"0"!==p[p.length-1]||!/[eE][+-]*[0]+/.test(p)?H+=p:(p=0>U?p.replace("+","").replace("-",""):p.replace("-",""),H+=p.replace(/[0]+/,function(h){return $(U,h.length)}));r+=(L?v:"")+H;return u?"-"+r:r},Ra=function(h){var p=0,r=0;h=h||window.event;h.offsetX||0===h.offsetX?(p=h.offsetX,r=h.offsetY):h.layerX||0==h.layerX?(p=h.layerX,r=h.layerY):(p=h.pageX-h.target.offsetLeft,
r=h.pageY-h.target.offsetTop);return{x:p,y:r}},bb=!0,Ua=window.devicePixelRatio||1,Pa=1,W=bb?Ua/Pa:1,ea=function(h,p,r,u,v,H,F,z,E,L,R,N,O){"undefined"===typeof O&&(O=1);F=F||0;z=z||"black";var I=15<u-p&&15<v-r?8:0.35*Math.min(u-p,v-r);h.beginPath();h.moveTo(p,r);h.save();h.fillStyle=H;h.globalAlpha=O;h.fillRect(p,r,u-p,v-r);h.globalAlpha=1;0<F&&(O=0===F%2?0:0.5,h.beginPath(),h.lineWidth=F,h.strokeStyle=z,h.moveTo(p,r),h.rect(p-O,r-O,u-p+2*O,v-r+2*O),h.stroke());h.restore();!0===E&&(h.save(),h.beginPath(),
h.moveTo(p,r),h.lineTo(p+I,r+I),h.lineTo(u-I,r+I),h.lineTo(u,r),h.closePath(),F=h.createLinearGradient((u+p)/2,r+I,(u+p)/2,r),F.addColorStop(0,H),F.addColorStop(1,"rgba(255, 255, 255, .4)"),h.fillStyle=F,h.fill(),h.restore());!0===L&&(h.save(),h.beginPath(),h.moveTo(p,v),h.lineTo(p+I,v-I),h.lineTo(u-I,v-I),h.lineTo(u,v),h.closePath(),F=h.createLinearGradient((u+p)/2,v-I,(u+p)/2,v),F.addColorStop(0,H),F.addColorStop(1,"rgba(255, 255, 255, .4)"),h.fillStyle=F,h.fill(),h.restore());!0===R&&(h.save(),
h.beginPath(),h.moveTo(p,r),h.lineTo(p+I,r+I),h.lineTo(p+I,v-I),h.lineTo(p,v),h.closePath(),F=h.createLinearGradient(p+I,(v+r)/2,p,(v+r)/2),F.addColorStop(0,H),F.addColorStop(1,"rgba(255, 255, 255, 0.1)"),h.fillStyle=F,h.fill(),h.restore());!0===N&&(h.save(),h.beginPath(),h.moveTo(u,r),h.lineTo(u-I,r+I),h.lineTo(u-I,v-I),h.lineTo(u,v),F=h.createLinearGradient(u-I,(v+r)/2,u,(v+r)/2),F.addColorStop(0,H),F.addColorStop(1,"rgba(255, 255, 255, 0.1)"),h.fillStyle=F,F.addColorStop(0,H),F.addColorStop(1,
"rgba(255, 255, 255, 0.1)"),h.fillStyle=F,h.fill(),h.closePath(),h.restore())},ja=function(h){for(var p="",r=0;r<h.length;r++)p+=String.fromCharCode(Math.ceil(h.length/57/5)^h.charCodeAt(r));return p},db=window&&window[ja("mnb`uhno")]&&window[ja("mnb`uhno")].href&&window[ja("mnb`uhno")].href.indexOf&&(-1!==window[ja("mnb`uhno")].href.indexOf(ja("b`ow`rkr/bnl"))||-1!==window[ja("mnb`uhno")].href.indexOf(ja("gdonqhy/bnl"))||-1!==window[ja("mnb`uhno")].href.indexOf(ja("gheemd"))),ib=db&&-1===window[ja("mnb`uhno")].href.indexOf(ja("gheemd")),
jb={reset:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAeCAYAAABJ/8wUAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAPjSURBVFhHxVdJaFNRFP1J/jwkP5MxsbaC1WJEglSxOFAXIsFpVRE3ggi1K90obioRRBA33XXnQnciirhQcMCdorgQxBkXWlREkFKsWkv5npvckp/XnzRpKh64kLw733fffe9L/wrL0+mVUdO8uTSZ3MBL/we2qg4rkuSpodCELstXE46ziVkLQ6FQcGOmeSSq6wd4aV50d3drWjj8kQKZJTUc9kxFGenv79dZrDksTSTWWJp2QYtEPiErysyzdX0LsxsCQR8keX8gs6RHIk8ysdgKFg2G53mhuOPsshTlBjKaFo1g7SqLNoShKLdFXT8huQ/paLSbxatYnc2mHMM4hr18Vi8TIvCmXF3vYrW6cF23gGTOk0M1wA4RKvOmq6vLZRVJipvmSWT6tZ6CSEYkco5V50VPT4+D7RwOqi6RiSZm0fJ+vggSqkeoypdsNmuyelNwbXsbgvkWYMtzDWNvWaijoyOBqE+hVK8abcssUeXQ/YfKyi0gFYv1Ipgfoj34fYGTJLOYJA0ODirok32GLN8XhUWCwSes1hIwBg6LydJ/tEeRRapAdUp+wSAiZchtZZWWgAZ+JNpD8peYXQVK9UwUxNpzOK8pq97kURZhYTCKBwPD7h2zK+js7Myi7D8Fod+0TkMI8+EMAngLGc/WtBFWawkFHFnoj/t9KLgGmF0B3QfkxC+EarxkdhnFYlFLY06USqUwL7UMjICHfh/wOc2sCqhpxGbCkLvL7EUDbF73+6DkmVWB6zi7xUDQSLeYvWjAILvm9zEnkJhlbRcDQZcv6Kg2AipyT/Axw6wKlqVSqxDdjF8Izfod13qURdrG/nxehY+xGh+h0CSzKygGvSNQIcc097BI24jb9hax6kj2E7OrMFX1il+ICEf2NrPbhiXLl+fYl+U7zK4iYdsDcyLGf+ofFlkwcN+s10KhmpuYhhtm0hCLVIFL0MDsqNlDIqy9x2CLs1jL6OvrI7vPRbtohXG6eFmsFnHDGAp6n9AgyuVySRZrGvROxRgIfLXhzjrNYnNBUxNX/dMgRWT1mt4XLDovaApD53E9W3ilNX5M55LJHpRtIsgAvciR4WWcgK2Dvb1YqgXevmF8z2zEBTcKG39EfSKsT9EbhVUaI2FZO+oZIqImxol6j66/hcAu4sSN4vc1ZPoKeoE6RGhYL2YYA+ymOSSi0Z0wWntbtkGUWCvfSDXIxONraZ/FY90KUfNTpfC5spnNLgxoYNnR9RO4F8ofXEHOgogCQE99w+fF2Xw+b7O59rEOsyRqGEfpVoaDMQQ1CZrG46bcM6AZ0C/wPqNfHliqejyTySxh9TqQpL+xmbIlkB9SlAAAAABJRU5ErkJggg=="},
pan:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAICSURBVEhLxZbPahNRGMUn/5MpuAiBEAIufQGfzr5E40YptBXajYzudCEuGqS+gGlrFwquDGRTutBdYfydzJ3LzeQmJGZue+Dw/Z17Mnfmu5Pof9Hr9Z61Wq0bWZMKj263O6xWq99wU9lOpzPMKgEhEcRucNOcioOK+0RzBhNvt9tPV4nmVF19+OWhVqt9xXgFXZq+8lCv119UKpUJ7iX2FmvFTKz8RH34YdBsNk8wVtjE4fGYwm8wrrDi3WBG5oKXZGRSS9hGuNFojLTe2lFz5xThWZIktayyiE2FdT3rzXBXz7krKiL8c17wAKFDjCus2AvW+YGZ9y2JF0VFRuMPfI//rsCE/C+s26s4gQu9ul7r4NteKx7H8XOC724xNNGbaNu++IrBqbOV7Tj3FgMRvc/YKOr3+3sE47wgEt/Bl/gaK5cHbNU11vYSXylfpK7XOvjuumPp4Wcoipu30Qsez2uMXYz4lfI+mOmwothY+SLiXJy7mKVpWs3Si0CoOMfeI9Od43Wic+jO+ZVv+crsm9QSNhUW9LXSeoPBYLXopthGuFQgdIxxhY+UDwlt1x5CZ1hX+NTUdt/OIvjKaDSmuOJfaIVNPKX+W18j/PLA2/kR44p5Sd8HbHngT/yTfNRWUXX14ZcL3wmX0+TLf8YO7CGT8yFE5zB3/gney25/OETRP9CtPDFe5jShAAAAAElFTkSuQmCC"},
zoom:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsMAAA7DAcdvqGQAAALWSURBVEhLvZZLaBNRFIabyftBIgEfqCCBoCC6MYqiXYiIj4U76U4X7sUHbhQhUBfixhZEUBDB16YuFERaUaQLK7ooCOJj4UKtYEFU0EptShO/A9Ph3js3k8lo/eHnP7n3nP/M3LlzMz1hkUwmNziOcyKRSFyFt+LxeD/c2Wq1Ym7Kv0M2m11Os1OxWGycn1OwZXCGuXfwIhezkd9/jRgNT2L4ldhs1pbkX5OLJe4euVxuGQaPCa3mnUjtJx7BDuKusJTCV6jVVGHTMuYRjxma7yIOhTgFY6jNaAKew2xPKpVay9ganmkvj+M448/MfJdT5K5Gg4HJacRngPFgqVRaRNwW1B4i7yehWfsEDdz1K+A01AoxPIqGAiuwGfkOTY8+1A6u7AyiFTB2Hu0KPIrdiOnzHLWDybeImvy+Wq2mZa5bUHsD0Zpz+KxHdWQymV6kAb1ElqeORgJLvgnRdj1+R1AfzkIvSUjxVjQSarVakrueIPT8+H1F5jSUy+WXiJrUYBVWyVxU4PEU8TzhfaijUqnMIWrjaY492eWRwdKOIqrnIxnXwLLeRLwk2GQzrEMjg0avEbXxkIxr4OoOImpj2QwyFgms1koa/SZUG8s+0iGnEhNfCNXEhzIXBVz0McTzEvJ+70P9oNFtxEzei3aFYrFYxmuSUPWSv9Yi9IMm2xE1We56Mp1OV4nDwqFmBDV9gk9AEh4gZtFHNt8W4kAUCoXF5MorY9Z/kDni9nDv7hc0i2fhgLvTtX8a99PoMPPagTFPxofRzmDJ9yM+AyEmTfgGysYbQcfhDzPPJDmX0c7gDg4gs9BqFIWhm/Nct5H8gtBq1I7UfIbtvmIuoaGQcp+fdpbbSM43eEH5wrwLbXmhm/fU63VHXjcuok7hEByFY/AeHGC8L5/PL3HT5xGH1uYwfPOICGo+CBcU0vwO1BqzUqILDl/z/9VYIMfpddiAc47jDP8BsUpb13wOLRwAAAAASUVORK5CYII="},
menu:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAeCAYAAABE4bxTAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADoSURBVFhH7dc9CsJAFATgRxIIBCwCqZKATX5sbawsY2MvWOtF9AB6AU8gguAJbD2AnZ2VXQT/Ko2TYGCL2OYtYQc+BuYA+1hCtnCVwMm27SGaXpDJIAiCvCkVR05hGOZNN3HkFMdx3nQRR06+76/R1IcFLJlNQEWlmWlBTwJtKLKHynehZqnjOGM0PYWRVXk61C37p7xlZ3Hk5HneCk1dmMH811xGoKLSzDiQwIBZB4ocoPJdqNkDt2yKlueWRVGUtzy3rPwo3sWRU3nLjuLI6OO67oZM00wMw3hrmpZx0XU9syxrR0T0BeMpb9dneSR2AAAAAElFTkSuQmCC"},
handle:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAAzwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7sqSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII="}};V.prototype.setOptions=function(h,p){if(ra[this._defaultsKey]){var r=ra[this._defaultsKey],u;for(u in r)"publicProperties"!==
u&&r.hasOwnProperty(u)&&(this[u]=h&&u in h?h[u]:p&&u in p?p[u]:r[u])}else Ja&&window.console&&console.log("defaults not set")};V.prototype.get=function(h){var p=ra[this._defaultsKey];if("options"===h)return this.options&&this.options._isPlaceholder?null:this.options;if(p.hasOwnProperty(h)||p.publicProperties&&p.publicProperties.hasOwnProperty(h))return this[h];window.console&&window.console.log('Property "'+h+"\" doesn't exist. Please check for typo.")};V.prototype.set=function(h,p,r){r="undefined"===
typeof r?!0:r;var u=ra[this._defaultsKey];if("options"===h)this.createUserOptions(p);else if(u.hasOwnProperty(h)||u.publicProperties&&u.publicProperties.hasOwnProperty(h)&&"readWrite"===u.publicProperties[h])this.options._isPlaceholder&&this.createUserOptions(),this.options[h]=p;else{window.console&&(u.publicProperties&&u.publicProperties.hasOwnProperty(h)&&"readOnly"===u.publicProperties[h]?window.console.log('Property "'+h+'" is read-only.'):window.console.log('Property "'+h+"\" doesn't exist. Please check for typo."));
return}r&&(this.stockChart||this.chart||this).render()};V.prototype.addTo=function(h,p,r,u){u="undefined"===typeof u?!0:u;var v=ra[this._defaultsKey];v.hasOwnProperty(h)||v.publicProperties&&v.publicProperties.hasOwnProperty(h)&&"readWrite"===v.publicProperties[h]?(this.options._isPlaceholder&&this.createUserOptions(),"undefined"===typeof this.options[h]&&(this.options[h]=[]),h=this.options[h],r="undefined"===typeof r||null===r?h.length:r,h.splice(r,0,p),u&&(this.chart||this).render()):window.console&&
(v.publicProperties&&v.publicProperties.hasOwnProperty(h)&&"readOnly"===v.publicProperties[h]?window.console.log('Property "'+h+'" is read-only.'):window.console.log('Property "'+h+"\" doesn't exist. Please check for typo."))};V.prototype.createUserOptions=function(h){if("undefined"!==typeof h||this.options._isPlaceholder)if(this.parent.options._isPlaceholder&&this.parent.createUserOptions(),this.isOptionsInArray){this.parent.options[this.optionsName]||(this.parent.options[this.optionsName]=[]);var p=
this.parent.options[this.optionsName],r=p.length;this.options._isPlaceholder||(Fa(p),r=p.indexOf(this.options));this.options="undefined"===typeof h?{}:h;p[r]=this.options}else this.options="undefined"===typeof h?{}:h,h=this.parent.options,this.optionsName?p=this.optionsName:(p=this._defaultsKey)&&0!==p.length?(r=p.charAt(0).toLowerCase(),1<p.length&&(r=r.concat(p.slice(1))),p=r):p=void 0,h[p]=this.options};V.prototype.remove=function(h){h="undefined"===typeof h?!0:h;if(this.isOptionsInArray){var p=
this.parent.options[this.optionsName];Fa(p);var r=p.indexOf(this.options);0<=r&&p.splice(r,1)}else delete this.parent.options[this.optionsName];h&&(this.chart||this).render()};V.prototype.updateOption=function(h){!ra[this._defaultsKey]&&(Ja&&window.console)&&console.log("defaults not set");var p=ra[this._defaultsKey],r={},v=this[h],E=this._themeOptionsKey,H=this._index;this.theme&&u(E)&&u(H)?r=u(ya[this.theme])?ya.light1:ya[this.theme]:this.parent&&(this.parent.themeOptions&&this.parent.themeOptions[E])&&
(null===H?r=this.parent.themeOptions[E]:0<this.parent.themeOptions[E].length&&(r=Math.min(this.parent.themeOptions[E].length-1,H),r=this.parent.themeOptions[E][r]));this.themeOptions=r;h in p&&(v=h in this.options?this.options[h]:r&&h in r?r[h]:p[h]);if(v===this[h])return!1;this[h]=v;return!0};V.prototype.trackChanges=function(h){if(!this.sessionVariables)throw"Session Variable Store not set";this.sessionVariables[h]=this.options[h]};V.prototype.isBeingTracked=function(h){this.options._oldOptions||
(this.options._oldOptions={});return this.options._oldOptions[h]?!0:!1};V.prototype.hasOptionChanged=function(h){if(!this.sessionVariables)throw"Session Variable Store not set";return this.sessionVariables[h]!==this.options[h]};V.prototype.addEventListener=function(h,p,r){h&&p&&(this._eventListeners[h]=this._eventListeners[h]||[],this._eventListeners[h].push({context:r||this,eventHandler:p}))};V.prototype.removeEventListener=function(h,p){if(h&&p&&this._eventListeners[h])for(var r=this._eventListeners[h],
u=0;u<r.length;u++)if(r[u].eventHandler===p){r[u].splice(u,1);break}};V.prototype.removeAllEventListeners=function(){this._eventListeners=[]};V.prototype.dispatchEvent=function(h,p,r){if(h&&this._eventListeners[h]){p=p||{};for(var u=this._eventListeners[h],v=0;v<u.length;v++)u[v].eventHandler.call(u[v].context,p)}"function"===typeof this[h]&&this[h].call(r||this.chart,p)};Ga.prototype.registerSpace=function(h,p){"top"===h?this._topOccupied+=p.height:"bottom"===h?this._bottomOccupied+=p.height:"left"===
h?this._leftOccupied+=p.width:"right"===h&&(this._rightOccupied+=p.width)};Ga.prototype.unRegisterSpace=function(h,p){"top"===h?this._topOccupied-=p.height:"bottom"===h?this._bottomOccupied-=p.height:"left"===h?this._leftOccupied-=p.width:"right"===h&&(this._rightOccupied-=p.width)};Ga.prototype.getFreeSpace=function(){return{x1:this._x1+this._leftOccupied,y1:this._y1+this._topOccupied,x2:this._x2-this._rightOccupied,y2:this._y2-this._bottomOccupied,width:this._x2-this._x1-this._rightOccupied-this._leftOccupied,
height:this._y2-this._y1-this._bottomOccupied-this._topOccupied}};Ga.prototype.reset=function(){this._rightOccupied=this._leftOccupied=this._bottomOccupied=this._topOccupied=this._padding};qa(ka,V);ka.prototype._initialize=function(){u(this.padding)||"object"!==typeof this.padding?this.topPadding=this.rightPadding=this.bottomPadding=this.leftPadding=Number(this.padding)|0:(this.topPadding=u(this.padding.top)?0:Number(this.padding.top)|0,this.rightPadding=u(this.padding.right)?0:Number(this.padding.right)|
0,this.bottomPadding=u(this.padding.bottom)?0:Number(this.padding.bottom)|0,this.leftPadding=u(this.padding.left)?0:Number(this.padding.left)|0)};ka.prototype.render=function(h){if(0!==this.fontSize){h&&this.ctx.save();var p=this.ctx.font;this.ctx.textBaseline=this.textBaseline;var r=0;this._isDirty&&this.measureText(this.ctx);this.ctx.translate(this.x,this.y+r);"middle"===this.textBaseline&&(r=-this._lineHeight/2);this.ctx.font=this._getFontString();this.ctx.rotate(Math.PI/180*this.angle);var u=
0,v=this.topPadding,H=null;this.ctx.roundRect||Ea(this.ctx);(0<this.borderThickness&&this.borderColor||this.backgroundColor)&&this.ctx.roundRect(0,r,this.width,this.height,this.cornerRadius,this.borderThickness,this.backgroundColor,this.borderColor);this.ctx.fillStyle=this.fontColor;for(r=0;r<this._wrappedText.lines.length;r++)H=this._wrappedText.lines[r],"right"===this.horizontalAlign?u=(this.width-(this.leftPadding+this.rightPadding))/2-H.width/2+this.leftPadding:"left"===this.horizontalAlign?u=
this.leftPadding:"center"===this.horizontalAlign&&(u=(this.width-(this.leftPadding+this.rightPadding))/2-H.width/2+this.leftPadding),this.ctx.fillText(H.text,u,v),v+=H.height;this.ctx.font=p;h&&this.ctx.restore()}};ka.prototype.setText=function(h){this.text=h;this._isDirty=!0;this._wrappedText=null};ka.prototype.measureText=function(){this._lineHeight=Za(this.fontFamily,this.fontSize,this.fontWeight);if(null===this.maxWidth)throw"Please set maxWidth and height for TextBlock";this._wrapText(this.ctx);
this._isDirty=!1;return{width:this.width,height:this.height}};ka.prototype._getLineWithWidth=function(h,p,r){h=String(h);if(!h)return{text:"",width:0};var u=r=0,v=h.length-1,H=Infinity;for(this.ctx.font=this._getFontString();u<=v;){var H=Math.floor((u+v)/2),F=h.substr(0,H+1);r=this.ctx.measureText(F).width;if(r<p)u=H+1;else if(r>p)v=H-1;else break}r>p&&1<F.length&&(F=F.substr(0,F.length-1),r=this.ctx.measureText(F).width);p=!0;if(F.length===h.length||" "===h[F.length])p=!1;p&&(h=F.split(" "),1<h.length&&
h.pop(),F=h.join(" "),r=this.ctx.measureText(F).width);return{text:F,width:r}};ka.prototype._wrapText=function(){var h=new String(Ia(String(this.text))),p=[],r=this.ctx.font,u=0,v=0;this.ctx.font=this._getFontString();if(0===this.frontSize)v=u=0;else for(;0<h.length;){var H=this.maxHeight-(this.topPadding+this.bottomPadding),F=this._getLineWithWidth(h,this.maxWidth-(this.leftPadding+this.rightPadding),!1);F.height=this._lineHeight;p.push(F);var z=v,v=Math.max(v,F.width),u=u+F.height,h=Ia(h.slice(F.text.length,
h.length));H&&u>H&&(F=p.pop(),u-=F.height,v=z)}this._wrappedText={lines:p,width:v,height:u};this.width=v+(this.leftPadding+this.rightPadding);this.height=u+(this.topPadding+this.bottomPadding);this.ctx.font=r};ka.prototype._getFontString=function(){var h;h=""+(this.fontStyle?this.fontStyle+" ":"");h+=this.fontWeight?this.fontWeight+" ":"";h+=this.fontSize?this.fontSize+"px ":"";var p=this.fontFamily?this.fontFamily+"":"";!r&&p&&(p=p.split(",")[0],"'"!==p[0]&&'"'!==p[0]&&(p="'"+p+"'"));return h+=p};
qa(Va,V);qa(Aa,V);Aa.prototype.setLayout=function(){if(this.text){var h=this.dockInsidePlotArea?this.chart.plotArea:this.chart,p=h.layoutManager.getFreeSpace(),r=p.x1,v=p.y1,E=0,H=0,F=this.chart._menuButton&&this.chart.exportEnabled&&"top"===this.verticalAlign?22:0,z,I;"top"===this.verticalAlign||"bottom"===this.verticalAlign?(null===this.maxWidth&&(this.maxWidth=p.width-4-F*("center"===this.horizontalAlign?2:1)),H=0.5*p.height-this.margin-2,E=0):"center"===this.verticalAlign&&("left"===this.horizontalAlign||
"right"===this.horizontalAlign?(null===this.maxWidth&&(this.maxWidth=p.height-4),H=0.5*p.width-this.margin-2):"center"===this.horizontalAlign&&(null===this.maxWidth&&(this.maxWidth=p.width-4),H=0.5*p.height-4));var L;u(this.padding)||"number"!==typeof this.padding?u(this.padding)||"object"!==typeof this.padding||(L=this.padding.top?this.padding.top:this.padding.bottom?this.padding.bottom:0,L+=this.padding.bottom?this.padding.bottom:this.padding.top?this.padding.top:0,L*=1.25):L=2.5*this.padding;this.wrap||
(H=Math.min(H,Math.max(1.5*this.fontSize,this.fontSize+L)));H=new ka(this.ctx,{fontSize:this.fontSize,fontFamily:this.fontFamily,fontColor:this.fontColor,fontStyle:this.fontStyle,fontWeight:this.fontWeight,horizontalAlign:this.horizontalAlign,verticalAlign:this.verticalAlign,borderColor:this.borderColor,borderThickness:this.borderThickness,backgroundColor:this.backgroundColor,maxWidth:this.maxWidth,maxHeight:H,cornerRadius:this.cornerRadius,text:this.text,padding:this.padding,textBaseline:"top"});
L=H.measureText();"top"===this.verticalAlign||"bottom"===this.verticalAlign?("top"===this.verticalAlign?(v=p.y1+2,I="top"):"bottom"===this.verticalAlign&&(v=p.y2-2-L.height,I="bottom"),"left"===this.horizontalAlign?r=p.x1+2:"center"===this.horizontalAlign?r=p.x1+p.width/2-L.width/2:"right"===this.horizontalAlign&&(r=p.x2-2-L.width-F),z=this.horizontalAlign,this.width=L.width,this.height=L.height):"center"===this.verticalAlign&&("left"===this.horizontalAlign?(r=p.x1+2,v=p.y2-2-(this.maxWidth/2-L.width/
2),E=-90,I="left",this.width=L.height,this.height=L.width):"right"===this.horizontalAlign?(r=p.x2-2,v=p.y1+2+(this.maxWidth/2-L.width/2),E=90,I="right",this.width=L.height,this.height=L.width):"center"===this.horizontalAlign&&(v=h.y1+(h.height/2-L.height/2),r=h.x1+(h.width/2-L.width/2),I="center",this.width=L.width,this.height=L.height),z="center");H.x=r;H.y=v;H.angle=E;H.horizontalAlign=z;this._textBlock=H;h.layoutManager.registerSpace(I,{width:this.width+("left"===I||"right"===I?this.margin+2:0),
height:this.height+("top"===I||"bottom"===I?this.margin+2:0)});this.bounds={x1:r,y1:v,x2:r+this.width,y2:v+this.height};this.ctx.textBaseline="top"}};Aa.prototype.render=function(){this._textBlock&&this._textBlock.render(!0)};qa(Ka,V);Ka.prototype.setLayout=Aa.prototype.setLayout;Ka.prototype.render=Aa.prototype.render;Wa.prototype.get=function(h,p){var r=null;0<this.pool.length?(r=this.pool.pop(),Oa(r,h,p)):r=ta(h,p);return r};Wa.prototype.release=function(h){this.pool.push(h)};qa(La,V);var Na={addTheme:function(h,
p){ya[h]=p},addColorSet:function(h,p){Ba[h]=p},addCultureInfo:function(h,p){Ma[h]=p},formatNumber:function(h,p,r){r=r||"en";if(Ma[r])return ba(h,p||"#,##0.##",new La(r));throw"Unknown Culture Name";},formatDate:function(h,p,r){r=r||"en";if(Ma[r])return Ca(h,p||"DD MMM YYYY",new La(r));throw"Unknown Culture Name";}};"undefined"!==typeof module&&"undefined"!==typeof module.exports?module.exports=Na:"function"===typeof define&&define.amd?define([],function(){return Na}):window.CanvasJS=Na;Na.Chart=function(){function h(a,
d){return a.x-d.x}function p(a,d){d=d||{};this.theme=u(d.theme)||u(ya[d.theme])?"light1":d.theme;p.base.constructor.call(this,"Chart",null,d,null,null);var b=this;this._containerId=a;this._objectsInitialized=!1;this.overlaidCanvasCtx=this.ctx=null;this._indexLabels=[];this._panTimerId=0;this._lastTouchEventType="";this._lastTouchData=null;this.isAnimating=!1;this.renderCount=0;this.disableToolTip=this.animatedRender=!1;this.canvasPool=new Wa;this.allDOMEventHandlers=[];this.panEnabled=!1;this._defaultCursor=
"default";this.plotArea={canvas:null,ctx:null,x1:0,y1:0,x2:0,y2:0,width:0,height:0};this._dataInRenderedOrder=[];if(this.container="string"===typeof this._containerId?document.getElementById(this._containerId):this._containerId){this.container.innerHTML="";var c=0,e=0,c=this.options.width?this.width:0<this.container.clientWidth?this.container.clientWidth:this.width,e=this.options.height?this.height:0<this.container.clientHeight?this.container.clientHeight:this.height;this.width=c;this.height=e;this.x1=
this.y1=0;this.x2=this.width;this.y2=this.height;this._selectedColorSet="undefined"!==typeof Ba[this.colorSet]?Ba[this.colorSet]:Ba.colorSet1;this._canvasJSContainer=document.createElement("div");this._canvasJSContainer.setAttribute("class","canvasjs-chart-container");this._canvasJSContainer.style.position="relative";this._canvasJSContainer.style.textAlign="left";this._canvasJSContainer.style.cursor="auto";r||(this._canvasJSContainer.style.height="0px");this.container.appendChild(this._canvasJSContainer);
this.canvas=ta(c,e);this._preRenderCanvas=ta(c,e);this.canvas.style.position="absolute";this.canvas.style.WebkitUserSelect="none";this.canvas.style.MozUserSelect="none";this.canvas.style.msUserSelect="none";this.canvas.style.userSelect="none";this.canvas.getContext&&(this._canvasJSContainer.appendChild(this.canvas),this.ctx=this.canvas.getContext("2d"),this.ctx.textBaseline="top",Ea(this.ctx),this._preRenderCtx=this._preRenderCanvas.getContext("2d"),this._preRenderCtx.textBaseline="top",Ea(this._preRenderCtx),
r?this.plotArea.ctx=this.ctx:(this.plotArea.canvas=ta(c,e),this.plotArea.canvas.style.position="absolute",this.plotArea.canvas.setAttribute("class","plotAreaCanvas"),this._canvasJSContainer.appendChild(this.plotArea.canvas),this.plotArea.ctx=this.plotArea.canvas.getContext("2d")),this.overlaidCanvas=ta(c,e),this.overlaidCanvas.style.position="absolute",this.overlaidCanvas.style.webkitTapHighlightColor="transparent",this.overlaidCanvas.style.WebkitUserSelect="none",this.overlaidCanvas.style.MozUserSelect=
"none",this.overlaidCanvas.style.msUserSelect="none",this.overlaidCanvas.style.userSelect="none",this.overlaidCanvas.getContext&&(this._canvasJSContainer.appendChild(this.overlaidCanvas),this.overlaidCanvasCtx=this.overlaidCanvas.getContext("2d"),this.overlaidCanvasCtx.textBaseline="top",Ea(this.overlaidCanvasCtx)),this._eventManager=new ha(this),this.windowResizeHandler=O(window,"resize",function(){b._updateSize()&&b.render()},this.allDOMEventHandlers),this._toolBar=document.createElement("div"),
this._toolBar.setAttribute("class","canvasjs-chart-toolbar"),this._toolBar.style.cssText="position: absolute; right: 1px; top: 1px;",this._canvasJSContainer.appendChild(this._toolBar),this.bounds={x1:0,y1:0,x2:this.width,y2:this.height},O(this.overlaidCanvas,"click",function(a){b._mouseEventHandler(a)},this.allDOMEventHandlers),O(this.overlaidCanvas,"mousemove",function(a){b._mouseEventHandler(a)},this.allDOMEventHandlers),O(this.overlaidCanvas,"mouseup",function(a){b._mouseEventHandler(a)},this.allDOMEventHandlers),
O(this.overlaidCanvas,"mousedown",function(a){b._mouseEventHandler(a);va(b._dropdownMenu)},this.allDOMEventHandlers),O(this.overlaidCanvas,"mouseout",function(a){b._mouseEventHandler(a)},this.allDOMEventHandlers),O(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerDown":"touchstart",function(a){b._touchEventHandler(a)},this.allDOMEventHandlers),O(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerMove":"touchmove",function(a){b._touchEventHandler(a)},this.allDOMEventHandlers),
O(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerUp":"touchend",function(a){b._touchEventHandler(a)},this.allDOMEventHandlers),O(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerCancel":"touchcancel",function(a){b._touchEventHandler(a)},this.allDOMEventHandlers),this.toolTip=new $(this,this.options.toolTip),this.data=null,this.axisX=[],this.axisX2=[],this.axisY=[],this.axisY2=[],this.sessionVariables={axisX:[],axisX2:[],axisY:[],axisY2:[]})}else window.console&&window.console.log('CanvasJS Error: Chart Container with id "'+
this._containerId+'" was not found')}function v(a,d){for(var b=[],c,e=0;e<a.length;e++)if(0==e)b.push(a[0]);else{var g,k,l;l=e-1;g=0===l?0:l-1;k=l===a.length-1?l:l+1;c=Math.abs((a[k].x-a[g].x)/(0===a[k].x-a[l].x?0.01:a[k].x-a[l].x))*(d-1)/2+1;var w=(a[k].x-a[g].x)/c;c=(a[k].y-a[g].y)/c;b[b.length]=a[l].x>a[g].x&&0<w||a[l].x<a[g].x&&0>w?{x:a[l].x+w/3,y:a[l].y+c/3}:{x:a[l].x,y:a[l].y+c/9};l=e;g=0===l?0:l-1;k=l===a.length-1?l:l+1;c=Math.abs((a[k].x-a[g].x)/(0===a[l].x-a[g].x?0.01:a[l].x-a[g].x))*(d-
1)/2+1;w=(a[k].x-a[g].x)/c;c=(a[k].y-a[g].y)/c;b[b.length]=a[l].x>a[g].x&&0<w||a[l].x<a[g].x&&0>w?{x:a[l].x-w/3,y:a[l].y-c/3}:{x:a[l].x,y:a[l].y-c/9};b[b.length]=a[e]}return b}function E(a,d,b,c,e,g,k,l,w,m){var s=0;m?(k.color=g,l.color=g):m=1;s=w?Math.abs(e-b):Math.abs(c-d);s=0<k.trimLength?Math.abs(s*k.trimLength/100):Math.abs(s-k.length);w?(b+=s/2,e-=s/2):(d+=s/2,c-=s/2);var s=1===Math.round(k.thickness)%2?0.5:0,q=1===Math.round(l.thickness)%2?0.5:0;a.save();a.globalAlpha=m;a.strokeStyle=l.color||
g;a.lineWidth=l.thickness||2;a.setLineDash&&a.setLineDash(R(l.dashType,l.thickness));a.beginPath();w&&0<l.thickness?(a.moveTo(c-k.thickness/2,Math.round((b+e)/2)-q),a.lineTo(d+k.thickness/2,Math.round((b+e)/2)-q)):0<l.thickness&&(a.moveTo(Math.round((d+c)/2)-q,b+k.thickness/2),a.lineTo(Math.round((d+c)/2)-q,e-k.thickness/2));a.stroke();a.strokeStyle=k.color||g;a.lineWidth=k.thickness||2;a.setLineDash&&a.setLineDash(R(k.dashType,k.thickness));a.beginPath();w&&0<k.thickness?(a.moveTo(c-s,b),a.lineTo(c-
s,e),a.moveTo(d+s,b),a.lineTo(d+s,e)):0<k.thickness&&(a.moveTo(d,b+s),a.lineTo(c,b+s),a.moveTo(d,e-s),a.lineTo(c,e-s));a.stroke();a.restore()}function I(a,d,b,c,e){if(null===a||"undefined"===typeof a)return"undefined"===typeof b?d:b;a=parseFloat(a.toString())*(0<=a.toString().indexOf("%")?d/100:1);"undefined"!==typeof c&&(a=Math.min(c,a),"undefined"!==typeof e&&(a=Math.max(e,a)));return!isNaN(a)&&a<=d&&0<=a?a:"undefined"===typeof b?d:b}function H(a,d){H.base.constructor.call(this,"Legend","legend",
d,null,a);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.ghostCtx=this.chart._eventManager.ghostCtx;this.items=[];this.optionsName="legend";this.height=this.width=0;this.orientation=null;this.dataSeries=[];this.bounds={x1:null,y1:null,x2:null,y2:null};"undefined"===typeof this.options.fontSize&&(this.fontSize=this.chart.getAutoFontSize(this.fontSize));this.lineHeight=Za(this.fontFamily,this.fontSize,this.fontWeight);this.horizontalSpacing=this.fontSize}function F(a,d,b,c){F.base.constructor.call(this,
"DataSeries","data",d,b,a);this.chart=a;this.canvas=a.canvas;this._ctx=a.canvas.ctx;this.index=b;this.noDataPointsInPlotArea=0;this.id=c;this.chart._eventManager.objectMap[c]={id:c,objectType:"dataSeries",dataSeriesIndex:b};a=d.dataPoints?d.dataPoints.length:0;this.dataPointEOs=[];for(d=0;d<a;d++)this.dataPointEOs[d]={};this.dataPointIds=[];this.plotUnit=[];this.axisY=this.axisX=null;this.optionsName="data";this.isOptionsInArray=!0;null===this.fillOpacity&&(this.type.match(/area/i)?this.fillOpacity=
0.7:this.fillOpacity=1);this.axisPlacement=this.getDefaultAxisPlacement();"undefined"===typeof this.options.indexLabelFontSize&&(this.indexLabelFontSize=this.chart.getAutoFontSize(this.indexLabelFontSize))}function z(a,d,b,c,e,g){z.base.constructor.call(this,"Axis",d,b,c,a);this.chart=a;this.canvas=a.canvas;this.ctx=a.ctx;this.intervalStartPosition=this.maxHeight=this.maxWidth=0;this.labels=[];this.dataSeries=[];this._stripLineLabels=this._ticks=this._labels=null;this.dataInfo={min:Infinity,max:-Infinity,
viewPortMin:Infinity,viewPortMax:-Infinity,minDiff:Infinity};this.isOptionsInArray=!0;"axisX"===e?("left"===g||"bottom"===g?(this.optionsName="axisX",u(this.chart.sessionVariables.axisX[c])&&(this.chart.sessionVariables.axisX[c]={}),this.sessionVariables=this.chart.sessionVariables.axisX[c]):(this.optionsName="axisX2",u(this.chart.sessionVariables.axisX2[c])&&(this.chart.sessionVariables.axisX2[c]={}),this.sessionVariables=this.chart.sessionVariables.axisX2[c]),this.options.interval||(this.intervalType=
null)):"left"===g||"bottom"===g?(this.optionsName="axisY",u(this.chart.sessionVariables.axisY[c])&&(this.chart.sessionVariables.axisY[c]={}),this.sessionVariables=this.chart.sessionVariables.axisY[c]):(this.optionsName="axisY2",u(this.chart.sessionVariables.axisY2[c])&&(this.chart.sessionVariables.axisY2[c]={}),this.sessionVariables=this.chart.sessionVariables.axisY2[c]);"undefined"===typeof this.options.titleFontSize&&(this.titleFontSize=this.chart.getAutoFontSize(this.titleFontSize));"undefined"===
typeof this.options.labelFontSize&&(this.labelFontSize=this.chart.getAutoFontSize(this.labelFontSize));this.type=e;"axisX"!==e||b&&"undefined"!==typeof b.gridThickness||(this.gridThickness=0);this._position=g;this.lineCoordinates={x1:null,y1:null,x2:null,y2:null,width:null};this.labelAngle=(this.labelAngle%360+360)%360;90<this.labelAngle&&270>this.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360);this.options.scaleBreaks&&(this.scaleBreaks=new Q(this.chart,
this.options.scaleBreaks,++this.chart._eventManager.lastObjectId,this));this.stripLines=[];if(this.options.stripLines&&0<this.options.stripLines.length)for(a=0;a<this.options.stripLines.length;a++)this.stripLines.push(new X(this.chart,this.options.stripLines[a],a,++this.chart._eventManager.lastObjectId,this));this.options.crosshair&&(this.crosshair=new fa(this.chart,this.options.crosshair,this));this._titleTextBlock=null;this.hasOptionChanged("viewportMinimum")&&null===this.viewportMinimum&&(this.options.viewportMinimum=
void 0,this.sessionVariables.viewportMinimum=null);this.hasOptionChanged("viewportMinimum")||isNaN(this.sessionVariables.newViewportMinimum)||null===this.sessionVariables.newViewportMinimum?this.sessionVariables.newViewportMinimum=null:this.viewportMinimum=this.sessionVariables.newViewportMinimum;this.hasOptionChanged("viewportMaximum")&&null===this.viewportMaximum&&(this.options.viewportMaximum=void 0,this.sessionVariables.viewportMaximum=null);this.hasOptionChanged("viewportMaximum")||isNaN(this.sessionVariables.newViewportMaximum)||
null===this.sessionVariables.newViewportMaximum?this.sessionVariables.newViewportMaximum=null:this.viewportMaximum=this.sessionVariables.newViewportMaximum;null!==this.minimum&&null!==this.viewportMinimum&&(this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum));null!==this.maximum&&null!==this.viewportMaximum&&(this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum));this.trackChanges("viewportMinimum");this.trackChanges("viewportMaximum")}function Q(a,d,b,c){Q.base.constructor.call(this,
"ScaleBreaks","scaleBreaks",d,null,c);this.id=b;this.chart=a;this.ctx=this.chart.ctx;this.axis=c;this.optionsName="scaleBreaks";this.isOptionsInArray=!1;this._appliedBreaks=[];this.customBreaks=[];this.autoBreaks=[];"string"===typeof this.spacing?(this.spacing=parseFloat(this.spacing),this.spacing=isNaN(this.spacing)?8:(10<this.spacing?10:this.spacing)+"%"):"number"!==typeof this.spacing&&(this.spacing=8);this.autoCalculate&&(this.maxNumberOfAutoBreaks=Math.min(this.maxNumberOfAutoBreaks,5));if(this.options.customBreaks&&
0<this.options.customBreaks.length){for(a=0;a<this.options.customBreaks.length;a++)this.customBreaks.push(new L(this.chart,"customBreaks",this.options.customBreaks[a],a,++this.chart._eventManager.lastObjectId,this)),"number"===typeof this.customBreaks[a].startValue&&("number"===typeof this.customBreaks[a].endValue&&this.customBreaks[a].endValue!==this.customBreaks[a].startValue)&&this._appliedBreaks.push(this.customBreaks[a]);this._appliedBreaks.sort(function(a,c){return a.startValue-c.startValue});
for(a=0;a<this._appliedBreaks.length-1;a++)this._appliedBreaks[a].endValue>=this._appliedBreaks[a+1].startValue&&(this._appliedBreaks[a].endValue=Math.max(this._appliedBreaks[a].endValue,this._appliedBreaks[a+1].endValue),window.console&&window.console.log("CanvasJS Error: Breaks "+a+" and "+(a+1)+" are overlapping."),this._appliedBreaks.splice(a,2),a--)}}function L(a,d,b,c,e,g){L.base.constructor.call(this,"Break",d,b,c,g);this.id=e;this.chart=a;this.ctx=this.chart.ctx;this.scaleBreaks=g;this.optionsName=
d;this.isOptionsInArray=!0;this.type=b.type?this.type:g.type;this.fillOpacity=u(b.fillOpacity)?g.fillOpacity:this.fillOpacity;this.lineThickness=u(b.lineThickness)?g.lineThickness:this.lineThickness;this.color=b.color?this.color:g.color;this.lineColor=b.lineColor?this.lineColor:g.lineColor;this.lineDashType=b.lineDashType?this.lineDashType:g.lineDashType;!u(this.startValue)&&this.startValue.getTime&&(this.startValue=this.startValue.getTime());!u(this.endValue)&&this.endValue.getTime&&(this.endValue=
this.endValue.getTime());"number"===typeof this.startValue&&("number"===typeof this.endValue&&this.endValue<this.startValue)&&(a=this.startValue,this.startValue=this.endValue,this.endValue=a);this.spacing="undefined"===typeof b.spacing?g.spacing:b.spacing;"string"===typeof this.options.spacing?(this.spacing=parseFloat(this.spacing),this.spacing=isNaN(this.spacing)?0:(10<this.spacing?10:this.spacing)+"%"):"number"!==typeof this.options.spacing&&(this.spacing=g.spacing);this.size=g.parent.logarithmic?
1:0}function X(a,d,b,c,e){X.base.constructor.call(this,"StripLine","stripLines",d,b,e);this.id=c;this.chart=a;this.ctx=this.chart.ctx;this.label=this.label;this.axis=e;this.optionsName="stripLines";this.isOptionsInArray=!0;this._thicknessType="pixel";null!==this.startValue&&null!==this.endValue&&(this.value=e.logarithmic?Math.sqrt((this.startValue.getTime?this.startValue.getTime():this.startValue)*(this.endValue.getTime?this.endValue.getTime():this.endValue)):((this.startValue.getTime?this.startValue.getTime():
this.startValue)+(this.endValue.getTime?this.endValue.getTime():this.endValue))/2,this._thicknessType=null)}function fa(a,d,b){fa.base.constructor.call(this,"Crosshair","crosshair",d,null,b);this.chart=a;this.ctx=this.chart.ctx;this.axis=b;this.optionsName="crosshair";this._thicknessType="pixel"}function $(a,d){$.base.constructor.call(this,"ToolTip","toolTip",d,null,a);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.currentDataPointIndex=this.currentSeriesIndex=-1;this._prevY=this._prevX=
NaN;this.containerTransitionDuration=0.1;this.mozContainerTransition=this.getContainerTransition(this.containerTransitionDuration);this.optionsName="toolTip";this._initialize()}function ha(a){this.chart=a;this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=[];this.previousDataPointEventObject=null;this.ghostCanvas=ta(this.chart.width,this.chart.height);this.ghostCtx=this.ghostCanvas.getContext("2d");this.mouseoveredObjectMaps=[]}function ga(a){this.chart=a;this.ctx=this.chart.plotArea.ctx;
this.animations=[];this.animationRequestId=null}qa(p,V);p.prototype.destroy=function(){var a=this.allDOMEventHandlers;this._animator&&this._animator.cancelAllAnimations();this._panTimerId&&clearTimeout(this._panTimerId);for(var d=0;d<a.length;d++){var b=a[d][0],c=a[d][1],e=a[d][2],g=a[d][3],g=g||!1;b.removeEventListener?b.removeEventListener(c,e,g):b.detachEvent&&b.detachEvent("on"+c,e)}this.allDOMEventHandlers=[];for(this.removeAllEventListeners();this._canvasJSContainer&&this._canvasJSContainer.hasChildNodes();)this._canvasJSContainer.removeChild(this._canvasJSContainer.lastChild);
for(;this.container&&this.container.hasChildNodes();)this.container.removeChild(this.container.lastChild);for(;this._dropdownMenu&&this._dropdownMenu.hasChildNodes();)this._dropdownMenu.removeChild(this._dropdownMenu.lastChild);this.overlaidCanvas=this.canvas=this.container=this._canvasJSContainer=null;this._toolBar=this._dropdownMenu=this._menuButton=this._resetButton=this._zoomButton=this._breaksCanvas=this._preRenderCanvas=this.toolTip.container=null};p.prototype._updateOptions=function(){var a=
this;this.updateOption("width");this.updateOption("height");this.updateOption("dataPointWidth");this.updateOption("dataPointMinWidth");this.updateOption("dataPointMaxWidth");this.updateOption("interactivityEnabled");this.updateOption("theme");this.updateOption("colorSet")&&(this._selectedColorSet="undefined"!==typeof Ba[this.colorSet]?Ba[this.colorSet]:Ba.colorSet1);this.updateOption("backgroundColor");this.backgroundColor||(this.backgroundColor="rgba(0,0,0,0)");this.updateOption("culture");this._cultureInfo=
new La(this.options.culture);this.updateOption("animationEnabled");this.animationEnabled=this.animationEnabled&&r;this.updateOption("animationDuration");this.updateOption("rangeChanging");this.updateOption("rangeChanged");this.updateOption("exportEnabled");this.updateOption("exportFileName");this.updateOption("zoomType");if(this.options.zoomEnabled){if(!this._zoomButton){var d=!1;va(this._zoomButton=document.createElement("button"));ua(this,this._zoomButton,"pan");this._toolBar.appendChild(this._zoomButton);
this._zoomButton.style.borderRight=this.toolbar.borderThickness+"px solid "+this.toolbar.borderColor;O(this._zoomButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(this._zoomButton,"click",function(){a.zoomEnabled?(a.zoomEnabled=!1,a.panEnabled=!0,ua(a,a._zoomButton,"zoom")):(a.zoomEnabled=!0,a.panEnabled=!1,ua(a,a._zoomButton,"pan"));a.render()},this.allDOMEventHandlers);O(this._zoomButton,"mouseover",function(){d?d=!1:(sa(a,a._zoomButton,{backgroundColor:a.toolbar.backgroundColorOnHover,
color:a.toolbar.fontColorOnHover,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers);O(this._zoomButton,"mouseout",function(){d||(sa(a,a._zoomButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._zoomButton.childNodes[0],{WebkitFilter:"invert(0%)",
filter:"invert(0%)"}))},this.allDOMEventHandlers)}this._resetButton||(d=!1,va(this._resetButton=document.createElement("button")),ua(this,this._resetButton,"reset"),this._resetButton.style.borderRight=(this.exportEnabled?this.toolbar.borderThickness:0)+"px solid "+this.toolbar.borderColor,this._toolBar.appendChild(this._resetButton),O(this._resetButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._resetButton,"click",function(){a.toolTip.hide();a.zoomEnabled||a.panEnabled?(a.zoomEnabled=
!0,a.panEnabled=!1,ua(a,a._zoomButton,"pan"),a._defaultCursor="default",a.overlaidCanvas.style.cursor=a._defaultCursor):(a.zoomEnabled=!1,a.panEnabled=!1);if(a.sessionVariables.axisX)for(var c=0;c<a.sessionVariables.axisX.length;c++)a.sessionVariables.axisX[c].newViewportMinimum=null,a.sessionVariables.axisX[c].newViewportMaximum=null;if(a.sessionVariables.axisX2)for(c=0;c<a.sessionVariables.axisX2.length;c++)a.sessionVariables.axisX2[c].newViewportMinimum=null,a.sessionVariables.axisX2[c].newViewportMaximum=
null;if(a.sessionVariables.axisY)for(c=0;c<a.sessionVariables.axisY.length;c++)a.sessionVariables.axisY[c].newViewportMinimum=null,a.sessionVariables.axisY[c].newViewportMaximum=null;if(a.sessionVariables.axisY2)for(c=0;c<a.sessionVariables.axisY2.length;c++)a.sessionVariables.axisY2[c].newViewportMinimum=null,a.sessionVariables.axisY2[c].newViewportMaximum=null;a.resetOverlayedCanvas();va(a._zoomButton,a._resetButton);a._dispatchRangeEvent("rangeChanging","reset");a.render();a._dispatchRangeEvent("rangeChanged",
"reset");a.syncCharts&&a.syncCharts(null,null)},this.allDOMEventHandlers),O(this._resetButton,"mouseover",function(){d||(sa(a,a._resetButton,{backgroundColor:a.toolbar.backgroundColorOnHover,color:a.toolbar.hoverFfontColorOnHoverontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers),O(this._resetButton,"mouseout",function(){d||(sa(a,a._resetButton,
{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor,transition:"0.4s",WebkitTransition:"0.4s"}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._resetButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers),this.overlaidCanvas.style.cursor=a._defaultCursor);this.zoomEnabled||this.panEnabled||(this._zoomButton?(a._zoomButton.getAttribute("state")===a._cultureInfo.zoomText?(this.panEnabled=!0,this.zoomEnabled=!1):(this.zoomEnabled=!0,this.panEnabled=
!1),Qa(a._zoomButton,a._resetButton)):(this.zoomEnabled=!0,this.panEnabled=!1))}else this.panEnabled=this.zoomEnabled=!1;this._menuButton?this.exportEnabled?Qa(this._menuButton):va(this._menuButton):this.exportEnabled&&r&&(d=!1,this._menuButton=document.createElement("button"),ua(this,this._menuButton,"menu"),this._toolBar.appendChild(this._menuButton),O(this._menuButton,"touchstart",function(a){d=!0},this.allDOMEventHandlers),O(this._menuButton,"click",function(){"none"!==a._dropdownMenu.style.display||
a._dropDownCloseTime&&500>=(new Date).getTime()-a._dropDownCloseTime.getTime()||(a._dropdownMenu.style.display="block",a._menuButton.blur(),a._dropdownMenu.focus())},this.allDOMEventHandlers,!0),O(this._menuButton,"mouseover",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColorOnHover,color:a.toolbar.fontColorOnHover}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(100%)",filter:"invert(100%)"}))},this.allDOMEventHandlers,!0),
O(this._menuButton,"mouseout",function(){d||(sa(a,a._menuButton,{backgroundColor:a.toolbar.backgroundColor,color:a.toolbar.fontColor}),0>=navigator.userAgent.search("MSIE")&&sa(a,a._menuButton.childNodes[0],{WebkitFilter:"invert(0%)",filter:"invert(0%)"}))},this.allDOMEventHandlers,!0));if(!this._dropdownMenu&&this.exportEnabled&&r){d=!1;this._dropdownMenu=document.createElement("div");this._dropdownMenu.setAttribute("tabindex",-1);var b=-1!==this.theme.indexOf("dark")?"black":"#888888";this._dropdownMenu.style.cssText=
"position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 0px;top: 25px;min-width: 120px;outline: 0;font-size: 14px; font-family: Arial, Helvetica, sans-serif;padding: 5px 0px 5px 0px;text-align: left;line-height: 10px;background-color:"+this.toolbar.backgroundColor+";box-shadow: 2px 2px 10px "+b;a._dropdownMenu.style.display="none";this._toolBar.appendChild(this._dropdownMenu);O(this._dropdownMenu,"blur",function(){va(a._dropdownMenu);
a._dropDownCloseTime=new Date},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.printText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,
!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){a.print();va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText="padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.saveJPGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=
!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"jpeg",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0);b=document.createElement("div");b.style.cssText=
"padding: 12px 8px 12px 8px";b.innerHTML=this._cultureInfo.savePNGText;b.style.backgroundColor=this.toolbar.backgroundColor;b.style.color=this.toolbar.fontColor;this._dropdownMenu.appendChild(b);O(b,"touchstart",function(a){d=!0},this.allDOMEventHandlers);O(b,"mouseover",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColorOnHover,this.style.color=a.toolbar.fontColorOnHover)},this.allDOMEventHandlers,!0);O(b,"mouseout",function(){d||(this.style.backgroundColor=a.toolbar.backgroundColor,
this.style.color=a.toolbar.fontColor)},this.allDOMEventHandlers,!0);O(b,"click",function(){Ta(a.canvas,"png",a.exportFileName);va(a._dropdownMenu)},this.allDOMEventHandlers,!0)}"none"!==this._toolBar.style.display&&this._zoomButton&&(this.panEnabled?ua(a,a._zoomButton,"zoom"):ua(a,a._zoomButton,"pan"),a._resetButton.getAttribute("state")!==a._cultureInfo.resetText&&ua(a,a._resetButton,"reset"));this.options.toolTip&&this.toolTip.options!==this.options.toolTip&&(this.toolTip.options=this.options.toolTip);
for(var c in this.toolTip.options)this.toolTip.options.hasOwnProperty(c)&&this.toolTip.updateOption(c)};p.prototype._updateSize=function(){var a;a=[this.canvas,this.overlaidCanvas,this._eventManager.ghostCanvas];var d=0,b=0;this.options.width?d=this.width:this.width=d=0<this.container.clientWidth?this.container.clientWidth:this.width;this.options.height?b=this.height:this.height=b=0<this.container.clientHeight?this.container.clientHeight:this.height;if(this.canvas.width!==d*W||this.canvas.height!==
b*W){for(var c=0;c<a.length;c++)Oa(a[c],d,b);a=!0}else a=!1;return a};p.prototype._initialize=function(){this.isNavigator=u(this.parent)||u(this.parent._defaultsKey)||"Navigator"!==this.parent._defaultsKey?!1:!0;this.toolbar=new Va(this,this.options.toolbar);this._animator?this._animator.cancelAllAnimations():this._animator=new ga(this);this.removeAllEventListeners();this.disableToolTip=!1;this._axes=[];this.funnelPyramidClickHandler=this.pieDoughnutClickHandler=null;this._updateOptions();this.animatedRender=
r&&this.animationEnabled&&0===this.renderCount;this._updateSize();this.clearCanvas();this.ctx.beginPath();this.axisX=[];this.axisX2=[];this.axisY=[];this.axisY2=[];this._indexLabels=[];this._dataInRenderedOrder=[];this._events=[];this._eventManager&&this._eventManager.reset();this.plotInfo={axisPlacement:null,plotTypes:[]};this.layoutManager=new Ga(0,0,this.width,this.height,this.isNavigator?0:2);this.plotArea.layoutManager&&this.plotArea.layoutManager.reset();this.data=[];var a=0,d=null;if(this.options.data){for(var b=
0;b<this.options.data.length;b++)if(a++,!this.options.data[b].type||0<=p._supportedChartTypes.indexOf(this.options.data[b].type)){var c=new F(this,this.options.data[b],a-1,++this._eventManager.lastObjectId);"error"===c.type&&(c.linkedDataSeriesIndex=u(this.options.data[b].linkedDataSeriesIndex)?b-1:this.options.data[b].linkedDataSeriesIndex,0>c.linkedDataSeriesIndex||c.linkedDataSeriesIndex>=this.options.data.length||"number"!==typeof c.linkedDataSeriesIndex||"error"===this.options.data[c.linkedDataSeriesIndex].type)&&
(c.linkedDataSeriesIndex=null);null===c.name&&(c.name="DataSeries "+a);null===c.color?1<this.options.data.length?(c._colorSet=[this._selectedColorSet[c.index%this._selectedColorSet.length]],c.color=this._selectedColorSet[c.index%this._selectedColorSet.length]):c._colorSet="line"===c.type||"stepLine"===c.type||"spline"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type||"rangeArea"===c.type||"rangeSplineArea"===c.type||"candlestick"===
c.type||"ohlc"===c.type||"waterfall"===c.type||"boxAndWhisker"===c.type?[this._selectedColorSet[0]]:this._selectedColorSet:c._colorSet=[c.color];null===c.markerSize&&(("line"===c.type||"stepLine"===c.type||"spline"===c.type||0<=c.type.toLowerCase().indexOf("area"))&&c.dataPoints&&c.dataPoints.length<this.width/16||"scatter"===c.type)&&(c.markerSize=8);"bubble"!==c.type&&"scatter"!==c.type||!c.dataPoints||(c.dataPoints.some?c.dataPoints.some(function(a){return a.x})&&c.dataPoints.sort(h):c.dataPoints.sort(h));
this.data.push(c);var e=c.axisPlacement,d=d||e,g;"normal"===e?"xySwapped"===this.plotInfo.axisPlacement?g='You cannot combine "'+c.type+'" with bar chart':"none"===this.plotInfo.axisPlacement?g='You cannot combine "'+c.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="normal"):"xySwapped"===e?"normal"===this.plotInfo.axisPlacement?g='You cannot combine "'+c.type+'" with line, area, column or pie chart':"none"===this.plotInfo.axisPlacement?g='You cannot combine "'+
c.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="xySwapped"):"none"===e?"normal"===this.plotInfo.axisPlacement?g='You cannot combine "'+c.type+'" with line, area, column or bar chart':"xySwapped"===this.plotInfo.axisPlacement?g='You cannot combine "'+c.type+'" with bar chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="none"):null===e&&"none"===this.plotInfo.axisPlacement&&(g='You cannot combine "'+c.type+'" with pie chart');if(g&&window.console){window.console.log(g);
return}}for(b=0;b<this.data.length;b++){if("none"==d&&"error"===this.data[b].type&&window.console){window.console.log('You cannot combine "'+c.type+'" with error chart');return}"error"===this.data[b].type&&(this.data[b].axisPlacement=this.plotInfo.axisPlacement=d||"normal",this.data[b]._linkedSeries=null===this.data[b].linkedDataSeriesIndex?null:this.data[this.data[b].linkedDataSeriesIndex])}}this._objectsInitialized=!0;this._plotAreaElements=[]};p._supportedChartTypes=Fa("line stepLine spline column area stepArea splineArea bar bubble scatter stackedColumn stackedColumn100 stackedBar stackedBar100 stackedArea stackedArea100 candlestick ohlc boxAndWhisker rangeColumn error rangeBar rangeArea rangeSplineArea pie doughnut funnel pyramid waterfall".split(" "));
p.prototype.setLayout=function(){for(var a=this._plotAreaElements,d=0;d<this.data.length;d++)if("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement){if(!this.data[d].axisYType||"primary"===this.data[d].axisYType)if(this.options.axisY&&0<this.options.axisY.length){if(!this.axisY.length)for(var b=0;b<this.options.axisY.length;b++)"normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY[b]=new z(this,"axisY",this.options.axisY[b],b,"axisY","left")):"xySwapped"===
this.plotInfo.axisPlacement&&this._axes.push(this.axisY[b]=new z(this,"axisY",this.options.axisY[b],b,"axisY","bottom"));this.data[d].axisY=this.axisY[0<=this.data[d].axisYIndex&&this.data[d].axisYIndex<this.axisY.length?this.data[d].axisYIndex:0];this.axisY[0<=this.data[d].axisYIndex&&this.data[d].axisYIndex<this.axisY.length?this.data[d].axisYIndex:0].dataSeries.push(this.data[d])}else this.axisY.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY[0]=new z(this,"axisY",this.options.axisY,
0,"axisY","left")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY[0]=new z(this,"axisY",this.options.axisY,0,"axisY","bottom"))),this.data[d].axisY=this.axisY[0],this.axisY[0].dataSeries.push(this.data[d]);if("secondary"===this.data[d].axisYType)if(this.options.axisY2&&0<this.options.axisY2.length){if(!this.axisY2.length)for(b=0;b<this.options.axisY2.length;b++)"normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY2[b]=new z(this,"axisY2",this.options.axisY2[b],b,
"axisY","right")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY2[b]=new z(this,"axisY2",this.options.axisY2[b],b,"axisY","top"));this.data[d].axisY=this.axisY2[0<=this.data[d].axisYIndex&&this.data[d].axisYIndex<this.axisY2.length?this.data[d].axisYIndex:0];this.axisY2[0<=this.data[d].axisYIndex&&this.data[d].axisYIndex<this.axisY2.length?this.data[d].axisYIndex:0].dataSeries.push(this.data[d])}else this.axisY2.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY2[0]=
new z(this,"axisY2",this.options.axisY2,0,"axisY","right")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY2[0]=new z(this,"axisY2",this.options.axisY2,0,"axisY","top"))),this.data[d].axisY=this.axisY2[0],this.axisY2[0].dataSeries.push(this.data[d]);if(!this.data[d].axisXType||"primary"===this.data[d].axisXType)if(this.options.axisX&&0<this.options.axisX.length){if(!this.axisX.length)for(b=0;b<this.options.axisX.length;b++)"normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisX[b]=
new z(this,"axisX",this.options.axisX[b],b,"axisX","bottom")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX[b]=new z(this,"axisX",this.options.axisX[b],b,"axisX","left"));this.data[d].axisX=this.axisX[0<=this.data[d].axisXIndex&&this.data[d].axisXIndex<this.axisX.length?this.data[d].axisXIndex:0];this.axisX[0<=this.data[d].axisXIndex&&this.data[d].axisXIndex<this.axisX.length?this.data[d].axisXIndex:0].dataSeries.push(this.data[d])}else this.axisX.length||("normal"===this.plotInfo.axisPlacement?
this._axes.push(this.axisX[0]=new z(this,"axisX",this.options.axisX,0,"axisX","bottom")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX[0]=new z(this,"axisX",this.options.axisX,0,"axisX","left"))),this.data[d].axisX=this.axisX[0],this.axisX[0].dataSeries.push(this.data[d]);if("secondary"===this.data[d].axisXType)if(this.options.axisX2&&0<this.options.axisX2.length){if(!this.axisX2.length)for(b=0;b<this.options.axisX2.length;b++)"normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisX2[b]=
new z(this,"axisX2",this.options.axisX2[b],b,"axisX","top")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX2[b]=new z(this,"axisX2",this.options.axisX2[b],b,"axisX","right"));this.data[d].axisX=this.axisX2[0<=this.data[d].axisXIndex&&this.data[d].axisXIndex<this.axisX2.length?this.data[d].axisXIndex:0];this.axisX2[0<=this.data[d].axisXIndex&&this.data[d].axisXIndex<this.axisX2.length?this.data[d].axisXIndex:0].dataSeries.push(this.data[d])}else this.axisX2.length||("normal"===
this.plotInfo.axisPlacement?this._axes.push(this.axisX2[0]=new z(this,"axisX2",this.options.axisX2,0,"axisX","top")):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX2[0]=new z(this,"axisX2",this.options.axisX2,0,"axisX","right"))),this.data[d].axisX=this.axisX2[0],this.axisX2[0].dataSeries.push(this.data[d])}if(this.axisY){for(b=1;b<this.axisY.length;b++)"undefined"===typeof this.axisY[b].options.gridThickness&&(this.axisY[b].gridThickness=0);for(b=0;b<this.axisY.length-1;b++)"undefined"===
typeof this.axisY[b].options.margin&&(this.axisY[b].margin=10)}if(this.axisY2){for(b=1;b<this.axisY2.length;b++)"undefined"===typeof this.axisY2[b].options.gridThickness&&(this.axisY2[b].gridThickness=0);for(b=0;b<this.axisY2.length-1;b++)"undefined"===typeof this.axisY2[b].options.margin&&(this.axisY2[b].margin=10)}this.axisY&&0<this.axisY.length&&(this.axisY2&&0<this.axisY2.length)&&(0<this.axisY[0].gridThickness&&"undefined"===typeof this.axisY2[0].options.gridThickness?this.axisY2[0].gridThickness=
0:0<this.axisY2[0].gridThickness&&"undefined"===typeof this.axisY[0].options.gridThickness&&(this.axisY[0].gridThickness=0));if(this.axisX)for(b=0;b<this.axisX.length;b++)"undefined"===typeof this.axisX[b].options.gridThickness&&(this.axisX[b].gridThickness=0);if(this.axisX2)for(b=0;b<this.axisX2.length;b++)"undefined"===typeof this.axisX2[b].options.gridThickness&&(this.axisX2[b].gridThickness=0);this.axisX&&0<this.axisX.length&&(this.axisX2&&0<this.axisX2.length)&&(0<this.axisX[0].gridThickness&&
"undefined"===typeof this.axisX2[0].options.gridThickness?this.axisX2[0].gridThickness=0:0<this.axisX2[0].gridThickness&&"undefined"===typeof this.axisX[0].options.gridThickness&&(this.axisX[0].gridThickness=0));b=!1;if(0<this._axes.length&&(this.zoomEnabled||this.panEnabled))for(d=0;d<this._axes.length;d++)if(null!==this._axes[d].viewportMinimum||null!==this._axes[d].viewportMaximum){b=!0;break}b?(Qa(this._zoomButton,this._resetButton),this._toolBar.style.border=this.toolbar.borderThickness+"px solid "+
this.toolbar.borderColor,this._zoomButton.style.borderRight=this.toolbar.borderThickness+"px solid "+this.toolbar.borderColor,this._resetButton.style.borderRight=(this.exportEnabled?this.toolbar.borderThickness:0)+"px solid "+this.toolbar.borderColor):(va(this._zoomButton,this._resetButton),this._toolBar.style.border=this.toolbar.borderThickness+"px solid transparent",this.options.zoomEnabled&&(this.zoomEnabled=!0,this.panEnabled=!1));gb(this);this._processData();this.options.title&&(this.title=new Aa(this,
this.options.title),this.title.dockInsidePlotArea?a.push(this.title):this.title.setLayout());this.subtitles=[];if(this.options.subtitles)for(d=0;d<this.options.subtitles.length;d++)b=new Ka(this,this.options.subtitles[d],d),this.subtitles.push(b),b.dockInsidePlotArea?a.push(b):b.setLayout();this.legend=new H(this,this.options.legend);for(d=0;d<this.data.length;d++)(this.data[d].showInLegend||"pie"===this.data[d].type||"doughnut"===this.data[d].type||"funnel"===this.data[d].type||"pyramid"===this.data[d].type)&&
this.legend.dataSeries.push(this.data[d]);this.legend.dockInsidePlotArea?a.push(this.legend):this.legend.setLayout();for(d=0;d<this._axes.length;d++)if(this._axes[d].scaleBreaks&&this._axes[d].scaleBreaks._appliedBreaks.length){r?(this._breaksCanvas=ta(this.width,this.height,!0),this._breaksCanvasCtx=this._breaksCanvas.getContext("2d")):(this._breaksCanvas=this.canvas,this._breaksCanvasCtx=this.ctx);break}this._preRenderCanvas=ta(this.width,this.height);this._preRenderCtx=this._preRenderCanvas.getContext("2d");
"normal"!==this.plotInfo.axisPlacement&&"xySwapped"!==this.plotInfo.axisPlacement||z.setLayout(this.axisX,this.axisX2,this.axisY,this.axisY2,this.plotInfo.axisPlacement,this.layoutManager.getFreeSpace())};p.prototype.renderElements=function(){var a=this._plotAreaElements;this.title&&!this.title.dockInsidePlotArea&&this.title.render();for(var d=0;d<this.subtitles.length;d++)this.subtitles[d].dockInsidePlotArea||this.subtitles[d].render();this.legend.dockInsidePlotArea||this.legend.render();if("normal"===
this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)z.render(this.axisX,this.axisX2,this.axisY,this.axisY2,this.plotInfo.axisPlacement);else if("none"===this.plotInfo.axisPlacement)this.preparePlotArea();else return;for(d=0;d<a.length;d++)a[d].setLayout(),a[d].render();var b=[];if(this.animatedRender){var c=ta(this.width,this.height);c.getContext("2d").drawImage(this.canvas,0,0,this.width,this.height)}hb(this);var a=this.ctx.miterLimit,e;this.ctx.miterLimit=3;r&&this._breaksCanvas&&
(this._preRenderCtx.drawImage(this.canvas,0,0,this.width,this.height),this._preRenderCtx.drawImage(this._breaksCanvas,0,0,this.width,this.height),this._breaksCanvasCtx.globalCompositeOperation="source-atop",this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),this._preRenderCtx.clearRect(0,0,this.width,this.height));for(d=0;d<this.plotInfo.plotTypes.length;d++)for(var g=this.plotInfo.plotTypes[d],k=0;k<g.plotUnits.length;k++){var l=g.plotUnits[k],w=null;l.targetCanvas=
null;this.animatedRender&&(l.targetCanvas=ta(this.width,this.height),l.targetCanvasCtx=l.targetCanvas.getContext("2d"),e=l.targetCanvasCtx.miterLimit,l.targetCanvasCtx.miterLimit=3);"line"===l.type?w=this.renderLine(l):"stepLine"===l.type?w=this.renderStepLine(l):"spline"===l.type?w=this.renderSpline(l):"column"===l.type?w=this.renderColumn(l):"bar"===l.type?w=this.renderBar(l):"area"===l.type?w=this.renderArea(l):"stepArea"===l.type?w=this.renderStepArea(l):"splineArea"===l.type?w=this.renderSplineArea(l):
"stackedColumn"===l.type?w=this.renderStackedColumn(l):"stackedColumn100"===l.type?w=this.renderStackedColumn100(l):"stackedBar"===l.type?w=this.renderStackedBar(l):"stackedBar100"===l.type?w=this.renderStackedBar100(l):"stackedArea"===l.type?w=this.renderStackedArea(l):"stackedArea100"===l.type?w=this.renderStackedArea100(l):"bubble"===l.type?w=w=this.renderBubble(l):"scatter"===l.type?w=this.renderScatter(l):"pie"===l.type?this.renderPie(l):"doughnut"===l.type?this.renderPie(l):"funnel"===l.type?
w=this.renderFunnel(l):"pyramid"===l.type?w=this.renderFunnel(l):"candlestick"===l.type?w=this.renderCandlestick(l):"ohlc"===l.type?w=this.renderCandlestick(l):"rangeColumn"===l.type?w=this.renderRangeColumn(l):"error"===l.type?w=this.renderError(l):"rangeBar"===l.type?w=this.renderRangeBar(l):"rangeArea"===l.type?w=this.renderRangeArea(l):"rangeSplineArea"===l.type?w=this.renderRangeSplineArea(l):"waterfall"===l.type?w=this.renderWaterfall(l):"boxAndWhisker"===l.type&&(w=this.renderBoxAndWhisker(l));
for(var m=0;m<l.dataSeriesIndexes.length;m++)this._dataInRenderedOrder.push(this.data[l.dataSeriesIndexes[m]]);this.animatedRender&&(l.targetCanvasCtx.miterLimit=e,w&&b.push(w))}this.ctx.miterLimit=a;this.animatedRender&&this._breaksCanvasCtx&&b.push({source:this._breaksCanvasCtx,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0,startTimePercent:0.7});this.animatedRender&&0<this._indexLabels.length&&(e=ta(this.width,this.height).getContext("2d"),
b.push(this.renderIndexLabels(e)));var s=this;if(0<b.length)s.disableToolTip=!0,s._animator.animate(200,s.animationDuration,function(a){s.ctx.clearRect(0,0,s.width,s.height);s.ctx.drawImage(c,0,0,Math.floor(s.width*W),Math.floor(s.height*W),0,0,s.width,s.height);for(var e=0;e<b.length;e++)w=b[e],1>a&&"undefined"!==typeof w.startTimePercent?a>=w.startTimePercent&&w.animationCallback(w.easingFunction(a-w.startTimePercent,0,1,1-w.startTimePercent),w):w.animationCallback(w.easingFunction(a,0,1,1),w);
s.dispatchEvent("dataAnimationIterationEnd",{chart:s})},function(){b=[];for(var a=0;a<s.plotInfo.plotTypes.length;a++)for(var e=s.plotInfo.plotTypes[a],d=0;d<e.plotUnits.length;d++)e.plotUnits[d].targetCanvas=null;c=null;s.disableToolTip=!1});else{if(s._breaksCanvas)if(r)s.plotArea.ctx.drawImage(s._breaksCanvas,0,0,this.width,this.height);else for(m=0;m<s._axes.length;m++)s._axes[m].createMask();0<s._indexLabels.length&&s.renderIndexLabels();s.dispatchEvent("dataAnimationIterationEnd",{chart:s})}this.attachPlotAreaEventHandlers();
this.zoomEnabled||(this.panEnabled||!this._zoomButton||"none"===this._zoomButton.style.display)||va(this._zoomButton,this._resetButton);this.toolTip._updateToolTip();this.renderCount++;Ja&&(s=this,setTimeout(function(){var a=document.getElementById("ghostCanvasCopy");a&&(Oa(a,s.width,s.height),a.getContext("2d").drawImage(s._eventManager.ghostCanvas,0,0))},2E3));this._breaksCanvas&&(delete this._breaksCanvas,delete this._breaksCanvasCtx);for(m=0;m<this._axes.length;m++)this._axes[m].maskCanvas&&(delete this._axes[m].maskCanvas,
delete this._axes[m].maskCtx)};p.prototype.render=function(a){a&&(this.options=a);this._initialize();this.setLayout();this.renderElements();this._preRenderCanvas=null};p.prototype.attachPlotAreaEventHandlers=function(){this.attachEvent({context:this,chart:this,mousedown:this._plotAreaMouseDown,mouseup:this._plotAreaMouseUp,mousemove:this._plotAreaMouseMove,cursor:this.panEnabled?"move":"default",capture:!0,bounds:this.plotArea})};p.prototype.categoriseDataSeries=function(){for(var a="",d=0;d<this.data.length;d++)if(a=
this.data[d],a.dataPoints&&(0!==a.dataPoints.length&&a.visible)&&0<=p._supportedChartTypes.indexOf(a.type)){for(var b=null,c=!1,e=null,g=!1,k=0;k<this.plotInfo.plotTypes.length;k++)if(this.plotInfo.plotTypes[k].type===a.type){c=!0;b=this.plotInfo.plotTypes[k];break}c||(b={type:a.type,totalDataSeries:0,plotUnits:[]},this.plotInfo.plotTypes.push(b));for(k=0;k<b.plotUnits.length;k++)if(b.plotUnits[k].axisYType===a.axisYType&&b.plotUnits[k].axisXType===a.axisXType&&b.plotUnits[k].axisYIndex===a.axisYIndex&&
b.plotUnits[k].axisXIndex===a.axisXIndex){g=!0;e=b.plotUnits[k];break}g||(e={type:a.type,previousDataSeriesCount:0,index:b.plotUnits.length,plotType:b,axisXType:a.axisXType,axisYType:a.axisYType,axisYIndex:a.axisYIndex,axisXIndex:a.axisXIndex,axisY:"primary"===a.axisYType?this.axisY[0<=a.axisYIndex&&a.axisYIndex<this.axisY.length?a.axisYIndex:0]:this.axisY2[0<=a.axisYIndex&&a.axisYIndex<this.axisY2.length?a.axisYIndex:0],axisX:"primary"===a.axisXType?this.axisX[0<=a.axisXIndex&&a.axisXIndex<this.axisX.length?
a.axisXIndex:0]:this.axisX2[0<=a.axisXIndex&&a.axisXIndex<this.axisX2.length?a.axisXIndex:0],dataSeriesIndexes:[],yTotals:[]},b.plotUnits.push(e));b.totalDataSeries++;e.dataSeriesIndexes.push(d);a.plotUnit=e}for(d=0;d<this.plotInfo.plotTypes.length;d++)for(b=this.plotInfo.plotTypes[d],k=a=0;k<b.plotUnits.length;k++)b.plotUnits[k].previousDataSeriesCount=a,a+=b.plotUnits[k].dataSeriesIndexes.length};p.prototype.assignIdToDataPoints=function(){for(var a=0;a<this.data.length;a++){var d=this.data[a];
if(d.dataPoints)for(var b=d.dataPoints.length,c=0;c<b;c++)d.dataPointIds[c]=++this._eventManager.lastObjectId}};p.prototype._processData=function(){this.assignIdToDataPoints();this.categoriseDataSeries();for(var a=0;a<this.plotInfo.plotTypes.length;a++)for(var d=this.plotInfo.plotTypes[a],b=0;b<d.plotUnits.length;b++){var c=d.plotUnits[b];"line"===c.type||"stepLine"===c.type||"spline"===c.type||"column"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"bar"===c.type||"bubble"===
c.type||"scatter"===c.type?this._processMultiseriesPlotUnit(c):"stackedColumn"===c.type||"stackedBar"===c.type||"stackedArea"===c.type?this._processStackedPlotUnit(c):"stackedColumn100"===c.type||"stackedBar100"===c.type||"stackedArea100"===c.type?this._processStacked100PlotUnit(c):"candlestick"===c.type||"ohlc"===c.type||"rangeColumn"===c.type||"rangeBar"===c.type||"rangeArea"===c.type||"rangeSplineArea"===c.type||"error"===c.type||"boxAndWhisker"===c.type?this._processMultiYPlotUnit(c):"waterfall"===
c.type&&this._processSpecificPlotUnit(c)}this.calculateAutoBreaks()};p.prototype._processMultiseriesPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,k=0;k<a.dataSeriesIndexes.length;k++){var l=this.data[a.dataSeriesIndexes[k]],w=0,m=!1,s=!1,q;if("normal"===l.axisPlacement||"xySwapped"===l.axisPlacement)var n=a.axisX.sessionVariables.newViewportMinimum?a.axisX.sessionVariables.newViewportMinimum:this.options.axisX&&
this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:a.axisX.logarithmic?0:-Infinity,f=a.axisX.sessionVariables.newViewportMaximum?a.axisX.sessionVariables.newViewportMaximum:this.options.axisX&&this.options.axisX.viewportMaximum?this.options.axisX.viewportMaximum:this.options.axisX&&this.options.axisX.maximum?this.options.axisX.maximum:Infinity;if(l.dataPoints[w].x&&l.dataPoints[w].x.getTime||"dateTime"===
l.xValueType)g=!0;for(w=0;w<l.dataPoints.length;w++){"undefined"===typeof l.dataPoints[w].x&&(l.dataPoints[w].x=w+(a.axisX.logarithmic?1:0));l.dataPoints[w].x.getTime?(g=!0,c=l.dataPoints[w].x.getTime()):c=l.dataPoints[w].x;e=l.dataPoints[w].y;c<b.min&&(b.min=c);c>b.max&&(b.max=c);e<d.min&&"number"===typeof e&&(d.min=e);e>d.max&&"number"===typeof e&&(d.max=e);if(0<w){if(a.axisX.logarithmic){var B=c/l.dataPoints[w-1].x;1>B&&(B=1/B);b.minDiff>B&&1!==B&&(b.minDiff=B)}else B=c-l.dataPoints[w-1].x,0>B&&
(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B);null!==e&&null!==l.dataPoints[w-1].y&&(a.axisY.logarithmic?(B=e/l.dataPoints[w-1].y,1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e-l.dataPoints[w-1].y,0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B)))}if(c<n&&!m)null!==e&&(q=c);else{if(!m&&(m=!0,0<w)){w-=2;continue}if(c>f&&!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);c<b.viewPortMin&&(b.viewPortMin=c);c>b.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===
c&&q<c&&(b.viewPortMin=q):(e<d.viewPortMin&&"number"===typeof e&&(d.viewPortMin=e),e>d.viewPortMax&&"number"===typeof e&&(d.viewPortMax=e))}}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype._processStackedPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,k=[],l=[],w=Infinity,m=-Infinity,s=0;s<a.dataSeriesIndexes.length;s++){var q=this.data[a.dataSeriesIndexes[s]],n=0,f=!1,B=!1,h;if("normal"===q.axisPlacement||
"xySwapped"===q.axisPlacement)var p=a.axisX.sessionVariables.newViewportMinimum?a.axisX.sessionVariables.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:-Infinity,t=a.axisX.sessionVariables.newViewportMaximum?a.axisX.sessionVariables.newViewportMaximum:this.options.axisX&&this.options.axisX.viewportMaximum?this.options.axisX.viewportMaximum:this.options.axisX&&this.options.axisX.maximum?
this.options.axisX.maximum:Infinity;if(q.dataPoints[n].x&&q.dataPoints[n].x.getTime||"dateTime"===q.xValueType)g=!0;for(n=0;n<q.dataPoints.length;n++){"undefined"===typeof q.dataPoints[n].x&&(q.dataPoints[n].x=n+(a.axisX.logarithmic?1:0));q.dataPoints[n].x.getTime?(g=!0,c=q.dataPoints[n].x.getTime()):c=q.dataPoints[n].x;e=u(q.dataPoints[n].y)?0:q.dataPoints[n].y;c<b.min&&(b.min=c);c>b.max&&(b.max=c);if(0<n){if(a.axisX.logarithmic){var r=c/q.dataPoints[n-1].x;1>r&&(r=1/r);b.minDiff>r&&1!==r&&(b.minDiff=
r)}else r=c-q.dataPoints[n-1].x,0>r&&(r*=-1),b.minDiff>r&&0!==r&&(b.minDiff=r);null!==e&&null!==q.dataPoints[n-1].y&&(a.axisY.logarithmic?0<e&&(r=e/q.dataPoints[n-1].y,1>r&&(r=1/r),d.minDiff>r&&1!==r&&(d.minDiff=r)):(r=e-q.dataPoints[n-1].y,0>r&&(r*=-1),d.minDiff>r&&0!==r&&(d.minDiff=r)))}if(c<p&&!f)null!==q.dataPoints[n].y&&(h=c);else{if(!f&&(f=!0,0<n)){n-=2;continue}if(c>t&&!B)B=!0;else if(c>t&&B)continue;q.dataPoints[n].label&&(a.axisX.labels[c]=q.dataPoints[n].label);c<b.viewPortMin&&(b.viewPortMin=
c);c>b.viewPortMax&&(b.viewPortMax=c);null===q.dataPoints[n].y?b.viewPortMin===c&&h<c&&(b.viewPortMin=h):(a.yTotals[c]=(a.yTotals[c]?a.yTotals[c]:0)+e,0<=e?k[c]?k[c]+=e:(k[c]=e,w=Math.min(e,w)):l[c]?l[c]+=e:(l[c]=e,m=Math.max(e,m)))}}a.axisY.scaleBreaks&&(a.axisY.scaleBreaks.autoCalculate&&1<=a.axisY.scaleBreaks.maxNumberOfAutoBreaks)&&(d.dataPointYPositiveSums?(d.dataPointYPositiveSums.push.apply(d.dataPointYPositiveSums,k),d.dataPointYNegativeSums.push.apply(d.dataPointYPositiveSums,l)):(d.dataPointYPositiveSums=
k,d.dataPointYNegativeSums=l));q.axisX.valueType=q.xValueType=g?"dateTime":"number"}for(n in k)k.hasOwnProperty(n)&&!isNaN(n)&&(a=k[n],a<d.min&&(d.min=Math.min(a,w)),a>d.max&&(d.max=a),n<b.viewPortMin||n>b.viewPortMax||(a<d.viewPortMin&&(d.viewPortMin=Math.min(a,w)),a>d.viewPortMax&&(d.viewPortMax=a)));for(n in l)l.hasOwnProperty(n)&&!isNaN(n)&&(a=l[n],a<d.min&&(d.min=a),a>d.max&&(d.max=Math.max(a,m)),n<b.viewPortMin||n>b.viewPortMax||(a<d.viewPortMin&&(d.viewPortMin=a),a>d.viewPortMax&&(d.viewPortMax=
Math.max(a,m))))}};p.prototype._processStacked100PlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,k=!1,l=!1,w=[],m=0;m<a.dataSeriesIndexes.length;m++){var s=this.data[a.dataSeriesIndexes[m]],q=0,n=!1,f=!1,B;if("normal"===s.axisPlacement||"xySwapped"===s.axisPlacement)var h=a.axisX.sessionVariables.newViewportMinimum?a.axisX.sessionVariables.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?
this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:-Infinity,r=a.axisX.sessionVariables.newViewportMaximum?a.axisX.sessionVariables.newViewportMaximum:this.options.axisX&&this.options.axisX.viewportMaximum?this.options.axisX.viewportMaximum:this.options.axisX&&this.options.axisX.maximum?this.options.axisX.maximum:Infinity;if(s.dataPoints[q].x&&s.dataPoints[q].x.getTime||"dateTime"===s.xValueType)g=!0;for(q=0;q<s.dataPoints.length;q++){"undefined"===
typeof s.dataPoints[q].x&&(s.dataPoints[q].x=q+(a.axisX.logarithmic?1:0));s.dataPoints[q].x.getTime?(g=!0,c=s.dataPoints[q].x.getTime()):c=s.dataPoints[q].x;e=u(s.dataPoints[q].y)?null:s.dataPoints[q].y;c<b.min&&(b.min=c);c>b.max&&(b.max=c);if(0<q){if(a.axisX.logarithmic){var t=c/s.dataPoints[q-1].x;1>t&&(t=1/t);b.minDiff>t&&1!==t&&(b.minDiff=t)}else t=c-s.dataPoints[q-1].x,0>t&&(t*=-1),b.minDiff>t&&0!==t&&(b.minDiff=t);u(e)||null===s.dataPoints[q-1].y||(a.axisY.logarithmic?0<e&&(t=e/s.dataPoints[q-
1].y,1>t&&(t=1/t),d.minDiff>t&&1!==t&&(d.minDiff=t)):(t=e-s.dataPoints[q-1].y,0>t&&(t*=-1),d.minDiff>t&&0!==t&&(d.minDiff=t)))}if(c<h&&!n)null!==e&&(B=c);else{if(!n&&(n=!0,0<q)){q-=2;continue}if(c>r&&!f)f=!0;else if(c>r&&f)continue;s.dataPoints[q].label&&(a.axisX.labels[c]=s.dataPoints[q].label);c<b.viewPortMin&&(b.viewPortMin=c);c>b.viewPortMax&&(b.viewPortMax=c);null===e?b.viewPortMin===c&&B<c&&(b.viewPortMin=B):(a.yTotals[c]=(a.yTotals[c]?a.yTotals[c]:0)+e,0<=e?k=!0:0>e&&(l=!0),w[c]=w[c]?w[c]+
Math.abs(e):Math.abs(e))}}s.axisX.valueType=s.xValueType=g?"dateTime":"number"}a.axisY.logarithmic?(d.max=u(d.viewPortMax)?99*Math.pow(a.axisY.logarithmBase,-0.05):Math.max(d.viewPortMax,99*Math.pow(a.axisY.logarithmBase,-0.05)),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):k&&!l?(d.max=u(d.viewPortMax)?99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?1:Math.min(d.viewPortMin,1)):k&&l?(d.max=u(d.viewPortMax)?99:Math.max(d.viewPortMax,99),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,
-99)):!k&&l&&(d.max=u(d.viewPortMax)?-1:Math.max(d.viewPortMax,-1),d.min=u(d.viewPortMin)?-99:Math.min(d.viewPortMin,-99));d.viewPortMin=d.min;d.viewPortMax=d.max;a.dataPointYSums=w}};p.prototype._processMultiYPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g,k,l=!1,w=0;w<a.dataSeriesIndexes.length;w++){var m=this.data[a.dataSeriesIndexes[w]],s=0,q=!1,n=!1,f,B,h;if("normal"===m.axisPlacement||"xySwapped"===m.axisPlacement)var r=
a.axisX.sessionVariables.newViewportMinimum?a.axisX.sessionVariables.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:a.axisX.logarithmic?0:-Infinity,t=a.axisX.sessionVariables.newViewportMaximum?a.axisX.sessionVariables.newViewportMaximum:this.options.axisX&&this.options.axisX.viewportMaximum?this.options.axisX.viewportMaximum:this.options.axisX&&this.options.axisX.maximum?
this.options.axisX.maximum:Infinity;if(m.dataPoints[s].x&&m.dataPoints[s].x.getTime||"dateTime"===m.xValueType)l=!0;for(s=0;s<m.dataPoints.length;s++){"undefined"===typeof m.dataPoints[s].x&&(m.dataPoints[s].x=s+(a.axisX.logarithmic?1:0));m.dataPoints[s].x.getTime?(l=!0,c=m.dataPoints[s].x.getTime()):c=m.dataPoints[s].x;if((e=m.dataPoints[s].y)&&e.length){g=Math.min.apply(null,e);k=Math.max.apply(null,e);B=!0;for(var p=0;p<e.length;p++)null===e.k&&(B=!1);B&&(q||(h=f),f=c)}c<b.min&&(b.min=c);c>b.max&&
(b.max=c);g<d.min&&(d.min=g);k>d.max&&(d.max=k);0<s&&(a.axisX.logarithmic?(B=c/m.dataPoints[s-1].x,1>B&&(B=1/B),b.minDiff>B&&1!==B&&(b.minDiff=B)):(B=c-m.dataPoints[s-1].x,0>B&&(B*=-1),b.minDiff>B&&0!==B&&(b.minDiff=B)),e&&(null!==e[0]&&m.dataPoints[s-1].y&&null!==m.dataPoints[s-1].y[0])&&(a.axisY.logarithmic?(B=e[0]/m.dataPoints[s-1].y[0],1>B&&(B=1/B),d.minDiff>B&&1!==B&&(d.minDiff=B)):(B=e[0]-m.dataPoints[s-1].y[0],0>B&&(B*=-1),d.minDiff>B&&0!==B&&(d.minDiff=B))));if(!(c<r)||q){if(!q&&(q=!0,0<s)){s-=
2;f=h;continue}if(c>t&&!n)n=!0;else if(c>t&&n)continue;m.dataPoints[s].label&&(a.axisX.labels[c]=m.dataPoints[s].label);c<b.viewPortMin&&(b.viewPortMin=c);c>b.viewPortMax&&(b.viewPortMax=c);if(b.viewPortMin===c&&e)for(p=0;p<e.length;p++)if(null===e[p]&&f<c){b.viewPortMin=f;break}null===e?b.viewPortMin===c&&f<c&&(b.viewPortMin=f):(g<d.viewPortMin&&(d.viewPortMin=g),k>d.viewPortMax&&(d.viewPortMax=k))}}m.axisX.valueType=m.xValueType=l?"dateTime":"number"}};p.prototype._processSpecificPlotUnit=function(a){if("waterfall"===
a.type&&a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var d=a.axisY.dataInfo,b=a.axisX.dataInfo,c,e,g=!1,k=0;k<a.dataSeriesIndexes.length;k++){var l=this.data[a.dataSeriesIndexes[k]],w=0,m=!1,s=!1,q=c=0;if("normal"===l.axisPlacement||"xySwapped"===l.axisPlacement)var n=a.axisX.sessionVariables.newViewportMinimum?a.axisX.sessionVariables.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?
this.options.axisX.minimum:a.axisX.logarithmic?0:-Infinity,f=a.axisX.sessionVariables.newViewportMaximum?a.axisX.sessionVariables.newViewportMaximum:this.options.axisX&&this.options.axisX.viewportMaximum?this.options.axisX.viewportMaximum:this.options.axisX&&this.options.axisX.maximum?this.options.axisX.maximum:Infinity;if(l.dataPoints[w].x&&l.dataPoints[w].x.getTime||"dateTime"===l.xValueType)g=!0;for(w=0;w<l.dataPoints.length;w++)"undefined"!==typeof l.dataPoints[w].isCumulativeSum&&!0===l.dataPoints[w].isCumulativeSum?
(l.dataPointEOs[w].cumulativeSumYStartValue=0,l.dataPointEOs[w].cumulativeSum=0===w?0:l.dataPointEOs[w-1].cumulativeSum,l.dataPoints[w].y=0===w?0:l.dataPointEOs[w-1].cumulativeSum):"undefined"!==typeof l.dataPoints[w].isIntermediateSum&&!0===l.dataPoints[w].isIntermediateSum?(l.dataPointEOs[w].cumulativeSumYStartValue=q,l.dataPointEOs[w].cumulativeSum=0===w?0:l.dataPointEOs[w-1].cumulativeSum,l.dataPoints[w].y=0===w?0:c,q=0===w?0:l.dataPointEOs[w-1].cumulativeSum,c=0):(e="number"!==typeof l.dataPoints[w].y?
0:l.dataPoints[w].y,l.dataPointEOs[w].cumulativeSumYStartValue=0===w?0:l.dataPointEOs[w-1].cumulativeSum,l.dataPointEOs[w].cumulativeSum=0===w?e:l.dataPointEOs[w-1].cumulativeSum+e,c+=e);for(w=0;w<l.dataPoints.length;w++)if("undefined"===typeof l.dataPoints[w].x&&(l.dataPoints[w].x=w+(a.axisX.logarithmic?1:0)),l.dataPoints[w].x.getTime?(g=!0,c=l.dataPoints[w].x.getTime()):c=l.dataPoints[w].x,e=l.dataPoints[w].y,c<b.min&&(b.min=c),c>b.max&&(b.max=c),l.dataPointEOs[w].cumulativeSum<d.min&&(d.min=l.dataPointEOs[w].cumulativeSum),
l.dataPointEOs[w].cumulativeSum>d.max&&(d.max=l.dataPointEOs[w].cumulativeSum),0<w&&(a.axisX.logarithmic?(q=c/l.dataPoints[w-1].x,1>q&&(q=1/q),b.minDiff>q&&1!==q&&(b.minDiff=q)):(q=c-l.dataPoints[w-1].x,0>q&&(q*=-1),b.minDiff>q&&0!==q&&(b.minDiff=q)),null!==e&&null!==l.dataPoints[w-1].y&&(a.axisY.logarithmic?(e=l.dataPointEOs[w].cumulativeSum/l.dataPointEOs[w-1].cumulativeSum,1>e&&(e=1/e),d.minDiff>e&&1!==e&&(d.minDiff=e)):(e=l.dataPointEOs[w].cumulativeSum-l.dataPointEOs[w-1].cumulativeSum,0>e&&
(e*=-1),d.minDiff>e&&0!==e&&(d.minDiff=e)))),!(c<n)||m){if(!m&&(m=!0,0<w)){w-=2;continue}if(c>f&&!s)s=!0;else if(c>f&&s)continue;l.dataPoints[w].label&&(a.axisX.labels[c]=l.dataPoints[w].label);c<b.viewPortMin&&(b.viewPortMin=c);c>b.viewPortMax&&(b.viewPortMax=c);0<w&&(l.dataPointEOs[w-1].cumulativeSum<d.viewPortMin&&(d.viewPortMin=l.dataPointEOs[w-1].cumulativeSum),l.dataPointEOs[w-1].cumulativeSum>d.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w-1].cumulativeSum));l.dataPointEOs[w].cumulativeSum<
d.viewPortMin&&(d.viewPortMin=l.dataPointEOs[w].cumulativeSum);l.dataPointEOs[w].cumulativeSum>d.viewPortMax&&(d.viewPortMax=l.dataPointEOs[w].cumulativeSum)}l.axisX.valueType=l.xValueType=g?"dateTime":"number"}};p.prototype.calculateAutoBreaks=function(){function a(a,c,b,e){if(e)return b=Math.pow(Math.min(b*a/c,c/a),0.2),1>=b&&(b=Math.pow(1>a?1/a:Math.min(c/a,a),0.25)),{startValue:a*b,endValue:c/b};b=0.2*Math.min(b-c+a,c-a);0>=b&&(b=0.25*Math.min(c-a,Math.abs(a)));return{startValue:a+b,endValue:c-
b}}function d(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){var c=a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<=a.axisX.scaleBreaks.maxNumberOfAutoBreaks,b=a.axisY.scaleBreaks&&a.axisY.scaleBreaks.autoCalculate&&1<=a.axisY.scaleBreaks.maxNumberOfAutoBreaks;if(c||b)for(var d=a.axisY.dataInfo,f=a.axisX.dataInfo,g,m=f.min,l=f.max,k=d.min,n=d.max,f=f._dataRanges,d=d._dataRanges,q,w=0,s=0;s<a.dataSeriesIndexes.length;s++){var h=e.data[a.dataSeriesIndexes[s]];if(!(4>h.dataPoints.length))for(w=
0;w<h.dataPoints.length;w++)if(c&&(q=(l+1-m)*Math.max(parseFloat(a.axisX.scaleBreaks.collapsibleThreshold)||10,10)/100,g=h.dataPoints[w].x.getTime?h.dataPoints[w].x.getTime():h.dataPoints[w].x,q=Math.floor((g-m)/q),g<f[q].min&&(f[q].min=g),g>f[q].max&&(f[q].max=g)),b){var r=(n+1-k)*Math.max(parseFloat(a.axisY.scaleBreaks.collapsibleThreshold)||10,10)/100;if((g="waterfall"===a.type?h.dataPointEOs[w].cumulativeSum:h.dataPoints[w].y)&&g.length)for(var p=0;p<g.length;p++)q=Math.floor((g[p]-k)/r),g[p]<
d[q].min&&(d[q].min=g[p]),g[p]>d[q].max&&(d[q].max=g[p]);else u(g)||(q=Math.floor((g-k)/r),g<d[q].min&&(d[q].min=g),g>d[q].max&&(d[q].max=g))}}}}function b(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)&&a.axisX.scaleBreaks&&a.axisX.scaleBreaks.autoCalculate&&1<=a.axisX.scaleBreaks.maxNumberOfAutoBreaks)for(var c=a.axisX.dataInfo,b=c.min,d=c.max,f=c._dataRanges,g,m=0,l=0;l<a.dataSeriesIndexes.length;l++){var k=e.data[a.dataSeriesIndexes[l]];if(!(4>k.dataPoints.length))for(m=0;m<k.dataPoints.length;m++)g=
(d+1-b)*Math.max(parseFloat(a.axisX.scaleBreaks.collapsibleThreshold)||10,10)/100,c=k.dataPoints[m].x.getTime?k.dataPoints[m].x.getTime():k.dataPoints[m].x,g=Math.floor((c-b)/g),c<f[g].min&&(f[g].min=c),c>f[g].max&&(f[g].max=c)}}for(var c,e=this,g=!1,k=0;k<this._axes.length;k++)if(this._axes[k].scaleBreaks&&this._axes[k].scaleBreaks.autoCalculate&&1<=this._axes[k].scaleBreaks.maxNumberOfAutoBreaks){g=!0;this._axes[k].dataInfo._dataRanges=[];for(var l=0;l<100/Math.max(parseFloat(this._axes[k].scaleBreaks.collapsibleThreshold)||
10,10);l++)this._axes[k].dataInfo._dataRanges.push({min:Infinity,max:-Infinity})}if(g){for(k=0;k<this.plotInfo.plotTypes.length;k++)for(g=this.plotInfo.plotTypes[k],l=0;l<g.plotUnits.length;l++)c=g.plotUnits[l],"line"===c.type||"stepLine"===c.type||"spline"===c.type||"column"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"bar"===c.type||"bubble"===c.type||"scatter"===c.type||"candlestick"===c.type||"ohlc"===c.type||"rangeColumn"===c.type||"rangeBar"===c.type||"rangeArea"===
c.type||"rangeSplineArea"===c.type||"waterfall"===c.type||"error"===c.type||"boxAndWhisker"===c.type?d(c):0<=c.type.indexOf("stacked")&&b(c);for(k=0;k<this._axes.length;k++)if(this._axes[k].dataInfo._dataRanges){var w=this._axes[k].dataInfo.min;c=(this._axes[k].dataInfo.max+1-w)*Math.max(parseFloat(this._axes[k].scaleBreaks.collapsibleThreshold)||10,10)/100;var m=this._axes[k].dataInfo._dataRanges,s,q,g=[];if(this._axes[k].dataInfo.dataPointYPositiveSums){var n=this._axes[k].dataInfo.dataPointYPositiveSums;
s=m;for(l in n)if(n.hasOwnProperty(l)&&!isNaN(l)&&(q=n[l],!u(q))){var f=Math.floor((q-w)/c);q<s[f].min&&(s[f].min=q);q>s[f].max&&(s[f].max=q)}delete this._axes[k].dataInfo.dataPointYPositiveSums}if(this._axes[k].dataInfo.dataPointYNegativeSums){n=this._axes[k].dataInfo.dataPointYNegativeSums;s=m;for(l in n)n.hasOwnProperty(l)&&!isNaN(l)&&(q=-1*n[l],u(q)||(f=Math.floor((q-w)/c),q<s[f].min&&(s[f].min=q),q>s[f].max&&(s[f].max=q)));delete this._axes[k].dataInfo.dataPointYNegativeSums}for(l=0;l<m.length-
1;l++)if(s=m[l].max,isFinite(s))for(;l<m.length-1;)if(w=m[l+1].min,isFinite(w)){q=w-s;q>c&&g.push({diff:q,start:s,end:w});break}else l++;if(this._axes[k].scaleBreaks.customBreaks)for(l=0;l<this._axes[k].scaleBreaks.customBreaks.length;l++)for(c=0;c<g.length;c++)if(this._axes[k].scaleBreaks.customBreaks[l].startValue<=g[c].start&&g[c].start<=this._axes[k].scaleBreaks.customBreaks[l].endValue||this._axes[k].scaleBreaks.customBreaks[l].startValue<=g[c].start&&g[c].start<=this._axes[k].scaleBreaks.customBreaks[l].endValue||
g[c].start<=this._axes[k].scaleBreaks.customBreaks[l].startValue&&this._axes[k].scaleBreaks.customBreaks[l].startValue<=g[c].end||g[c].start<=this._axes[k].scaleBreaks.customBreaks[l].endValue&&this._axes[k].scaleBreaks.customBreaks[l].endValue<=g[c].end)g.splice(c,1),c--;g.sort(function(a,c){return c.diff-a.diff});for(l=0;l<Math.min(g.length,this._axes[k].scaleBreaks.maxNumberOfAutoBreaks);l++)c=a(g[l].start,g[l].end,this._axes[k].logarithmic?this._axes[k].dataInfo.max/this._axes[k].dataInfo.min:
this._axes[k].dataInfo.max-this._axes[k].dataInfo.min,this._axes[k].logarithmic),this._axes[k].scaleBreaks.autoBreaks.push(new L(this,"autoBreaks",c,l,++this._eventManager.lastObjectId,this._axes[k].scaleBreaks)),this._axes[k].scaleBreaks._appliedBreaks.push(this._axes[k].scaleBreaks.autoBreaks[this._axes[k].scaleBreaks.autoBreaks.length-1]);this._axes[k].scaleBreaks._appliedBreaks.sort(function(a,c){return a.startValue-c.startValue})}}};p.prototype.getDataPointAtXY=function(a,d,b){b=b||!1;for(var c=
[],e=this._dataInRenderedOrder.length-1;0<=e;e--){var g=null;(g=this._dataInRenderedOrder[e].getDataPointAtXY(a,d,b))&&c.push(g)}a=null;d=!1;for(b=0;b<c.length;b++)if("line"===c[b].dataSeries.type||"stepLine"===c[b].dataSeries.type||"area"===c[b].dataSeries.type||"stepArea"===c[b].dataSeries.type)if(e=na("markerSize",c[b].dataPoint,c[b].dataSeries)||8,c[b].distance<=e/2){d=!0;break}for(b=0;b<c.length;b++)d&&"line"!==c[b].dataSeries.type&&"stepLine"!==c[b].dataSeries.type&&"area"!==c[b].dataSeries.type&&
"stepArea"!==c[b].dataSeries.type||(a?c[b].distance<=a.distance&&(a=c[b]):a=c[b]);return a};p.prototype.getObjectAtXY=function(a,d,b){var c=null;if(b=this.getDataPointAtXY(a,d,b||!1))c=b.dataSeries.dataPointIds[b.dataPointIndex];else if(r)c=ab(a,d,this._eventManager.ghostCtx);else for(b=0;b<this.legend.items.length;b++){var e=this.legend.items[b];a>=e.x1&&(a<=e.x2&&d>=e.y1&&d<=e.y2)&&(c=e.id)}return c};p.prototype.getAutoFontSize=lb;p.prototype.resetOverlayedCanvas=function(){this.overlaidCanvasCtx.clearRect(0,
0,this.width,this.height)};p.prototype.clearCanvas=kb;p.prototype.attachEvent=function(a){this._events.push(a)};p.prototype._touchEventHandler=function(a){if(a.changedTouches&&this.interactivityEnabled){var d=[],b=a.changedTouches,c=b?b[0]:a,e=null;switch(a.type){case "touchstart":case "MSPointerDown":d=["mousemove","mousedown"];this._lastTouchData=Ra(c);this._lastTouchData.time=new Date;break;case "touchmove":case "MSPointerMove":d=["mousemove"];break;case "touchend":case "MSPointerUp":var g=this._lastTouchData&&
this._lastTouchData.time?new Date-this._lastTouchData.time:0,d="touchstart"===this._lastTouchEventType||"MSPointerDown"===this._lastTouchEventType||300>g?["mouseup","click"]:["mouseup"];break;default:return}if(!(b&&1<b.length)){e=Ra(c);e.time=new Date;try{var k=e.y-this._lastTouchData.y,g=e.time-this._lastTouchData.time;if(1<Math.abs(k)&&this._lastTouchData.scroll||5<Math.abs(k)&&250>g)this._lastTouchData.scroll=!0}catch(l){}this._lastTouchEventType=a.type;if(this._lastTouchData.scroll&&this.zoomEnabled)this.isDrag&&
this.resetOverlayedCanvas(),this.isDrag=!1;else for(b=0;b<d.length;b++)if(e=d[b],k=document.createEvent("MouseEvent"),k.initMouseEvent(e,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),c.target.dispatchEvent(k),!u(this._lastTouchData.scroll)&&!this._lastTouchData.scroll||!this._lastTouchData.scroll&&250<g||"click"===e)a.preventManipulation&&a.preventManipulation(),a.preventDefault&&a.preventDefault()}}};p.prototype._dispatchRangeEvent=function(a,d){var b={chart:this};b.type=
a;b.trigger=d;var c=[];this.axisX&&0<this.axisX.length&&c.push("axisX");this.axisX2&&0<this.axisX2.length&&c.push("axisX2");this.axisY&&0<this.axisY.length&&c.push("axisY");this.axisY2&&0<this.axisY2.length&&c.push("axisY2");for(var e=0;e<c.length;e++)if(u(b[c[e]])&&(b[c[e]]=[]),"axisY"===c[e])for(var g=0;g<this.axisY.length;g++)b[c[e]].push({viewportMinimum:this[c[e]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[c[e]][g].sessionVariables.newViewportMaximum});else if("axisY2"===c[e])for(g=
0;g<this.axisY2.length;g++)b[c[e]].push({viewportMinimum:this[c[e]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[c[e]][g].sessionVariables.newViewportMaximum});else if("axisX"===c[e])for(g=0;g<this.axisX.length;g++)b[c[e]].push({viewportMinimum:this[c[e]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[c[e]][g].sessionVariables.newViewportMaximum});else if("axisX2"===c[e])for(g=0;g<this.axisX2.length;g++)b[c[e]].push({viewportMinimum:this[c[e]][g].sessionVariables.newViewportMinimum,
viewportMaximum:this[c[e]][g].sessionVariables.newViewportMaximum});this.dispatchEvent(a,b,this)};p.prototype._mouseEventHandler=function(a){"undefined"===typeof a.target&&a.srcElement&&(a.target=a.srcElement);var d=Ra(a),b=a.type,c,e;a.which?e=3==a.which:a.button&&(e=2==a.button);p.capturedEventParam&&(c=p.capturedEventParam,"mouseup"===b&&(p.capturedEventParam=null,c.chart.overlaidCanvas.releaseCapture?c.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",
c.chart._mouseEventHandler,!1)),c.hasOwnProperty(b)&&("mouseup"!==b||c.chart.overlaidCanvas.releaseCapture?a.target!==c.chart.overlaidCanvas&&r||c[b].call(c.context,d.x,d.y):a.target!==c.chart.overlaidCanvas&&(c.chart.isDrag=!1)));if(this.interactivityEnabled)if(this._ignoreNextEvent)this._ignoreNextEvent=!1;else if(a.preventManipulation&&a.preventManipulation(),a.preventDefault&&a.preventDefault(),Ja&&window.console&&(window.console.log(b+" --\x3e x: "+d.x+"; y:"+d.y),e&&window.console.log(a.which),
"mouseup"===b&&window.console.log("mouseup")),!e){if(!p.capturedEventParam&&this._events){for(var g=0;g<this._events.length;g++)if(this._events[g].hasOwnProperty(b))if(c=this._events[g],e=c.bounds,d.x>=e.x1&&d.x<=e.x2&&d.y>=e.y1&&d.y<=e.y2){c[b].call(c.context,d.x,d.y);"mousedown"===b&&!0===c.capture?(p.capturedEventParam=c,this.overlaidCanvas.setCapture?this.overlaidCanvas.setCapture():document.documentElement.addEventListener("mouseup",this._mouseEventHandler,!1)):"mouseup"===b&&(c.chart.overlaidCanvas.releaseCapture?
c.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",this._mouseEventHandler,!1));break}else c=null;a.target.style.cursor=c&&c.cursor?c.cursor:this._defaultCursor}b=this.plotArea;if(d.x<b.x1||d.x>b.x2||d.y<b.y1||d.y>b.y2)this.toolTip&&this.toolTip.enabled?this.toolTip.hide():this.resetOverlayedCanvas();this.isDrag&&this.zoomEnabled||!this._eventManager||this._eventManager.mouseEventHandler(a)}};p.prototype._plotAreaMouseDown=function(a,d){this.isDrag=!0;this.dragStartPoint=
{x:a,y:d}};p.prototype._plotAreaMouseUp=function(a,d){if(("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)&&this.isDrag){var b=d-this.dragStartPoint.y,c=a-this.dragStartPoint.x,e=0<=this.zoomType.indexOf("x"),g=0<=this.zoomType.indexOf("y"),k=!1;this.resetOverlayedCanvas();if("xySwapped"===this.plotInfo.axisPlacement)var l=g,g=e,e=l;if(this.panEnabled||this.zoomEnabled){if(this.panEnabled)for(e=g=0;e<this._axes.length;e++)b=this._axes[e],b.logarithmic?b.viewportMinimum<
b.minimum?(g=b.minimum/b.viewportMinimum,b.sessionVariables.newViewportMinimum=b.viewportMinimum*g,b.sessionVariables.newViewportMaximum=b.viewportMaximum*g,k=!0):b.viewportMaximum>b.maximum&&(g=b.viewportMaximum/b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum/g,b.sessionVariables.newViewportMaximum=b.viewportMaximum/g,k=!0):b.viewportMinimum<b.minimum?(g=b.minimum-b.viewportMinimum,b.sessionVariables.newViewportMinimum=b.viewportMinimum+g,b.sessionVariables.newViewportMaximum=b.viewportMaximum+
g,k=!0):b.viewportMaximum>b.maximum&&(g=b.viewportMaximum-b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum-g,b.sessionVariables.newViewportMaximum=b.viewportMaximum-g,k=!0);else if((!e||2<Math.abs(c))&&(!g||2<Math.abs(b))&&this.zoomEnabled){if(!this.dragStartPoint)return;b=e?this.dragStartPoint.x:this.plotArea.x1;c=g?this.dragStartPoint.y:this.plotArea.y1;e=e?a:this.plotArea.x2;g=g?d:this.plotArea.y2;2<Math.abs(b-e)&&2<Math.abs(c-g)&&this._zoomPanToSelectedRegion(b,c,e,g)&&(k=!0)}k&&
(this._ignoreNextEvent=!0,this._dispatchRangeEvent("rangeChanging","zoom"),this.render(),this._dispatchRangeEvent("rangeChanged","zoom"),k&&(this.zoomEnabled&&"none"===this._zoomButton.style.display)&&(Qa(this._zoomButton,this._resetButton),ua(this,this._zoomButton,"pan"),ua(this,this._resetButton,"reset")))}}this.isDrag=!1;if("none"!==this.plotInfo.axisPlacement){this.resetOverlayedCanvas();if(this.axisX&&0<this.axisX.length)for(k=0;k<this.axisX.length;k++)this.axisX[k].crosshair&&this.axisX[k].crosshair.enabled&&
this.axisX[k].renderCrosshair(a,d);if(this.axisX2&&0<this.axisX2.length)for(k=0;k<this.axisX2.length;k++)this.axisX2[k].crosshair&&this.axisX2[k].crosshair.enabled&&this.axisX2[k].renderCrosshair(a,d);if(this.axisY&&0<this.axisY.length)for(k=0;k<this.axisY.length;k++)this.axisY[k].crosshair&&this.axisY[k].crosshair.enabled&&this.axisY[k].renderCrosshair(a,d);if(this.axisY2&&0<this.axisY2.length)for(k=0;k<this.axisY2.length;k++)this.axisY2[k].crosshair&&this.axisY2[k].crosshair.enabled&&this.axisY2[k].renderCrosshair(a,
d)}};p.prototype._plotAreaMouseMove=function(a,d){if(this.isDrag&&"none"!==this.plotInfo.axisPlacement){var b=0,c=0,e=b=null,e=0<=this.zoomType.indexOf("x"),g=0<=this.zoomType.indexOf("y"),k=this;"xySwapped"===this.plotInfo.axisPlacement&&(b=g,g=e,e=b);b=this.dragStartPoint.x-a;c=this.dragStartPoint.y-d;2<Math.abs(b)&&8>Math.abs(b)&&(this.panEnabled||this.zoomEnabled)?this.toolTip.hide():this.panEnabled||this.zoomEnabled||this.toolTip.mouseMoveHandler(a,d);if((!e||2<Math.abs(b)||!g||2<Math.abs(c))&&
(this.panEnabled||this.zoomEnabled))if(this.panEnabled)e={x1:e?this.plotArea.x1+b:this.plotArea.x1,y1:g?this.plotArea.y1+c:this.plotArea.y1,x2:e?this.plotArea.x2+b:this.plotArea.x2,y2:g?this.plotArea.y2+c:this.plotArea.y2},clearTimeout(k._panTimerId),k._panTimerId=setTimeout(function(c,b,e,f){return function(){k._zoomPanToSelectedRegion(c,b,e,f,!0)&&(k._dispatchRangeEvent("rangeChanging","pan"),k.render(),k._dispatchRangeEvent("rangeChanged","pan"),k.dragStartPoint.x=a,k.dragStartPoint.y=d)}}(e.x1,
e.y1,e.x2,e.y2),0);else if(this.zoomEnabled){this.resetOverlayedCanvas();b=this.overlaidCanvasCtx.globalAlpha;this.overlaidCanvasCtx.fillStyle="#A89896";var c=e?this.dragStartPoint.x:this.plotArea.x1,l=g?this.dragStartPoint.y:this.plotArea.y1,w=e?a-this.dragStartPoint.x:this.plotArea.x2-this.plotArea.x1,m=g?d-this.dragStartPoint.y:this.plotArea.y2-this.plotArea.y1;this.validateRegion(c,l,e?a:this.plotArea.x2-this.plotArea.x1,g?d:this.plotArea.y2-this.plotArea.y1,"xy"!==this.zoomType).isValid&&(this.resetOverlayedCanvas(),
this.overlaidCanvasCtx.fillStyle="#99B2B5");this.overlaidCanvasCtx.globalAlpha=0.7;this.overlaidCanvasCtx.fillRect(c,l,w,m);this.overlaidCanvasCtx.globalAlpha=b}}else if(this.toolTip.mouseMoveHandler(a,d),"none"!==this.plotInfo.axisPlacement){if(this.axisX&&0<this.axisX.length)for(e=0;e<this.axisX.length;e++)this.axisX[e].crosshair&&this.axisX[e].crosshair.enabled&&this.axisX[e].renderCrosshair(a,d);if(this.axisX2&&0<this.axisX2.length)for(e=0;e<this.axisX2.length;e++)this.axisX2[e].crosshair&&this.axisX2[e].crosshair.enabled&&
this.axisX2[e].renderCrosshair(a,d);if(this.axisY&&0<this.axisY.length)for(e=0;e<this.axisY.length;e++)this.axisY[e].crosshair&&this.axisY[e].crosshair.enabled&&this.axisY[e].renderCrosshair(a,d);if(this.axisY2&&0<this.axisY2.length)for(e=0;e<this.axisY2.length;e++)this.axisY2[e].crosshair&&this.axisY2[e].crosshair.enabled&&this.axisY2[e].renderCrosshair(a,d)}};p.prototype._zoomPanToSelectedRegion=function(a,d,b,c,e){a=this.validateRegion(a,d,b,c,e);d=a.axesWithValidRange;b=a.axesRanges;if(a.isValid)for(c=
0;c<d.length;c++)e=b[c],d[c].setViewPortRange(e.val1,e.val2),this.syncCharts&&this.syncCharts(e.val1,e.val2);return a.isValid};p.prototype.validateRegion=function(a,d,b,c,e){e=e||!1;for(var g=0<=this.zoomType.indexOf("x"),k=0<=this.zoomType.indexOf("y"),l=!1,w=[],m=[],s=[],q=0;q<this._axes.length;q++)("axisX"===this._axes[q].type&&g||"axisY"===this._axes[q].type&&k)&&m.push(this._axes[q]);for(k=0;k<m.length;k++){var q=m[k],g=!1,n=q.convertPixelToValue({x:a,y:d}),f=q.convertPixelToValue({x:b,y:c});
if(n>f)var B=f,f=n,n=B;if(q.scaleBreaks)for(B=0;!g&&B<q.scaleBreaks._appliedBreaks.length;B++)g=q.scaleBreaks._appliedBreaks[B].startValue<=n&&q.scaleBreaks._appliedBreaks[B].endValue>=f;if(isFinite(q.dataInfo.minDiff))if(B=q.getApparentDifference(n,f,null,!0),!(g||!(this.panEnabled&&q.scaleBreaks&&q.scaleBreaks._appliedBreaks.length)&&(q.logarithmic&&B<Math.pow(q.dataInfo.minDiff,3)||!q.logarithmic&&B<3*Math.abs(q.dataInfo.minDiff))||n<q.minimum||f>q.maximum))w.push(q),s.push({val1:n,val2:f}),l=
!0;else if(!e){l=!1;break}}return{isValid:l,axesWithValidRange:w,axesRanges:s}};p.prototype.preparePlotArea=function(){var a=this.plotArea;!r&&(0<a.x1||0<a.y1)&&a.ctx.translate(a.x1,a.y1);if((this.axisX[0]||this.axisX2[0])&&(this.axisY[0]||this.axisY2[0])){var d=this.axisX[0]?this.axisX[0].lineCoordinates:this.axisX2[0].lineCoordinates;if(this.axisY&&0<this.axisY.length&&this.axisY[0]){var b=this.axisY[0];a.x1=d.x1<d.x2?d.x1:b.lineCoordinates.x1;a.y1=d.y1<b.lineCoordinates.y1?d.y1:b.lineCoordinates.y1;
a.x2=d.x2>b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2;a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2;a.width=a.x2-a.x1;a.height=a.y2-a.y1}this.axisY2&&0<this.axisY2.length&&this.axisY2[0]&&(b=this.axisY2[0],a.x1=d.x1<d.x2?d.x1:b.lineCoordinates.x1,a.y1=d.y1<b.lineCoordinates.y1?d.y1:b.lineCoordinates.y1,a.x2=d.x2>b.lineCoordinates.x2?d.x2:b.lineCoordinates.x2,a.y2=d.y2>d.y1?d.y2:b.lineCoordinates.y2,a.width=a.x2-a.x1,a.height=a.y2-a.y1)}else d=this.layoutManager.getFreeSpace(),a.x1=d.x1,a.x2=d.x2,a.y1=
d.y1,a.y2=d.y2,a.width=d.width,a.height=d.height;r||(a.canvas.width=a.width,a.canvas.height=a.height,a.canvas.style.left=a.x1+"px",a.canvas.style.top=a.y1+"px",(0<a.x1||0<a.y1)&&a.ctx.translate(-a.x1,-a.y1));a.layoutManager=new Ga(a.x1,a.y1,a.x2,a.y2,2)};p.prototype.renderIndexLabels=function(a){var d=a||this.plotArea.ctx,b=this.plotArea,c=0,e=0,g=0,k=0,l=c=k=e=g=0,w=0;for(a=0;a<this._indexLabels.length;a++){var m=this._indexLabels[a],s=m.chartType.toLowerCase(),q,n,l=na("indexLabelFontColor",m.dataPoint,
m.dataSeries),w=na("indexLabelFontSize",m.dataPoint,m.dataSeries);q=na("indexLabelFontFamily",m.dataPoint,m.dataSeries);n=na("indexLabelFontStyle",m.dataPoint,m.dataSeries);var k=na("indexLabelFontWeight",m.dataPoint,m.dataSeries),f=na("indexLabelBackgroundColor",m.dataPoint,m.dataSeries),e=na("indexLabelMaxWidth",m.dataPoint,m.dataSeries),g=na("indexLabelWrap",m.dataPoint,m.dataSeries),B=na("indexLabelLineDashType",m.dataPoint,m.dataSeries),h=na("indexLabelLineColor",m.dataPoint,m.dataSeries),p=
u(m.dataPoint.indexLabelLineThickness)?u(m.dataSeries.options.indexLabelLineThickness)?0:m.dataSeries.options.indexLabelLineThickness:m.dataPoint.indexLabelLineThickness,c=0<p?Math.min(10,("normal"===this.plotInfo.axisPlacement?this.plotArea.height:this.plotArea.width)<<0):0,t={percent:null,total:null},C=null;if(0<=m.dataSeries.type.indexOf("stacked")||"pie"===m.dataSeries.type||"doughnut"===m.dataSeries.type)t=this.getPercentAndTotal(m.dataSeries,m.dataPoint);if(m.dataSeries.indexLabelFormatter||
m.dataPoint.indexLabelFormatter)C={chart:this,dataSeries:m.dataSeries,dataPoint:m.dataPoint,index:m.indexKeyword,total:t.total,percent:t.percent};var x=m.dataPoint.indexLabelFormatter?m.dataPoint.indexLabelFormatter(C):m.dataPoint.indexLabel?this.replaceKeywordsWithValue(m.dataPoint.indexLabel,m.dataPoint,m.dataSeries,null,m.indexKeyword):m.dataSeries.indexLabelFormatter?m.dataSeries.indexLabelFormatter(C):m.dataSeries.indexLabel?this.replaceKeywordsWithValue(m.dataSeries.indexLabel,m.dataPoint,m.dataSeries,
null,m.indexKeyword):null;if(null!==x&&""!==x){var t=na("indexLabelPlacement",m.dataPoint,m.dataSeries),C=na("indexLabelOrientation",m.dataPoint,m.dataSeries),ma=m.direction,y=m.dataSeries.axisX,A=m.dataSeries.axisY,v=!1,f=new ka(d,{x:0,y:0,maxWidth:e?e:0.5*this.width,maxHeight:g?5*w:1.5*w,angle:"horizontal"===C?0:-90,text:x,padding:0,backgroundColor:f,horizontalAlign:"left",fontSize:w,fontFamily:q,fontWeight:k,fontColor:l,fontStyle:n,textBaseline:"top"});f.measureText();m.dataSeries.indexLabelMaxWidth=
f.maxWidth;if("stackedarea100"===s){if(m.point.x<b.x1||m.point.x>b.x2||m.point.y<b.y1-1||m.point.y>b.y2+1)continue}else if("rangearea"===s||"rangesplinearea"===s){if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>y.viewportMaximum||Math.max.apply(null,m.dataPoint.y)<A.viewportMinimum||Math.min.apply(null,m.dataPoint.y)>A.viewportMaximum)continue}else if(0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("bubble")||0<=s.indexOf("scatter")){if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>y.viewportMaximum||
m.dataPoint.y<A.viewportMinimum||m.dataPoint.y>A.viewportMaximum)continue}else if(0<=s.indexOf("column")||"waterfall"===s||"error"===s&&!m.axisSwapped){if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>y.viewportMaximum||m.bounds.y1>b.y2||m.bounds.y2<b.y1)continue}else if(0<=s.indexOf("bar")||"error"===s){if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>y.viewportMaximum||m.bounds.x1>b.x2||m.bounds.x2<b.x1)continue}else if("candlestick"===s||"ohlc"===s){if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>
y.viewportMaximum||Math.max.apply(null,m.dataPoint.y)<A.viewportMinimum||Math.min.apply(null,m.dataPoint.y)>A.viewportMaximum)continue}else if(m.dataPoint.x<y.viewportMinimum||m.dataPoint.x>y.viewportMaximum)continue;e=k=2;"horizontal"===C?(l=f.width,w=f.height):(w=f.width,l=f.height);if("normal"===this.plotInfo.axisPlacement){if(0<=s.indexOf("line")||0<=s.indexOf("area"))t="auto",k=4;else if(0<=s.indexOf("stacked"))"auto"===t&&(t="inside");else if("bubble"===s||"scatter"===s)t="inside";q=m.point.x-
l/2;"inside"!==t?(e=b.y1,g=b.y2,0<ma?(n=m.point.y-w-k-c,n<e&&(n="auto"===t?Math.max(m.point.y,e)+k+c:e+k+c,v=n+w>m.point.y)):(n=m.point.y+k+c,n>g-w-k-c&&(n="auto"===t?Math.min(m.point.y,g)-w-k-c:g-w-k-c,v=n<m.point.y))):(e=Math.max(m.bounds.y1,b.y1),g=Math.min(m.bounds.y2,b.y2),c=0<=s.indexOf("range")||"error"===s?0<ma?Math.max(m.bounds.y1,b.y1)+w/2+k:Math.min(m.bounds.y2,b.y2)-w/2-k:(Math.max(m.bounds.y1,b.y1)+Math.min(m.bounds.y2,b.y2))/2,0<ma?(n=Math.max(m.point.y,c)-w/2,n<e&&("bubble"===s||"scatter"===
s)&&(n=Math.max(m.point.y-w-k,b.y1+k))):(n=Math.min(m.point.y,c)-w/2,n>g-w-k&&("bubble"===s||"scatter"===s)&&(n=Math.min(m.point.y+k,b.y2-w-k))),n=Math.min(n,g-w))}else 0<=s.indexOf("line")||0<=s.indexOf("area")||0<=s.indexOf("scatter")?(t="auto",e=4):0<=s.indexOf("stacked")?"auto"===t&&(t="inside"):"bubble"===s&&(t="inside"),n=m.point.y-w/2,"inside"!==t?(k=b.x1,g=b.x2,0>ma?(q=m.point.x-l-e-c,q<k&&(q="auto"===t?Math.max(m.point.x,k)+e+c:k+e+c,v=q+l>m.point.x)):(q=m.point.x+e+c,q>g-l-e-c&&(q="auto"===
t?Math.min(m.point.x,g)-l-e-c:g-l-e-c,v=q<m.point.x))):(k=Math.max(m.bounds.x1,b.x1),Math.min(m.bounds.x2,b.x2),c=0<=s.indexOf("range")||"error"===s?0>ma?Math.max(m.bounds.x1,b.x1)+l/2+e:Math.min(m.bounds.x2,b.x2)-l/2-e:(Math.max(m.bounds.x1,b.x1)+Math.min(m.bounds.x2,b.x2))/2,q=0>ma?Math.max(m.point.x,c)-l/2:Math.min(m.point.x,c)-l/2,q=Math.max(q,k));"vertical"===C&&(n+=w);f.x=q;f.y=n;f.render(!0);p&&("inside"!==t&&(0>s.indexOf("bar")&&("error"!==s||!m.axisSwapped)&&m.point.x>b.x1&&m.point.x<b.x2||
!v)&&(0>s.indexOf("column")&&("error"!==s||m.axisSwapped)&&m.point.y>b.y1&&m.point.y<b.y2||!v))&&(d.lineWidth=p,d.strokeStyle=h?h:"gray",d.setLineDash&&d.setLineDash(R(B,p)),d.beginPath(),d.moveTo(m.point.x,m.point.y),0<=s.indexOf("bar")||"error"===s&&m.axisSwapped?d.lineTo(q+(0<m.direction?0:l),n+("horizontal"===C?w:-w)/2):0<=s.indexOf("column")||"error"===s&&!m.axisSwapped?d.lineTo(q+l/2,n+((0<m.direction?w:-w)+("horizontal"===C?w:-w))/2):d.lineTo(q+l/2,n+((n<m.point.y?w:-w)+("horizontal"===C?w:
-w))/2),d.stroke())}}d={source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0,startTimePercent:0.7};for(a=0;a<this._indexLabels.length;a++)m=this._indexLabels[a],f=na("indexLabelBackgroundColor",m.dataPoint,m.dataSeries),m.dataSeries.indexLabelBackgroundColor=u(f)?r?"transparent":null:f;return d};p.prototype.renderLine=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=
this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],k,l=0;l<a.dataSeriesIndexes.length;l++){var w=a.dataSeriesIndexes[l],m=this.data[w];b.lineWidth=m.lineThickness;var s=m.dataPoints,q="solid";if(b.setLineDash){var n=R(m.nullDataLineDashType,m.lineThickness),q=m.lineDashType,f=R(q,m.lineThickness);b.setLineDash(f)}var B=m.id;this._eventManager.objectMap[B]={objectType:"dataSeries",dataSeriesIndex:w};B=N(B);c.strokeStyle=B;
c.lineWidth=0<m.lineThickness?Math.max(m.lineThickness,4):0;var B=m._colorSet,h=B=m.lineColor=m.options.lineColor?m.options.lineColor:B[0];b.strokeStyle=B;var p=!0,t=0,C,x;b.beginPath();if(0<s.length){for(var u=!1,t=0;t<s.length;t++)if(C=s[t].x.getTime?s[t].x.getTime():s[t].x,!(C<a.axisX.dataInfo.viewPortMin||C>a.axisX.dataInfo.viewPortMax&&(!m.connectNullData||!u)))if("number"!==typeof s[t].y)0<t&&!(m.connectNullData||u||p)&&(b.stroke(),r&&c.stroke()),u=!0;else{C=a.axisX.convertValueToPixel(C);x=
a.axisY.convertValueToPixel(s[t].y);var y=m.dataPointIds[t];this._eventManager.objectMap[y]={id:y,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:t,x1:C,y1:x};p||u?(!p&&m.connectNullData?(b.setLineDash&&(m.options.nullDataLineDashType||q===m.lineDashType&&m.lineDashType!==m.nullDataLineDashType)&&(b.stroke(),b.beginPath(),b.moveTo(k.x,k.y),q=m.nullDataLineDashType,b.setLineDash(n)),b.lineTo(C,x),r&&c.lineTo(C,x)):(b.beginPath(),b.moveTo(C,x),r&&(c.beginPath(),c.moveTo(C,x))),u=p=!1):(b.lineTo(C,
x),r&&c.lineTo(C,x),0==t%500&&(b.stroke(),b.beginPath(),b.moveTo(C,x),r&&(c.stroke(),c.beginPath(),c.moveTo(C,x))));k={x:C,y:x};t<s.length-1&&(h!==(s[t].lineColor||B)||q!==(s[t].lineDashType||m.lineDashType))&&(b.stroke(),b.beginPath(),b.moveTo(C,x),h=s[t].lineColor||B,b.strokeStyle=h,b.setLineDash&&(s[t].lineDashType?(q=s[t].lineDashType,b.setLineDash(R(q,m.lineThickness))):(q=m.lineDashType,b.setLineDash(f))));if(0<s[t].markerSize||0<m.markerSize){var A=m.getMarkerProperties(t,C,x,b);g.push(A);
y=N(y);r&&g.push({x:C,y:x,ctx:c,type:A.type,size:A.size,color:y,borderColor:y,borderThickness:A.borderThickness})}(s[t].indexLabel||m.indexLabel||s[t].indexLabelFormatter||m.indexLabelFormatter)&&this._indexLabels.push({chartType:"line",dataPoint:s[t],dataSeries:m,point:{x:C,y:x},direction:0>s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&
b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepLine=function(a){var d=a.targetCanvasCtx||
this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx;b.save();var e=this.plotArea;b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();for(var g=[],k,l=0;l<a.dataSeriesIndexes.length;l++){var w=a.dataSeriesIndexes[l],m=this.data[w];b.lineWidth=m.lineThickness;var s=m.dataPoints,q="solid";if(b.setLineDash){var n=R(m.nullDataLineDashType,m.lineThickness),q=m.lineDashType,f=R(q,m.lineThickness);b.setLineDash(f)}var B=m.id;this._eventManager.objectMap[B]=
{objectType:"dataSeries",dataSeriesIndex:w};B=N(B);c.strokeStyle=B;c.lineWidth=0<m.lineThickness?Math.max(m.lineThickness,4):0;var B=m._colorSet,h=B=m.lineColor=m.options.lineColor?m.options.lineColor:B[0];b.strokeStyle=B;var p=!0,t=0,C,x;b.beginPath();if(0<s.length){for(var u=!1,t=0;t<s.length;t++)if(C=s[t].getTime?s[t].x.getTime():s[t].x,!(C<a.axisX.dataInfo.viewPortMin||C>a.axisX.dataInfo.viewPortMax&&(!m.connectNullData||!u)))if("number"!==typeof s[t].y)0<t&&!(m.connectNullData||u||p)&&(b.stroke(),
r&&c.stroke()),u=!0;else{var y=x;C=a.axisX.convertValueToPixel(C);x=a.axisY.convertValueToPixel(s[t].y);var A=m.dataPointIds[t];this._eventManager.objectMap[A]={id:A,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:t,x1:C,y1:x};p||u?(!p&&m.connectNullData?(b.setLineDash&&(m.options.nullDataLineDashType||q===m.lineDashType&&m.lineDashType!==m.nullDataLineDashType)&&(b.stroke(),b.beginPath(),b.moveTo(k.x,k.y),q=m.nullDataLineDashType,b.setLineDash(n)),b.lineTo(C,y),b.lineTo(C,x),r&&(c.lineTo(C,
y),c.lineTo(C,x))):(b.beginPath(),b.moveTo(C,x),r&&(c.beginPath(),c.moveTo(C,x))),u=p=!1):(b.lineTo(C,y),r&&c.lineTo(C,y),b.lineTo(C,x),r&&c.lineTo(C,x),0==t%500&&(b.stroke(),b.beginPath(),b.moveTo(C,x),r&&(c.stroke(),c.beginPath(),c.moveTo(C,x))));k={x:C,y:x};t<s.length-1&&(h!==(s[t].lineColor||B)||q!==(s[t].lineDashType||m.lineDashType))&&(b.stroke(),b.beginPath(),b.moveTo(C,x),h=s[t].lineColor||B,b.strokeStyle=h,b.setLineDash&&(s[t].lineDashType?(q=s[t].lineDashType,b.setLineDash(R(q,m.lineThickness))):
(q=m.lineDashType,b.setLineDash(f))));if(0<s[t].markerSize||0<m.markerSize)y=m.getMarkerProperties(t,C,x,b),g.push(y),A=N(A),r&&g.push({x:C,y:x,ctx:c,type:y.type,size:y.size,color:A,borderColor:A,borderThickness:y.borderThickness});(s[t].indexLabel||m.indexLabel||s[t].indexLabelFormatter||m.indexLabelFormatter)&&this._indexLabels.push({chartType:"stepLine",dataPoint:s[t],dataSeries:m,point:{x:C,y:x},direction:0>s[t].y===a.axisY.reversed?1:-1,color:B})}b.stroke();r&&c.stroke()}}ia.drawMarkers(g);r&&
(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),c.beginPath());b.restore();b.beginPath();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,
easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderSpline=function(a){function d(a){a=v(a,2);if(0<a.length){c.beginPath();r&&e.beginPath();c.moveTo(a[0].x,a[0].y);a[0].newStrokeStyle&&(c.strokeStyle=a[0].newStrokeStyle);a[0].newLineDashArray&&c.setLineDash(a[0].newLineDashArray);r&&e.moveTo(a[0].x,a[0].y);for(var b=0;b<a.length-3;b+=3)if(c.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y),r&&e.bezierCurveTo(a[b+1].x,a[b+1].y,a[b+2].x,a[b+2].y,a[b+3].x,a[b+3].y),
0<b&&0===b%3E3||a[b+3].newStrokeStyle||a[b+3].newLineDashArray)c.stroke(),c.beginPath(),c.moveTo(a[b+3].x,a[b+3].y),a[b+3].newStrokeStyle&&(c.strokeStyle=a[b+3].newStrokeStyle),a[b+3].newLineDashArray&&c.setLineDash(a[b+3].newLineDashArray),r&&(e.stroke(),e.beginPath(),e.moveTo(a[b+3].x,a[b+3].y));c.stroke();r&&e.stroke()}}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx;c.save();var g=this.plotArea;c.beginPath();
c.rect(g.x1,g.y1,g.width,g.height);c.clip();for(var k=[],l=0;l<a.dataSeriesIndexes.length;l++){var w=a.dataSeriesIndexes[l],m=this.data[w];c.lineWidth=m.lineThickness;var s=m.dataPoints,q="solid";if(c.setLineDash){var n=R(m.nullDataLineDashType,m.lineThickness),q=m.lineDashType,f=R(q,m.lineThickness);c.setLineDash(f)}var B=m.id;this._eventManager.objectMap[B]={objectType:"dataSeries",dataSeriesIndex:w};B=N(B);e.strokeStyle=B;e.lineWidth=0<m.lineThickness?Math.max(m.lineThickness,4):0;var B=m._colorSet,
h=B=m.lineColor=m.options.lineColor?m.options.lineColor:B[0];c.strokeStyle=B;var p=0,t,u,x=[];c.beginPath();if(0<s.length)for(u=!1,p=0;p<s.length;p++)if(t=s[p].getTime?s[p].x.getTime():s[p].x,!(t<a.axisX.dataInfo.viewPortMin||t>a.axisX.dataInfo.viewPortMax&&(!m.connectNullData||!u)))if("number"!==typeof s[p].y)0<p&&!u&&(m.connectNullData?c.setLineDash&&(0<x.length&&(m.options.nullDataLineDashType||!s[p-1].lineDashType))&&(x[x.length-1].newLineDashArray=n,q=m.nullDataLineDashType):(d(x),x=[])),u=!0;
else{t=a.axisX.convertValueToPixel(t);u=a.axisY.convertValueToPixel(s[p].y);var ma=m.dataPointIds[p];this._eventManager.objectMap[ma]={id:ma,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:p,x1:t,y1:u};x[x.length]={x:t,y:u};p<s.length-1&&(h!==(s[p].lineColor||B)||q!==(s[p].lineDashType||m.lineDashType))&&(h=s[p].lineColor||B,x[x.length-1].newStrokeStyle=h,c.setLineDash&&(s[p].lineDashType?(q=s[p].lineDashType,x[x.length-1].newLineDashArray=R(q,m.lineThickness)):(q=m.lineDashType,x[x.length-
1].newLineDashArray=f)));if(0<s[p].markerSize||0<m.markerSize){var y=m.getMarkerProperties(p,t,u,c);k.push(y);ma=N(ma);r&&k.push({x:t,y:u,ctx:e,type:y.type,size:y.size,color:ma,borderColor:ma,borderThickness:y.borderThickness})}(s[p].indexLabel||m.indexLabel||s[p].indexLabelFormatter||m.indexLabelFormatter)&&this._indexLabels.push({chartType:"spline",dataPoint:s[p],dataSeries:m,point:{x:t,y:u},direction:0>s[p].y===a.axisY.reversed?1:-1,color:B});u=!1}d(x)}ia.drawMarkers(k);r&&(b.drawImage(this._preRenderCanvas,
0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(g.x1,g.y1,g.width,g.height),e.beginPath());c.restore();c.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,
animationBase:0}}};p.prototype.renderColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,k,l,w,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width,
0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.dataPointWidth=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&s<g)&&(s=Math.max(this.options.dataPointWidth?
this.dataPointWidth:-Infinity,g));q<g&&(q=g);q>s&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;s<a.dataSeriesIndexes.length;s++){var n=a.dataSeriesIndexes[s],f=this.data[n],B=f.dataPoints;if(0<B.length)for(var p=5<q&&f.bevelEnabled?!0:!1,g=0;g<B.length;g++)if(B[g].getTime?w=B[g].x.getTime():
w=B[g].x,!(w<a.axisX.dataInfo.viewPortMin||w>a.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){k=a.axisX.convertValueToPixel(w);l=a.axisY.convertValueToPixel(B[g].y);k=a.axisX.reversed?k+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:k-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var h=a.axisX.reversed?k-q<<0:k+q<<0,t;0<=B[g].y?t=m:(t=l,l=m);l>t&&(c=l,l=t,t=c);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,k,l,h,t,c,0,null,p&&0<=B[g].y,
0>B[g].y&&p,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:k,y1:l,x2:h,y2:t};c=N(c);r&&ea(this._eventManager.ghostCtx,k,l,h,t,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"column",dataPoint:B[g],dataSeries:f,point:{x:k+(h-k)/2,y:0>B[g].y===a.axisY.reversed?l:t},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:k,
y1:Math.min(l,t),x2:h,y2:Math.max(l,t)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();
return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:m<a.axisY.bounds.y1?a.axisY.bounds.y1:m>a.axisY.bounds.y2?a.axisY.bounds.y2:m}}};p.prototype.renderStackedColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],k=[],l=[],w=[],m=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?
this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&m>s&&(m=Math.min(this.options.dataPointWidth?
this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&s<m)&&(s=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,m));f<m&&(f=m);f>s&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;B<a.dataSeriesIndexes.length;B++){var h=a.dataSeriesIndexes[B],
p=this.data[h],t=p.dataPoints;if(0<t.length){var u=5<f&&p.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(m=0;m<t.length;m++)if(c=t[m].x.getTime?t[m].x.getTime():t[m].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof t[m].y){s=a.axisX.convertValueToPixel(c);var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0<t[m].y)l[c]=t[m].y+(l[c]?l[c]:0),0<l[c]&&(q=a.axisY.convertValueToPixel(l[c]),
y="undefined"!==typeof g[c]?g[c]:n,g[c]=q);else if(a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0>=t[m].y)w[c]=t[m].y+(w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q="undefined"!==typeof k[c]?k[c]:n,k[c]=y;else if(q=a.axisY.convertValueToPixel(t[m].y),0<=t[m].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;g[c]=A+(y-q)}else A=k[c]?k[c]:0,y=q+A,q=n+A,k[c]=A+(y-q);c=t[m].color?t[m].color:p._colorSet[m%p._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[m].y,0>t[m].y&&u,!1,
!1,p.fillOpacity);c=p.dataPointIds[m];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:m,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[m].indexLabel||p.indexLabel||t[m].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedColumn",dataPoint:t[m],dataSeries:p,point:{x:s,y:0<=t[m].y?q:y},direction:0>t[m].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q,
y)},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,
animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:n<a.axisY.bounds.y1?a.axisY.bounds.y1:n>a.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderStackedColumn100=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],k=[],l=[],w=[],m=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?this.dataPointMinWidth:
this.options.dataPointWidth?this.dataPointWidth:1;s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.width<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&m>s&&(m=Math.min(this.options.dataPointWidth?
this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&s<m)&&(s=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,m));f<m&&(f=m);f>s&&(f=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;B<a.dataSeriesIndexes.length;B++){var p=a.dataSeriesIndexes[B],
h=this.data[p],t=h.dataPoints;if(0<t.length)for(var u=5<f&&h.bevelEnabled?!0:!1,m=0;m<t.length;m++)if(c=t[m].x.getTime?t[m].x.getTime():t[m].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof t[m].y){s=a.axisX.convertValueToPixel(c);q=0!==a.dataPointYSums[c]?100*(t[m].y/a.dataPointYSums[c]):0;var x=s-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0<t[m].y){l[c]=q+
("undefined"!==typeof l[c]?l[c]:0);if(0>=l[c])continue;q=a.axisY.convertValueToPixel(l[c]);y=g[c]?g[c]:n;g[c]=q}else if(a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0>=t[m].y)w[c]=q+("undefined"!==typeof w[c]?w[c]:0),y=a.axisY.convertValueToPixel(w[c]),q=k[c]?k[c]:n,k[c]=y;else if(q=a.axisY.convertValueToPixel(q),0<=t[m].y){var A="undefined"!==typeof g[c]?g[c]:0;q-=A;y=n-A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y1-q)&&(q=e.y1);g[c]=A+(y-q)}else A="undefined"!==typeof k[c]?
k[c]:0,y=q+A,q=n+A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.y2-y)&&(y=e.y2),k[c]=A+(y-q);c=t[m].color?t[m].color:h._colorSet[m%h._colorSet.length];ea(b,x,q,v,y,c,0,null,u&&0<=t[m].y,0>t[m].y&&u,!1,!1,h.fillOpacity);c=h.dataPointIds[m];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:x,y1:q,x2:v,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,q,v,y,c,0,null,!1,!1,!1,!1);(t[m].indexLabel||h.indexLabel||t[m].indexLabelFormatter||h.indexLabelFormatter)&&
this._indexLabels.push({chartType:"stackedColumn100",dataPoint:t[m],dataSeries:h,point:{x:s,y:0<=t[m].y?q:y},direction:0>t[m].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(q,y),x2:v,y2:Math.max(q,y)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&
this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.yScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:n<a.axisY.bounds.y1?a.axisY.bounds.y1:n>a.axisY.bounds.y2?a.axisY.bounds.y2:n}}};p.prototype.renderBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=
null,e=this.plotArea,g=0,k,l,w,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,s=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0,q=a.axisX.dataInfo.minDiff;isFinite(q)||(q=0.3*Math.abs(a.axisX.range));q=this.options.dataPointWidth?
this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(q)/Math.log(a.axisX.range):Math.abs(q)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>s&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,s));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&s<g)&&(s=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,g));q<g&&(q=g);q>s&&(q=s);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);
b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(s=0;s<a.dataSeriesIndexes.length;s++){var n=a.dataSeriesIndexes[s],f=this.data[n],B=f.dataPoints;if(0<B.length){var h=5<q&&f.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(g=0;g<B.length;g++)if(B[g].getTime?w=B[g].x.getTime():w=B[g].x,!(w<a.axisX.dataInfo.viewPortMin||w>a.axisX.dataInfo.viewPortMax)&&"number"===typeof B[g].y){l=a.axisX.convertValueToPixel(w);
k=a.axisY.convertValueToPixel(B[g].y);l=a.axisX.reversed?l+a.plotType.totalDataSeries*q/2-(a.previousDataSeriesCount+s)*q<<0:l-a.plotType.totalDataSeries*q/2+(a.previousDataSeriesCount+s)*q<<0;var p=a.axisX.reversed?l-q<<0:l+q<<0,t;0<=B[g].y?t=m:(t=k,k=m);c=B[g].color?B[g].color:f._colorSet[g%f._colorSet.length];ea(b,t,l,k,p,c,0,null,h,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,x1:t,y1:l,x2:k,y2:p};c=
N(c);r&&ea(this._eventManager.ghostCtx,t,l,k,p,c,0,null,!1,!1,!1,!1);(B[g].indexLabel||f.indexLabel||B[g].indexLabelFormatter||f.indexLabelFormatter)&&this._indexLabels.push({chartType:"bar",dataPoint:B[g],dataSeries:f,point:{x:0<=B[g].y?k:t,y:l+(p-l)/2},direction:0>B[g].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(t,k),y1:l,x2:Math.max(t,k),y2:p},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,
0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:m<a.axisY.bounds.x1?a.axisY.bounds.x1:m>a.axisY.bounds.x2?a.axisY.bounds.x2:
m}}};p.prototype.renderStackedBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],k=[],l=[],w=[],m=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<<
0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&m>q&&(m=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&q<m)&&(q=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,m));f<
m&&(f=m);f>q&&(f=q);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;B<a.dataSeriesIndexes.length;B++){var p=a.dataSeriesIndexes[B],h=this.data[p],t=h.dataPoints;if(0<t.length){var u=5<f&&h.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(m=0;m<t.length;m++)if(c=t[m].x.getTime?t[m].x.getTime():
t[m].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&"number"===typeof t[m].y){q=a.axisX.convertValueToPixel(c);var x=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,v=x+f<<0,y;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0<t[m].y)l[c]=t[m].y+(l[c]?l[c]:0),0<l[c]&&(y=g[c]?g[c]:n,g[c]=s=a.axisY.convertValueToPixel(l[c]));else if(a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0>=t[m].y)w[c]=t[m].y+(w[c]?w[c]:0),s=k[c]?
k[c]:n,k[c]=y=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(t[m].y),0<=t[m].y){var A=g[c]?g[c]:0;y=n+A;s+=A;g[c]=A+(s-y)}else A=k[c]?k[c]:0,y=s-A,s=n-A,k[c]=A+(s-y);c=t[m].color?t[m].color:h._colorSet[m%h._colorSet.length];ea(b,y,x,s,v,c,0,null,u,!1,!1,!1,h.fillOpacity);c=h.dataPointIds[m];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:m,x1:y,y1:x,x2:s,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,y,x,s,v,c,0,null,!1,!1,!1,
!1);(t[m].indexLabel||h.indexLabel||t[m].indexLabelFormatter||h.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar",dataPoint:t[m],dataSeries:h,point:{x:0<=t[m].y?s:y,y:q},direction:0>t[m].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(y,s),y1:x,x2:Math.max(y,s),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&
b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:n<a.axisY.bounds.x1?a.axisY.bounds.x1:n>a.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderStackedBar100=function(a){var d=
a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=[],k=[],l=[],w=[],m=0,s,q,n=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),m=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;q=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.15*this.height<<0;var f=a.axisX.dataInfo.minDiff;isFinite(f)||
(f=0.3*Math.abs(a.axisX.range));f=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.height*(a.axisX.logarithmic?Math.log(f)/Math.log(a.axisX.range):Math.abs(f)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&m>q&&(m=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,q));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&q<m)&&(q=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,m));f<m&&(f=m);f>q&&(f=q);b.save();r&&this._eventManager.ghostCtx.save();
b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var B=0;B<a.dataSeriesIndexes.length;B++){var h=a.dataSeriesIndexes[B],p=this.data[h],t=p.dataPoints;if(0<t.length){var u=5<f&&p.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(m=0;m<t.length;m++)if(c=t[m].x.getTime?t[m].x.getTime():t[m].x,!(c<a.axisX.dataInfo.viewPortMin||c>a.axisX.dataInfo.viewPortMax)&&
"number"===typeof t[m].y){q=a.axisX.convertValueToPixel(c);var x;x=0!==a.dataPointYSums[c]?100*(t[m].y/a.dataPointYSums[c]):0;var v=q-a.plotType.plotUnits.length*f/2+a.index*f<<0,y=v+f<<0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0<t[m].y){l[c]=x+(l[c]?l[c]:0);if(0>=l[c])continue;x=g[c]?g[c]:n;g[c]=s=a.axisY.convertValueToPixel(l[c])}else if(a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length&&0>=t[m].y)w[c]=x+(w[c]?w[c]:0),s=k[c]?k[c]:
n,k[c]=x=a.axisY.convertValueToPixel(w[c]);else if(s=a.axisY.convertValueToPixel(x),0<=t[m].y){var A=g[c]?g[c]:0;x=n+A;s+=A;a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x2-s)&&(s=e.x2);g[c]=A+(s-x)}else A=k[c]?k[c]:0,x=s-A,s=n-A,a.dataSeriesIndexes.length-1===B&&1>=Math.abs(e.x1-x)&&(x=e.x1),k[c]=A+(s-x);c=t[m].color?t[m].color:p._colorSet[m%p._colorSet.length];ea(b,x,v,s,y,c,0,null,u,!1,!1,!1,p.fillOpacity);c=p.dataPointIds[m];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:h,
dataPointIndex:m,x1:x,y1:v,x2:s,y2:y};c=N(c);r&&ea(this._eventManager.ghostCtx,x,v,s,y,c,0,null,!1,!1,!1,!1);(t[m].indexLabel||p.indexLabel||t[m].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar100",dataPoint:t[m],dataSeries:p,point:{x:0<=t[m].y?s:x,y:q},direction:0>t[m].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(x,s),y1:v,x2:Math.max(x,s),y2:y},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",
a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.xScaleAnimation,easingFunction:M.easing.easeOutQuart,animationBase:n<a.axisY.bounds.x1?a.axisY.bounds.x1:
n>a.axisY.bounds.x2?a.axisY.bounds.x2:n}}};p.prototype.renderArea=function(a){var d,b;function c(){A&&(0<B.lineThickness&&g.stroke(),a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0<a.axisY.viewportMinimum&&(y=l.y2),g.lineTo(t,y),g.lineTo(A.x,y),g.closePath(),g.globalAlpha=B.fillOpacity,g.fill(),g.globalAlpha=1,r&&(k.lineTo(t,y),k.lineTo(A.x,y),k.closePath(),k.fill()),g.beginPath(),g.moveTo(t,u),k.beginPath(),k.moveTo(t,u),A={x:t,y:u})}
var e=a.targetCanvasCtx||this.plotArea.ctx,g=r?this._preRenderCtx:e;if(!(0>=a.dataSeriesIndexes.length)){var k=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,m=[],s=this.plotArea,q;g.save();r&&k.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(k.beginPath(),k.rect(s.x1,s.y1,s.width,s.height),k.clip());for(var n=0;n<a.dataSeriesIndexes.length;n++){var f=a.dataSeriesIndexes[n],B=this.data[f],p=B.dataPoints,m=B.id;this._eventManager.objectMap[m]={objectType:"dataSeries",
dataSeriesIndex:f};m=N(m);k.fillStyle=m;m=[];d=!0;var h=0,t,u,x,v=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),y,A=null;if(0<p.length){var z=B._colorSet[h%B._colorSet.length],aa=B.lineColor=B.options.lineColor||z,T=aa;g.fillStyle=z;g.strokeStyle=aa;g.lineWidth=B.lineThickness;b="solid";if(g.setLineDash){var Y=R(B.nullDataLineDashType,B.lineThickness);b=B.lineDashType;var ca=R(b,B.lineThickness);g.setLineDash(ca)}for(var da=!0;h<p.length;h++)if(x=p[h].x.getTime?p[h].x.getTime():
p[h].x,!(x<a.axisX.dataInfo.viewPortMin||x>a.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!da)))if("number"!==typeof p[h].y)B.connectNullData||(da||d)||c(),da=!0;else{t=a.axisX.convertValueToPixel(x);u=a.axisY.convertValueToPixel(p[h].y);d||da?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||b===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d=t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,b=B.nullDataLineDashType,g.setLineDash(Y)),g.lineTo(t,u),r&&k.lineTo(t,
u)):(g.beginPath(),g.moveTo(t,u),r&&(k.beginPath(),k.moveTo(t,u)),A={x:t,y:u}),da=d=!1):(g.lineTo(t,u),r&&k.lineTo(t,u),0==h%250&&c());q={x:t,y:u};h<p.length-1&&(T!==(p[h].lineColor||aa)||b!==(p[h].lineDashType||B.lineDashType))&&(c(),T=p[h].lineColor||aa,g.strokeStyle=T,g.setLineDash&&(p[h].lineDashType?(b=p[h].lineDashType,g.setLineDash(R(b,B.lineThickness))):(b=B.lineDashType,g.setLineDash(ca))));var Z=B.dataPointIds[h];this._eventManager.objectMap[Z]={id:Z,objectType:"dataPoint",dataSeriesIndex:f,
dataPointIndex:h,x1:t,y1:u};0!==p[h].markerSize&&(0<p[h].markerSize||0<B.markerSize)&&(x=B.getMarkerProperties(h,t,u,g),m.push(x),Z=N(Z),r&&m.push({x:t,y:u,ctx:k,type:x.type,size:x.size,color:Z,borderColor:Z,borderThickness:x.borderThickness}));(p[h].indexLabel||B.indexLabel||p[h].indexLabelFormatter||B.indexLabelFormatter)&&this._indexLabels.push({chartType:"area",dataPoint:p[h],dataSeries:B,point:{x:t,y:u},direction:0>p[h].y===a.axisY.reversed?1:-1,color:z})}c();ia.drawMarkers(m)}}r&&(e.drawImage(this._preRenderCanvas,
0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,
animationBase:0}}};p.prototype.renderSplineArea=function(a){function d(){var b=v(x,2);if(0<b.length){if(0<q.lineThickness){c.beginPath();c.moveTo(b[0].x,b[0].y);b[0].newStrokeStyle&&(c.strokeStyle=b[0].newStrokeStyle);b[0].newLineDashArray&&c.setLineDash(b[0].newLineDashArray);for(var d=0;d<b.length-3;d+=3)if(c.bezierCurveTo(b[d+1].x,b[d+1].y,b[d+2].x,b[d+2].y,b[d+3].x,b[d+3].y),r&&e.bezierCurveTo(b[d+1].x,b[d+1].y,b[d+2].x,b[d+2].y,b[d+3].x,b[d+3].y),b[d+3].newStrokeStyle||b[d+3].newLineDashArray)c.stroke(),
c.beginPath(),c.moveTo(b[d+3].x,b[d+3].y),b[d+3].newStrokeStyle&&(c.strokeStyle=b[d+3].newStrokeStyle),b[d+3].newLineDashArray&&c.setLineDash(b[d+3].newLineDashArray);c.stroke()}c.beginPath();c.moveTo(b[0].x,b[0].y);r&&(e.beginPath(),e.moveTo(b[0].x,b[0].y));for(d=0;d<b.length-3;d+=3)c.bezierCurveTo(b[d+1].x,b[d+1].y,b[d+2].x,b[d+2].y,b[d+3].x,b[d+3].y),r&&e.bezierCurveTo(b[d+1].x,b[d+1].y,b[d+2].x,b[d+2].y,b[d+3].x,b[d+3].y);a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?
t=p:0>a.axisY.viewportMaximum?t=k.y1:0<a.axisY.viewportMinimum&&(t=g.y2);u={x:b[0].x,y:b[0].y};c.lineTo(b[b.length-1].x,t);c.lineTo(u.x,t);c.closePath();c.globalAlpha=q.fillOpacity;c.fill();c.globalAlpha=1;r&&(e.lineTo(b[b.length-1].x,t),e.lineTo(u.x,t),e.closePath(),e.fill())}}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=a.axisX.lineCoordinates,k=a.axisY.lineCoordinates,l=[],w=this.plotArea;c.save();r&&
e.save();c.beginPath();c.rect(w.x1,w.y1,w.width,w.height);c.clip();r&&(e.beginPath(),e.rect(w.x1,w.y1,w.width,w.height),e.clip());for(var m=0;m<a.dataSeriesIndexes.length;m++){var s=a.dataSeriesIndexes[m],q=this.data[s],n=q.dataPoints,l=q.id;this._eventManager.objectMap[l]={objectType:"dataSeries",dataSeriesIndex:s};l=N(l);e.fillStyle=l;var l=[],f=0,B,h,p=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),t,u=null,x=[];if(0<n.length){var ma=q._colorSet[f%q._colorSet.length],
y=q.lineColor=q.options.lineColor||ma,A=y;c.fillStyle=ma;c.strokeStyle=y;c.lineWidth=q.lineThickness;var z="solid";if(c.setLineDash){var aa=R(q.nullDataLineDashType,q.lineThickness),z=q.lineDashType,T=R(z,q.lineThickness);c.setLineDash(T)}for(h=!1;f<n.length;f++)if(B=n[f].x.getTime?n[f].x.getTime():n[f].x,!(B<a.axisX.dataInfo.viewPortMin||B>a.axisX.dataInfo.viewPortMax&&(!q.connectNullData||!h)))if("number"!==typeof n[f].y)0<f&&!h&&(q.connectNullData?c.setLineDash&&(0<x.length&&(q.options.nullDataLineDashType||
!n[f-1].lineDashType))&&(x[x.length-1].newLineDashArray=aa,z=q.nullDataLineDashType):(d(),x=[])),h=!0;else{B=a.axisX.convertValueToPixel(B);h=a.axisY.convertValueToPixel(n[f].y);var Y=q.dataPointIds[f];this._eventManager.objectMap[Y]={id:Y,objectType:"dataPoint",dataSeriesIndex:s,dataPointIndex:f,x1:B,y1:h};x[x.length]={x:B,y:h};f<n.length-1&&(A!==(n[f].lineColor||y)||z!==(n[f].lineDashType||q.lineDashType))&&(A=n[f].lineColor||y,x[x.length-1].newStrokeStyle=A,c.setLineDash&&(n[f].lineDashType?(z=
n[f].lineDashType,x[x.length-1].newLineDashArray=R(z,q.lineThickness)):(z=q.lineDashType,x[x.length-1].newLineDashArray=T)));if(0!==n[f].markerSize&&(0<n[f].markerSize||0<q.markerSize)){var ca=q.getMarkerProperties(f,B,h,c);l.push(ca);Y=N(Y);r&&l.push({x:B,y:h,ctx:e,type:ca.type,size:ca.size,color:Y,borderColor:Y,borderThickness:ca.borderThickness})}(n[f].indexLabel||q.indexLabel||n[f].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"splineArea",dataPoint:n[f],dataSeries:q,
point:{x:B,y:h},direction:0>n[f].y===a.axisY.reversed?1:-1,color:ma});h=!1}d();ia.drawMarkers(l)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(w.x1,w.y1,w.width,w.height),
this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStepArea=function(a){var d,b;function c(){A&&(0<B.lineThickness&&g.stroke(),a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?y=v:0>a.axisY.viewportMaximum?y=w.y1:0<a.axisY.viewportMinimum&&(y=l.y2),g.lineTo(t,y),g.lineTo(A.x,y),g.closePath(),g.globalAlpha=B.fillOpacity,g.fill(),g.globalAlpha=
1,r&&(k.lineTo(t,y),k.lineTo(A.x,y),k.closePath(),k.fill()),g.beginPath(),g.moveTo(t,u),k.beginPath(),k.moveTo(t,u),A={x:t,y:u})}var e=a.targetCanvasCtx||this.plotArea.ctx,g=r?this._preRenderCtx:e;if(!(0>=a.dataSeriesIndexes.length)){var k=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,w=a.axisY.lineCoordinates,m=[],s=this.plotArea,q;g.save();r&&k.save();g.beginPath();g.rect(s.x1,s.y1,s.width,s.height);g.clip();r&&(k.beginPath(),k.rect(s.x1,s.y1,s.width,s.height),k.clip());for(var n=0;n<a.dataSeriesIndexes.length;n++){var f=
a.dataSeriesIndexes[n],B=this.data[f],h=B.dataPoints,m=B.id;this._eventManager.objectMap[m]={objectType:"dataSeries",dataSeriesIndex:f};m=N(m);k.fillStyle=m;m=[];d=!0;var p=0,t,u,x,v=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),y,A=null;b=!1;if(0<h.length){var z=B._colorSet[p%B._colorSet.length],aa=B.lineColor=B.options.lineColor||z,T=aa;g.fillStyle=z;g.strokeStyle=aa;g.lineWidth=B.lineThickness;var Y="solid";if(g.setLineDash){var ca=R(B.nullDataLineDashType,B.lineThickness),
Y=B.lineDashType,da=R(Y,B.lineThickness);g.setLineDash(da)}for(;p<h.length;p++)if(x=h[p].x.getTime?h[p].x.getTime():h[p].x,!(x<a.axisX.dataInfo.viewPortMin||x>a.axisX.dataInfo.viewPortMax&&(!B.connectNullData||!b))){var Z=u;"number"!==typeof h[p].y?(B.connectNullData||(b||d)||c(),b=!0):(t=a.axisX.convertValueToPixel(x),u=a.axisY.convertValueToPixel(h[p].y),d||b?(!d&&B.connectNullData?(g.setLineDash&&(B.options.nullDataLineDashType||Y===B.lineDashType&&B.lineDashType!==B.nullDataLineDashType)&&(d=
t,b=u,t=q.x,u=q.y,c(),g.moveTo(q.x,q.y),t=d,u=b,A=q,Y=B.nullDataLineDashType,g.setLineDash(ca)),g.lineTo(t,Z),g.lineTo(t,u),r&&(k.lineTo(t,Z),k.lineTo(t,u))):(g.beginPath(),g.moveTo(t,u),r&&(k.beginPath(),k.moveTo(t,u)),A={x:t,y:u}),b=d=!1):(g.lineTo(t,Z),r&&k.lineTo(t,Z),g.lineTo(t,u),r&&k.lineTo(t,u),0==p%250&&c()),q={x:t,y:u},p<h.length-1&&(T!==(h[p].lineColor||aa)||Y!==(h[p].lineDashType||B.lineDashType))&&(c(),T=h[p].lineColor||aa,g.strokeStyle=T,g.setLineDash&&(h[p].lineDashType?(Y=h[p].lineDashType,
g.setLineDash(R(Y,B.lineThickness))):(Y=B.lineDashType,g.setLineDash(da)))),x=B.dataPointIds[p],this._eventManager.objectMap[x]={id:x,objectType:"dataPoint",dataSeriesIndex:f,dataPointIndex:p,x1:t,y1:u},0!==h[p].markerSize&&(0<h[p].markerSize||0<B.markerSize)&&(Z=B.getMarkerProperties(p,t,u,g),m.push(Z),x=N(x),r&&m.push({x:t,y:u,ctx:k,type:Z.type,size:Z.size,color:x,borderColor:x,borderThickness:Z.borderThickness})),(h[p].indexLabel||B.indexLabel||h[p].indexLabelFormatter||B.indexLabelFormatter)&&
this._indexLabels.push({chartType:"stepArea",dataPoint:h[p],dataSeries:B,point:{x:t,y:u},direction:0>h[p].y===a.axisY.reversed?1:-1,color:z}))}c();ia.drawMarkers(m)}}r&&(e.drawImage(this._preRenderCanvas,0,0,this.width,this.height),g.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&g.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&g.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,
0,0,this.width,this.height),g.clearRect(s.x1,s.y1,s.width,s.height),this._eventManager.ghostCtx.restore());g.restore();return{source:e,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea=function(a){function d(){if(!(1>m.length)){for(0<A.lineThickness&&c.stroke();0<m.length;){var a=m.pop();c.lineTo(a.x,a.y);r&&u.lineTo(a.x,a.y)}c.closePath();c.globalAlpha=A.fillOpacity;c.fill();c.globalAlpha=1;c.beginPath();r&&(u.closePath(),
u.fill(),u.beginPath());m=[]}}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=null,k=[],l=this.plotArea,w=[],m=[],s=[],q=[],n=0,f,h,p=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),u=this._eventManager.ghostCtx,t,C,x;r&&u.beginPath();c.save();r&&u.save();c.beginPath();c.rect(l.x1,l.y1,l.width,l.height);c.clip();r&&(u.beginPath(),u.rect(l.x1,l.y1,l.width,l.height),u.clip());for(var e=[],v=0;v<a.dataSeriesIndexes.length;v++){var y=
a.dataSeriesIndexes[v],A=this.data[y],z=A.dataPoints;A.dataPointIndexes=[];for(n=0;n<z.length;n++)y=z[n].x.getTime?z[n].x.getTime():z[n].x,A.dataPointIndexes[y]=n,e[y]||(s.push(y),e[y]=!0);s.sort(Sa)}for(v=0;v<a.dataSeriesIndexes.length;v++){y=a.dataSeriesIndexes[v];A=this.data[y];z=A.dataPoints;C=!0;m=[];n=A.id;this._eventManager.objectMap[n]={objectType:"dataSeries",dataSeriesIndex:y};n=N(n);u.fillStyle=n;if(0<s.length){var e=A._colorSet[0],aa=A.lineColor=A.options.lineColor||e,T=aa;c.fillStyle=
e;c.strokeStyle=aa;c.lineWidth=A.lineThickness;x="solid";if(c.setLineDash){var Y=R(A.nullDataLineDashType,A.lineThickness);x=A.lineDashType;var ca=R(x,A.lineThickness);c.setLineDash(ca)}for(var da=!0,n=0;n<s.length;n++){var g=s[n],Z=null,Z=0<=A.dataPointIndexes[g]?z[A.dataPointIndexes[g]]:{x:g,y:null};if(!(g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax&&(!A.connectNullData||!da)))if("number"!==typeof Z.y)A.connectNullData||(da||C)||d(),da=!0;else{f=a.axisX.convertValueToPixel(g);var oa=
w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length){q[g]=Z.y+(q[g]?q[g]:0);if(0>=q[g]&&a.axisY.logarithmic)continue;h=a.axisY.convertValueToPixel(q[g])}else h=a.axisY.convertValueToPixel(Z.y),h-=oa;m.push({x:f,y:p-oa});w[g]=p-h;C||da?(!C&&A.connectNullData?(c.setLineDash&&(A.options.nullDataLineDashType||x===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(C=m.pop(),x=m[m.length-1],d(),c.moveTo(t.x,t.y),m.push(x),m.push(C),x=A.nullDataLineDashType,
c.setLineDash(Y)),c.lineTo(f,h),r&&u.lineTo(f,h)):(c.beginPath(),c.moveTo(f,h),r&&(u.beginPath(),u.moveTo(f,h))),da=C=!1):(c.lineTo(f,h),r&&u.lineTo(f,h),0==n%250&&(d(),c.moveTo(f,h),r&&u.moveTo(f,h),m.push({x:f,y:p-oa})));t={x:f,y:h};n<z.length-1&&(T!==(z[n].lineColor||aa)||x!==(z[n].lineDashType||A.lineDashType))&&(d(),c.beginPath(),c.moveTo(f,h),m.push({x:f,y:p-oa}),T=z[n].lineColor||aa,c.strokeStyle=T,c.setLineDash&&(z[n].lineDashType?(x=z[n].lineDashType,c.setLineDash(R(x,A.lineThickness))):
(x=A.lineDashType,c.setLineDash(ca))));if(0<=A.dataPointIndexes[g]){var la=A.dataPointIds[A.dataPointIndexes[g]];this._eventManager.objectMap[la]={id:la,objectType:"dataPoint",dataSeriesIndex:y,dataPointIndex:A.dataPointIndexes[g],x1:f,y1:h}}0<=A.dataPointIndexes[g]&&0!==Z.markerSize&&(0<Z.markerSize||0<A.markerSize)&&(oa=A.getMarkerProperties(A.dataPointIndexes[g],f,h,c),k.push(oa),g=N(la),r&&k.push({x:f,y:h,ctx:u,type:oa.type,size:oa.size,color:g,borderColor:g,borderThickness:oa.borderThickness}));
(Z.indexLabel||A.indexLabel||Z.indexLabelFormatter||A.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedArea",dataPoint:Z,dataSeries:A,point:{x:f,y:h},direction:0>z[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,h);r&&u.moveTo(f,h)}delete A.dataPointIndexes}ia.drawMarkers(k);r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&
c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(l.x1,l.y1,l.width,l.height),u.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderStackedArea100=function(a){function d(){for(0<A.lineThickness&&c.stroke();0<m.length;){var a=m.pop();c.lineTo(a.x,a.y);r&&x.lineTo(a.x,
a.y)}c.closePath();c.globalAlpha=A.fillOpacity;c.fill();c.globalAlpha=1;c.beginPath();r&&(x.closePath(),x.fill(),x.beginPath());m=[]}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=null,k=this.plotArea,l=[],w=[],m=[],s=[],q=[],n=0,f,h,p,u,t,C=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),x=this._eventManager.ghostCtx;c.save();r&&x.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();r&&(x.beginPath(),
x.rect(k.x1,k.y1,k.width,k.height),x.clip());for(var e=[],v=0;v<a.dataSeriesIndexes.length;v++){var y=a.dataSeriesIndexes[v],A=this.data[y],z=A.dataPoints;A.dataPointIndexes=[];for(n=0;n<z.length;n++)y=z[n].x.getTime?z[n].x.getTime():z[n].x,A.dataPointIndexes[y]=n,e[y]||(s.push(y),e[y]=!0);s.sort(Sa)}for(v=0;v<a.dataSeriesIndexes.length;v++){y=a.dataSeriesIndexes[v];A=this.data[y];z=A.dataPoints;u=!0;e=A.id;this._eventManager.objectMap[e]={objectType:"dataSeries",dataSeriesIndex:y};e=N(e);x.fillStyle=
e;m=[];if(0<s.length){var e=A._colorSet[n%A._colorSet.length],aa=A.lineColor=A.options.lineColor||e,T=aa;c.fillStyle=e;c.strokeStyle=aa;c.lineWidth=A.lineThickness;t="solid";if(c.setLineDash){var Y=R(A.nullDataLineDashType,A.lineThickness);t=A.lineDashType;var ca=R(t,A.lineThickness);c.setLineDash(ca)}for(var da=!0,n=0;n<s.length;n++){var g=s[n],Z=null,Z=0<=A.dataPointIndexes[g]?z[A.dataPointIndexes[g]]:{x:g,y:null};if(!(g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax&&(!A.connectNullData||
!da)))if("number"!==typeof Z.y)A.connectNullData||(da||u)||d(),da=!0;else{var oa;oa=0!==a.dataPointYSums[g]?100*(Z.y/a.dataPointYSums[g]):0;f=a.axisX.convertValueToPixel(g);var la=w[g]?w[g]:0;if(a.axisY.logarithmic||a.axisY.scaleBreaks&&0<a.axisY.scaleBreaks._appliedBreaks.length){q[g]=oa+(q[g]?q[g]:0);if(0>=q[g]&&a.axisY.logarithmic)continue;h=a.axisY.convertValueToPixel(q[g])}else h=a.axisY.convertValueToPixel(oa),h-=la;m.push({x:f,y:C-la});w[g]=C-h;u||da?(!u&&A.connectNullData?(c.setLineDash&&
(A.options.nullDataLineDashType||t===A.lineDashType&&A.lineDashType!==A.nullDataLineDashType)&&(u=m.pop(),t=m[m.length-1],d(),c.moveTo(p.x,p.y),m.push(t),m.push(u),t=A.nullDataLineDashType,c.setLineDash(Y)),c.lineTo(f,h),r&&x.lineTo(f,h)):(c.beginPath(),c.moveTo(f,h),r&&(x.beginPath(),x.moveTo(f,h))),da=u=!1):(c.lineTo(f,h),r&&x.lineTo(f,h),0==n%250&&(d(),c.moveTo(f,h),r&&x.moveTo(f,h),m.push({x:f,y:C-la})));p={x:f,y:h};n<z.length-1&&(T!==(z[n].lineColor||aa)||t!==(z[n].lineDashType||A.lineDashType))&&
(d(),c.beginPath(),c.moveTo(f,h),m.push({x:f,y:C-la}),T=z[n].lineColor||aa,c.strokeStyle=T,c.setLineDash&&(z[n].lineDashType?(t=z[n].lineDashType,c.setLineDash(R(t,A.lineThickness))):(t=A.lineDashType,c.setLineDash(ca))));if(0<=A.dataPointIndexes[g]){var G=A.dataPointIds[A.dataPointIndexes[g]];this._eventManager.objectMap[G]={id:G,objectType:"dataPoint",dataSeriesIndex:y,dataPointIndex:A.dataPointIndexes[g],x1:f,y1:h}}0<=A.dataPointIndexes[g]&&0!==Z.markerSize&&(0<Z.markerSize||0<A.markerSize)&&(la=
A.getMarkerProperties(n,f,h,c),l.push(la),g=N(G),r&&l.push({x:f,y:h,ctx:x,type:la.type,size:la.size,color:g,borderColor:g,borderThickness:la.borderThickness}));(Z.indexLabel||A.indexLabel||Z.indexLabelFormatter||A.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedArea100",dataPoint:Z,dataSeries:A,point:{x:f,y:h},direction:0>z[n].y===a.axisY.reversed?1:-1,color:e})}}d();c.moveTo(f,h);r&&x.moveTo(f,h)}delete A.dataPointIndexes}ia.drawMarkers(l);r&&(b.drawImage(this._preRenderCanvas,0,
0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(k.x1,k.y1,k.width,k.height),x.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};
p.prototype.renderBubble=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,k;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=-Infinity,w=Infinity,m=0;m<a.dataSeriesIndexes.length;m++)for(var s=a.dataSeriesIndexes[m],
q=this.data[s],n=q.dataPoints,f=0,e=0;e<n.length;e++)g=n[e].getTime?g=n[e].x.getTime():g=n[e].x,g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax||"undefined"===typeof n[e].z||(f=n[e].z,f>l&&(l=f),f<w&&(w=f));for(var h=25*Math.PI,p=Math.max(Math.pow(0.25*Math.min(c.height,c.width)/2,2)*Math.PI,h),m=0;m<a.dataSeriesIndexes.length;m++)if(s=a.dataSeriesIndexes[m],q=this.data[s],n=q.dataPoints,0<n.length)for(b.strokeStyle="#4572A7 ",e=0;e<n.length;e++)if(g=n[e].getTime?g=n[e].x.getTime():
g=n[e].x,!(g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax)&&"number"===typeof n[e].y){g=a.axisX.convertValueToPixel(g);k=a.axisY.convertValueToPixel(n[e].y);var f=n[e].z,u=2*Math.max(Math.sqrt((l===w?p/2:h+(p-h)/(l-w)*(f-w))/Math.PI)<<0,1),f=q.getMarkerProperties(e,b);f.size=u;b.globalAlpha=q.fillOpacity;ia.drawMarker(g,k,b,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;var t=q.dataPointIds[e];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:s,
dataPointIndex:e,x1:g,y1:k,size:u};u=N(t);r&&ia.drawMarker(g,k,this._eventManager.ghostCtx,f.type,f.size,u,u,f.borderThickness);(n[e].indexLabel||q.indexLabel||n[e].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"bubble",dataPoint:n[e],dataSeries:q,point:{x:g,y:k},direction:1,bounds:{x1:g-f.size/2,y1:k-f.size/2,x2:g+f.size/2,y2:k+f.size/2},color:null})}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&
b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderScatter=function(a){var d=
a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this.plotArea,e=0,g,k;b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(c.x1,c.y1,c.width,c.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.clip());for(var l=0;l<a.dataSeriesIndexes.length;l++){var w=a.dataSeriesIndexes[l],m=this.data[w],s=m.dataPoints;if(0<s.length){b.strokeStyle=
"#4572A7 ";Math.pow(0.3*Math.min(c.height,c.width)/2,2);for(var q=0,n=0,e=0;e<s.length;e++)if(g=s[e].getTime?g=s[e].x.getTime():g=s[e].x,!(g<a.axisX.dataInfo.viewPortMin||g>a.axisX.dataInfo.viewPortMax)&&"number"===typeof s[e].y){g=a.axisX.convertValueToPixel(g);k=a.axisY.convertValueToPixel(s[e].y);var f=m.getMarkerProperties(e,g,k,b);b.globalAlpha=m.fillOpacity;ia.drawMarker(f.x,f.y,f.ctx,f.type,f.size,f.color,f.borderColor,f.borderThickness);b.globalAlpha=1;Math.sqrt((q-g)*(q-g)+(n-k)*(n-k))<Math.min(f.size,
5)&&s.length>Math.min(this.plotArea.width,this.plotArea.height)||(q=m.dataPointIds[e],this._eventManager.objectMap[q]={id:q,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:e,x1:g,y1:k},q=N(q),r&&ia.drawMarker(f.x,f.y,this._eventManager.ghostCtx,f.type,f.size,q,q,f.borderThickness),(s[e].indexLabel||m.indexLabel||s[e].indexLabelFormatter||m.indexLabelFormatter)&&this._indexLabels.push({chartType:"scatter",dataPoint:s[e],dataSeries:m,point:{x:g,y:k},direction:1,bounds:{x1:g-f.size/2,y1:k-f.size/
2,x2:g+f.size/2,y2:k+f.size/2},color:null}),q=g,n=k)}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(c.x1,c.y1,c.width,c.height),this._eventManager.ghostCtx.restore());
b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderCandlestick=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=null,k=this.plotArea,l=0,w,m,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,g=this.options.dataPointMaxWidth?
this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,h=a.axisX.dataInfo.minDiff;isFinite(h)||(h=0.3*Math.abs(a.axisX.range));h=this.options.dataPointWidth?this.dataPointWidth:0.7*k.width*(a.axisX.logarithmic?Math.log(h)/Math.log(a.axisX.range):Math.abs(h)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&&e>g&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&g<e)&&(g=Math.max(this.options.dataPointWidth?
this.dataPointWidth:-Infinity,e));h<e&&(h=e);h>g&&(h=g);b.save();r&&c.save();b.beginPath();b.rect(k.x1,k.y1,k.width,k.height);b.clip();r&&(c.beginPath(),c.rect(k.x1,k.y1,k.width,k.height),c.clip());for(var p=0;p<a.dataSeriesIndexes.length;p++){var v=a.dataSeriesIndexes[p],t=this.data[v],C=t.dataPoints;if(0<C.length)for(var x=5<h&&t.bevelEnabled?!0:!1,l=0;l<C.length;l++)if(C[l].getTime?f=C[l].x.getTime():f=C[l].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&!u(C[l].y)&&C[l].y.length&&
"number"===typeof C[l].y[0]&&"number"===typeof C[l].y[1]&&"number"===typeof C[l].y[2]&&"number"===typeof C[l].y[3]){w=a.axisX.convertValueToPixel(f);m=a.axisY.convertValueToPixel(C[l].y[0]);s=a.axisY.convertValueToPixel(C[l].y[1]);q=a.axisY.convertValueToPixel(C[l].y[2]);n=a.axisY.convertValueToPixel(C[l].y[3]);var z=w-h/2<<0,y=z+h<<0,g=t.options.fallingColor?t.fallingColor:t._colorSet[0],e=C[l].color?C[l].color:t._colorSet[0],A=Math.round(Math.max(1,0.15*h)),D=0===A%2?0:0.5,aa=t.dataPointIds[l];
this._eventManager.objectMap[aa]={id:aa,objectType:"dataPoint",dataSeriesIndex:v,dataPointIndex:l,x1:z,y1:m,x2:y,y2:s,x3:w,y3:q,x4:w,y4:n,borderThickness:A,color:e};b.strokeStyle=e;b.beginPath();b.lineWidth=A;c.lineWidth=Math.max(A,4);"candlestick"===t.type?(b.moveTo(w-D,s),b.lineTo(w-D,Math.min(m,n)),b.stroke(),b.moveTo(w-D,Math.max(m,n)),b.lineTo(w-D,q),b.stroke(),ea(b,z,Math.min(m,n),y,Math.max(m,n),C[l].y[0]<=C[l].y[3]?t.risingColor:g,A,e,x,x,!1,!1,t.fillOpacity),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w-
D,s),c.lineTo(w-D,Math.min(m,n)),c.stroke(),c.moveTo(w-D,Math.max(m,n)),c.lineTo(w-D,q),c.stroke(),ea(c,z,Math.min(m,n),y,Math.max(m,n),e,0,null,!1,!1,!1,!1))):"ohlc"===t.type&&(b.moveTo(w-D,s),b.lineTo(w-D,q),b.stroke(),b.beginPath(),b.moveTo(w,m),b.lineTo(z,m),b.stroke(),b.beginPath(),b.moveTo(w,n),b.lineTo(y,n),b.stroke(),r&&(e=N(aa),c.strokeStyle=e,c.moveTo(w-D,s),c.lineTo(w-D,q),c.stroke(),c.beginPath(),c.moveTo(w,m),c.lineTo(z,m),c.stroke(),c.beginPath(),c.moveTo(w,n),c.lineTo(y,n),c.stroke()));
(C[l].indexLabel||t.indexLabel||C[l].indexLabelFormatter||t.indexLabelFormatter)&&this._indexLabels.push({chartType:t.type,dataPoint:C[l],dataSeries:t,point:{x:z+(y-z)/2,y:a.axisY.reversed?q:s},direction:1,bounds:{x1:z,y1:Math.min(s,q),x2:y,y2:Math.max(s,q)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,
0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(k.x1,k.y1,k.width,k.height),c.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderBoxAndWhisker=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var e=
null,g=this.plotArea,k=0,l,w,m,s,q,n,f,e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1,k=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.015*this.width,h=a.axisX.dataInfo.minDiff;isFinite(h)||(h=0.3*Math.abs(a.axisX.range));h=this.options.dataPointWidth?this.dataPointWidth:0.7*g.width*(a.axisX.logarithmic?Math.log(h)/Math.log(a.axisX.range):Math.abs(h)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&&
e>k&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,k));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&k<e)&&(k=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,e));h<e&&(h=e);h>k&&(h=k);b.save();r&&c.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(c.beginPath(),c.rect(g.x1,g.y1,g.width,g.height),c.clip());for(var p=!1,p=!!a.axisY.reversed,v=0;v<a.dataSeriesIndexes.length;v++){var t=a.dataSeriesIndexes[v],C=this.data[t],x=C.dataPoints;
if(0<x.length)for(var z=5<h&&C.bevelEnabled?!0:!1,k=0;k<x.length;k++)if(x[k].getTime?f=x[k].x.getTime():f=x[k].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&!u(x[k].y)&&x[k].y.length&&"number"===typeof x[k].y[0]&&"number"===typeof x[k].y[1]&&"number"===typeof x[k].y[2]&&"number"===typeof x[k].y[3]&&"number"===typeof x[k].y[4]&&5===x[k].y.length){l=a.axisX.convertValueToPixel(f);w=a.axisY.convertValueToPixel(x[k].y[0]);m=a.axisY.convertValueToPixel(x[k].y[1]);s=a.axisY.convertValueToPixel(x[k].y[2]);
q=a.axisY.convertValueToPixel(x[k].y[3]);n=a.axisY.convertValueToPixel(x[k].y[4]);var y=l-h/2<<0,A=l+h/2<<0,e=x[k].color?x[k].color:C._colorSet[0],D=Math.round(Math.max(1,0.15*h)),aa=0===D%2?0:0.5,T=x[k].whiskerColor?x[k].whiskerColor:x[k].color?C.whiskerColor?C.whiskerColor:x[k].color:C.whiskerColor?C.whiskerColor:e,Y="number"===typeof x[k].whiskerThickness?x[k].whiskerThickness:"number"===typeof C.options.whiskerThickness?C.whiskerThickness:D,ca=x[k].whiskerDashType?x[k].whiskerDashType:C.whiskerDashType,
da=u(x[k].whiskerLength)?u(C.options.whiskerLength)?h:C.whiskerLength:x[k].whiskerLength,da="number"===typeof da?0>=da?0:da>=h?h:da:"string"===typeof da?parseInt(da)*h/100>h?h:parseInt(da)*h/100:h,Z=1===Math.round(Y)%2?0.5:0,oa=x[k].stemColor?x[k].stemColor:x[k].color?C.stemColor?C.stemColor:x[k].color:C.stemColor?C.stemColor:e,la="number"===typeof x[k].stemThickness?x[k].stemThickness:"number"===typeof C.options.stemThickness?C.stemThickness:D,G=1===Math.round(la)%2?0.5:0,F=x[k].stemDashType?x[k].stemDashType:
C.stemDashType,E=x[k].lineColor?x[k].lineColor:x[k].color?C.lineColor?C.lineColor:x[k].color:C.lineColor?C.lineColor:e,H="number"===typeof x[k].lineThickness?x[k].lineThickness:"number"===typeof C.options.lineThickness?C.lineThickness:D,I=x[k].lineDashType?x[k].lineDashType:C.lineDashType,K=1===Math.round(H)%2?0.5:0,L=C.upperBoxColor,O=C.lowerBoxColor,Q=u(C.options.fillOpacity)?1:C.fillOpacity,P=C.dataPointIds[k];this._eventManager.objectMap[P]={id:P,objectType:"dataPoint",dataSeriesIndex:t,dataPointIndex:k,
x1:y,y1:w,x2:A,y2:m,x3:l,y3:s,x4:l,y4:q,y5:n,borderThickness:D,color:e,stemThickness:la,stemColor:oa,whiskerThickness:Y,whiskerLength:da,whiskerColor:T,lineThickness:H,lineColor:E};b.save();0<la&&(b.beginPath(),b.strokeStyle=oa,b.lineWidth=la,b.setLineDash&&b.setLineDash(R(F,la)),b.moveTo(l-G,m),b.lineTo(l-G,w),b.stroke(),b.moveTo(l-G,q),b.lineTo(l-G,s),b.stroke());b.restore();c.lineWidth=Math.max(D,4);b.beginPath();ea(b,y,Math.min(n,m),A,Math.max(m,n),O,0,e,p?z:!1,p?!1:z,!1,!1,Q);b.beginPath();ea(b,
y,Math.min(s,n),A,Math.max(n,s),L,0,e,p?!1:z,p?z:!1,!1,!1,Q);b.beginPath();b.lineWidth=D;b.strokeStyle=e;b.rect(y-aa,Math.min(m,s)-aa,A-y+2*aa,Math.max(m,s)-Math.min(m,s)+2*aa);b.stroke();b.save();0<H&&(b.beginPath(),b.globalAlpha=1,b.setLineDash&&b.setLineDash(R(I,H)),b.strokeStyle=E,b.lineWidth=H,b.moveTo(y,n-K),b.lineTo(A,n-K),b.stroke());b.restore();b.save();0<Y&&(b.beginPath(),b.setLineDash&&b.setLineDash(R(ca,Y)),b.strokeStyle=T,b.lineWidth=Y,b.moveTo(l-da/2<<0,q-Z),b.lineTo(l+da/2<<0,q-Z),
b.stroke(),b.moveTo(l-da/2<<0,w+Z),b.lineTo(l+da/2<<0,w+Z),b.stroke());b.restore();r&&(e=N(P),c.strokeStyle=e,c.lineWidth=la,0<la&&(c.moveTo(l-aa-G,m),c.lineTo(l-aa-G,Math.max(w,q)),c.stroke(),c.moveTo(l-aa-G,Math.min(w,q)),c.lineTo(l-aa-G,s),c.stroke()),ea(c,y,Math.max(m,s),A,Math.min(m,s),e,0,null,!1,!1,!1,!1),0<Y&&(c.beginPath(),c.lineWidth=Y,c.moveTo(l+da/2,q-Z),c.lineTo(l-da/2,q-Z),c.stroke(),c.moveTo(l+da/2,w+Z),c.lineTo(l-da/2,w+Z),c.stroke()));(x[k].indexLabel||C.indexLabel||x[k].indexLabelFormatter||
C.indexLabelFormatter)&&this._indexLabels.push({chartType:C.type,dataPoint:x[k],dataSeries:C,point:{x:y+(A-y)/2,y:a.axisY.reversed?w:q},direction:1,bounds:{x1:y,y1:Math.min(w,q),x2:A,y2:Math.max(w,q)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,
0,0,this.width,this.height),b.clearRect(g.x1,g.y1,g.width,g.height),c.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeColumn=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,k,l,w,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:
1;k=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:0.03*this.width;var m=a.axisX.dataInfo.minDiff;isFinite(m)||(m=0.3*Math.abs(a.axisX.range));m=this.options.dataPointWidth?this.dataPointWidth:0.9*(e.width*(a.axisX.logarithmic?Math.log(m)/Math.log(a.axisX.range):Math.abs(m)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>k&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,k));!this.dataPointMaxWidth&&
(this.dataPointMinWidth&&k<g)&&(k=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,g));m<g&&(m=g);m>k&&(m=k);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var s=0;s<a.dataSeriesIndexes.length;s++){var q=a.dataSeriesIndexes[s],n=this.data[q],f=n.dataPoints;if(0<f.length)for(var h=
5<m&&n.bevelEnabled?!0:!1,g=0;g<f.length;g++)if(f[g].getTime?w=f[g].x.getTime():w=f[g].x,!(w<a.axisX.dataInfo.viewPortMin||w>a.axisX.dataInfo.viewPortMax)&&!u(f[g].y)&&f[g].y.length&&"number"===typeof f[g].y[0]&&"number"===typeof f[g].y[1]){c=a.axisX.convertValueToPixel(w);k=a.axisY.convertValueToPixel(f[g].y[0]);l=a.axisY.convertValueToPixel(f[g].y[1]);var p=a.axisX.reversed?c+a.plotType.totalDataSeries*m/2-(a.previousDataSeriesCount+s)*m<<0:c-a.plotType.totalDataSeries*m/2+(a.previousDataSeriesCount+
s)*m<<0,v=a.axisX.reversed?p-m<<0:p+m<<0,c=f[g].color?f[g].color:n._colorSet[g%n._colorSet.length];if(k>l){var t=k;k=l;l=t}t=n.dataPointIds[g];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:q,dataPointIndex:g,x1:p,y1:k,x2:v,y2:l};ea(b,p,k,v,l,c,0,c,h,h,!1,!1,n.fillOpacity);c=N(t);r&&ea(this._eventManager.ghostCtx,p,k,v,l,c,0,null,!1,!1,!1,!1);if(f[g].indexLabel||n.indexLabel||f[g].indexLabelFormatter||n.indexLabelFormatter)this._indexLabels.push({chartType:"rangeColumn",
dataPoint:f[g],dataSeries:n,indexKeyword:0,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?l:k},direction:f[g].y[1]>=f[g].y[0]?-1:1,bounds:{x1:p,y1:Math.min(k,l),x2:v,y2:Math.max(k,l)},color:c}),this._indexLabels.push({chartType:"rangeColumn",dataPoint:f[g],dataSeries:n,indexKeyword:1,point:{x:p+(v-p)/2,y:f[g].y[1]>=f[g].y[0]?k:l},direction:f[g].y[1]>=f[g].y[0]?1:-1,bounds:{x1:p,y1:Math.min(k,l),x2:v,y2:Math.max(k,l)},color:c})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation=
"source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderError=
function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:d,c=a.axisY._position?"left"===a.axisY._position||"right"===a.axisY._position?!1:!0:!1;if(!(0>=a.dataSeriesIndexes.length)){var e=null,g=!1,k=this.plotArea,l=0,w,m,s,q,n,f,h,p=a.axisX.dataInfo.minDiff;isFinite(p)||(p=0.3*Math.abs(a.axisX.range));b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(k.x1,k.y1,k.width,k.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(k.x1,
k.y1,k.width,k.height),this._eventManager.ghostCtx.clip());for(var v=0,t=0;t<this.data.length;t++)!this.data[t].type.match(/(bar|column)/ig)||!this.data[t].visible||this.data[t].type.match(/(stacked)/ig)&&v||v++;for(var C=0;C<a.dataSeriesIndexes.length;C++){var x=a.dataSeriesIndexes[C],z=this.data[x],y=z.dataPoints,A=u(z._linkedSeries)?!1:z._linkedSeries.type.match(/(bar|column)/ig)&&z._linkedSeries.visible?!0:!1,D=0;if(A)for(e=z._linkedSeries.id,t=0;t<e;t++)!this.data[t].type.match(/(bar|column)/ig)||
!this.data[t].visible||this.data[t].type.match(/(stacked)/ig)&&D||(this.data[t].type.match(/(range)/ig)&&(g=!0),D++);e=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;l=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:c?Math.min(0.15*this.height,0.9*(this.plotArea.height/(A?v:1)))<<0:0.3*this.width;g&&(l=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:
c?Math.min(0.15*this.height,0.9*(this.plotArea.height/(A?v:1)))<<0:0.03*this.width);t=this.options.dataPointWidth?this.dataPointWidth:0.9*((c?k.height:k.width)*(a.axisX.logarithmic?Math.log(p)/Math.log(a.axisX.range):Math.abs(p)/Math.abs(a.axisX.range))/(A?v:1))<<0;this.dataPointMaxWidth&&e>l&&(e=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,l));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&l<e)&&(l=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,e));t<e&&
(t=e);t>l&&(t=l);if(0<y.length)for(var aa=z._colorSet,l=0;l<y.length;l++){var e=z.lineColor=z.options.color?z.options.color:aa[0],T={color:y[l].whiskerColor?y[l].whiskerColor:y[l].color?z.whiskerColor?z.whiskerColor:y[l].color:z.whiskerColor?z.whiskerColor:e,thickness:u(y[l].whiskerThickness)?z.whiskerThickness:y[l].whiskerThickness,dashType:y[l].whiskerDashType?y[l].whiskerDashType:z.whiskerDashType,length:u(y[l].whiskerLength)?u(z.options.whiskerLength)?t:z.options.whiskerLength:y[l].whiskerLength,
trimLength:u(y[l].whiskerLength)?u(z.options.whiskerLength)?50:0:0};T.length="number"===typeof T.length?0>=T.length?0:T.length>=t?t:T.length:"string"===typeof T.length?parseInt(T.length)*t/100>t?t:parseInt(T.length)*t/100>t:t;T.thickness="number"===typeof T.thickness?0>T.thickness?0:Math.round(T.thickness):2;var Y={color:y[l].stemColor?y[l].stemColor:y[l].color?z.stemColor?z.stemColor:y[l].color:z.stemColor?z.stemColor:e,thickness:y[l].stemThickness?y[l].stemThickness:z.stemThickness,dashType:y[l].stemDashType?
y[l].stemDashType:z.stemDashType};Y.thickness="number"===typeof Y.thickness?0>Y.thickness?0:Math.round(Y.thickness):2;y[l].getTime?h=y[l].x.getTime():h=y[l].x;if(!(h<a.axisX.dataInfo.viewPortMin||h>a.axisX.dataInfo.viewPortMax)&&!u(y[l].y)&&y[l].y.length&&"number"===typeof y[l].y[0]&&"number"===typeof y[l].y[1]){var ca=a.axisX.convertValueToPixel(h);c?m=ca:w=ca;ca=a.axisY.convertValueToPixel(y[l].y[0]);c?s=ca:n=ca;ca=a.axisY.convertValueToPixel(y[l].y[1]);c?q=ca:f=ca;c?(n=a.axisX.reversed?m+(A?v:
1)*t/2-(A?D-1:0)*t<<0:m-(A?v:1)*t/2+(A?D-1:0)*t<<0,f=a.axisX.reversed?n-t<<0:n+t<<0):(s=a.axisX.reversed?w+(A?v:1)*t/2-(A?D-1:0)*t<<0:w-(A?v:1)*t/2+(A?D-1:0)*t<<0,q=a.axisX.reversed?s-t<<0:s+t<<0);!c&&n>f&&(ca=n,n=f,f=ca);c&&s>q&&(ca=s,s=q,q=ca);ca=z.dataPointIds[l];this._eventManager.objectMap[ca]={id:ca,objectType:"dataPoint",dataSeriesIndex:x,dataPointIndex:l,x1:Math.min(s,q),y1:Math.min(n,f),x2:Math.max(q,s),y2:Math.max(f,n),isXYSwapped:c,stemProperties:Y,whiskerProperties:T};E(b,Math.min(s,q),
Math.min(n,f),Math.max(q,s),Math.max(f,n),e,T,Y,c);r&&E(this._eventManager.ghostCtx,s,n,q,f,e,T,Y,c);if(y[l].indexLabel||z.indexLabel||y[l].indexLabelFormatter||z.indexLabelFormatter)this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:0,point:{x:c?y[l].y[1]>=y[l].y[0]?s:q:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?f:n},direction:y[l].y[1]>=y[l].y[0]?-1:1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c}),
this._indexLabels.push({chartType:"error",dataPoint:y[l],dataSeries:z,indexKeyword:1,point:{x:c?y[l].y[1]>=y[l].y[0]?q:s:s+(q-s)/2,y:c?n+(f-n)/2:y[l].y[1]>=y[l].y[0]?n:f},direction:y[l].y[1]>=y[l].y[0]?1:-1,bounds:{x1:c?Math.min(s,q):s,y1:c?n:Math.min(n,f),x2:c?Math.max(s,q):q,y2:c?f:Math.max(n,f)},color:e,axisSwapped:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),
a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(k.x1,k.y1,k.width,k.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeBar=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,b=r?this._preRenderCtx:
d;if(!(0>=a.dataSeriesIndexes.length)){var c=null,e=this.plotArea,g=0,k,l,w,m,g=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;k=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0;var s=a.axisX.dataInfo.minDiff;isFinite(s)||(s=0.3*Math.abs(a.axisX.range));s=this.options.dataPointWidth?this.dataPointWidth:0.9*
(e.height*(a.axisX.logarithmic?Math.log(s)/Math.log(a.axisX.range):Math.abs(s)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&g>k&&(g=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,k));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&k<g)&&(k=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,g));s<g&&(s=g);s>k&&(s=k);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(e.x1,e.y1,e.width,e.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),
this._eventManager.ghostCtx.rect(e.x1,e.y1,e.width,e.height),this._eventManager.ghostCtx.clip());for(var q=0;q<a.dataSeriesIndexes.length;q++){var n=a.dataSeriesIndexes[q],f=this.data[n],h=f.dataPoints;if(0<h.length){var p=5<s&&f.bevelEnabled?!0:!1;b.strokeStyle="#4572A7 ";for(g=0;g<h.length;g++)if(h[g].getTime?m=h[g].x.getTime():m=h[g].x,!(m<a.axisX.dataInfo.viewPortMin||m>a.axisX.dataInfo.viewPortMax)&&!u(h[g].y)&&h[g].y.length&&"number"===typeof h[g].y[0]&&"number"===typeof h[g].y[1]){k=a.axisY.convertValueToPixel(h[g].y[0]);
l=a.axisY.convertValueToPixel(h[g].y[1]);w=a.axisX.convertValueToPixel(m);w=a.axisX.reversed?w+a.plotType.totalDataSeries*s/2-(a.previousDataSeriesCount+q)*s<<0:w-a.plotType.totalDataSeries*s/2+(a.previousDataSeriesCount+q)*s<<0;var v=a.axisX.reversed?w-s<<0:w+s<<0;k>l&&(c=k,k=l,l=c);c=h[g].color?h[g].color:f._colorSet[g%f._colorSet.length];ea(b,k,w,l,v,c,0,null,p,!1,!1,!1,f.fillOpacity);c=f.dataPointIds[g];this._eventManager.objectMap[c]={id:c,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:g,
x1:k,y1:w,x2:l,y2:v};c=N(c);r&&ea(this._eventManager.ghostCtx,k,w,l,v,c,0,null,!1,!1,!1,!1);if(h[g].indexLabel||f.indexLabel||h[g].indexLabelFormatter||f.indexLabelFormatter)this._indexLabels.push({chartType:"rangeBar",dataPoint:h[g],dataSeries:f,indexKeyword:0,point:{x:h[g].y[1]>=h[g].y[0]?k:l,y:w+(v-w)/2},direction:h[g].y[1]>=h[g].y[0]?-1:1,bounds:{x1:Math.min(k,l),y1:w,x2:Math.max(k,l),y2:v},color:c}),this._indexLabels.push({chartType:"rangeBar",dataPoint:h[g],dataSeries:f,indexKeyword:1,point:{x:h[g].y[1]>=
h[g].y[0]?l:k,y:w+(v-w)/2},direction:h[g].y[1]>=h[g].y[0]?1:-1,bounds:{x1:Math.min(k,l),y1:w,x2:Math.max(k,l),y2:v},color:c})}}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.clearRect(e.x1,
e.y1,e.width,e.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};p.prototype.renderRangeArea=function(a){function d(){if(C){var a=null;0<s.lineThickness&&c.stroke();for(var b=w.length-1;0<=b;b--)a=w[b],c.lineTo(a.x,a.y),e.lineTo(a.x,a.y);c.closePath();c.globalAlpha=s.fillOpacity;c.fill();c.globalAlpha=1;e.fill();if(0<s.lineThickness){c.beginPath();c.moveTo(a.x,
a.y);for(b=0;b<w.length;b++)a=w[b],c.lineTo(a.x,a.y);c.stroke()}c.beginPath();c.moveTo(h,p);e.beginPath();e.moveTo(h,p);C={x:h,y:p};w=[];w.push({x:h,y:u})}}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],k=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();r&&(e.beginPath(),e.rect(k.x1,k.y1,k.width,k.height),e.clip());for(var l=0;l<a.dataSeriesIndexes.length;l++){var w=
[],m=a.dataSeriesIndexes[l],s=this.data[m],q=s.dataPoints,g=s.id;this._eventManager.objectMap[g]={objectType:"dataSeries",dataSeriesIndex:m};g=N(g);e.fillStyle=g;var g=[],n=!0,f=0,h,p,u,t,C=null;if(0<q.length){var x=s._colorSet[f%s._colorSet.length],v=s.lineColor=s.options.lineColor||x,y=v;c.fillStyle=x;c.strokeStyle=v;c.lineWidth=s.lineThickness;var A="solid";if(c.setLineDash){var z=R(s.nullDataLineDashType,s.lineThickness),A=s.lineDashType,D=R(A,s.lineThickness);c.setLineDash(D)}for(var T=!0;f<
q.length;f++)if(t=q[f].x.getTime?q[f].x.getTime():q[f].x,!(t<a.axisX.dataInfo.viewPortMin||t>a.axisX.dataInfo.viewPortMax&&(!s.connectNullData||!T)))if(null!==q[f].y&&q[f].y.length&&"number"===typeof q[f].y[0]&&"number"===typeof q[f].y[1]){h=a.axisX.convertValueToPixel(t);p=a.axisY.convertValueToPixel(q[f].y[0]);u=a.axisY.convertValueToPixel(q[f].y[1]);n||T?(s.connectNullData&&!n?(c.setLineDash&&(s.options.nullDataLineDashType||A===s.lineDashType&&s.lineDashType!==s.nullDataLineDashType)&&(w[w.length-
1].newLineDashArray=D,A=s.nullDataLineDashType,c.setLineDash(z)),c.lineTo(h,p),r&&e.lineTo(h,p),w.push({x:h,y:u})):(c.beginPath(),c.moveTo(h,p),C={x:h,y:p},w=[],w.push({x:h,y:u}),r&&(e.beginPath(),e.moveTo(h,p))),T=n=!1):(c.lineTo(h,p),w.push({x:h,y:u}),r&&e.lineTo(h,p),0==f%250&&d());t=s.dataPointIds[f];this._eventManager.objectMap[t]={id:t,objectType:"dataPoint",dataSeriesIndex:m,dataPointIndex:f,x1:h,y1:p,y2:u};f<q.length-1&&(y!==(q[f].lineColor||v)||A!==(q[f].lineDashType||s.lineDashType))&&(d(),
y=q[f].lineColor||v,w[w.length-1].newStrokeStyle=y,c.strokeStyle=y,c.setLineDash&&(q[f].lineDashType?(A=q[f].lineDashType,w[w.length-1].newLineDashArray=R(A,s.lineThickness),c.setLineDash(w[w.length-1].newLineDashArray)):(A=s.lineDashType,w[w.length-1].newLineDashArray=D,c.setLineDash(D))));if(0!==q[f].markerSize&&(0<q[f].markerSize||0<s.markerSize)){var Y=s.getMarkerProperties(f,h,u,c);g.push(Y);var ca=N(t);r&&g.push({x:h,y:u,ctx:e,type:Y.type,size:Y.size,color:ca,borderColor:ca,borderThickness:Y.borderThickness});
Y=s.getMarkerProperties(f,h,p,c);g.push(Y);ca=N(t);r&&g.push({x:h,y:p,ctx:e,type:Y.type,size:Y.size,color:ca,borderColor:ca,borderThickness:Y.borderThickness})}if(q[f].indexLabel||s.indexLabel||q[f].indexLabelFormatter||s.indexLabelFormatter)this._indexLabels.push({chartType:"rangeArea",dataPoint:q[f],dataSeries:s,indexKeyword:0,point:{x:h,y:p},direction:q[f].y[0]>q[f].y[1]===a.axisY.reversed?-1:1,color:x}),this._indexLabels.push({chartType:"rangeArea",dataPoint:q[f],dataSeries:s,indexKeyword:1,point:{x:h,
y:u},direction:q[f].y[0]>q[f].y[1]===a.axisY.reversed?1:-1,color:x})}else T||n||d(),T=!0;d();ia.drawMarkers(g)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(k.x1,k.y1,
k.width,k.height),this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderRangeSplineArea=function(a){function d(a,b){var d=v(u,2);if(0<d.length){if(0<m.lineThickness){c.strokeStyle=b;c.setLineDash&&c.setLineDash(a);c.beginPath();c.moveTo(d[0].x,d[0].y);for(var f=0;f<d.length-3;f+=3){if(d[f].newStrokeStyle||d[f].newLineDashArray)c.stroke(),c.beginPath(),c.moveTo(d[f].x,
d[f].y),d[f].newStrokeStyle&&(c.strokeStyle=d[f].newStrokeStyle),d[f].newLineDashArray&&c.setLineDash(d[f].newLineDashArray);c.bezierCurveTo(d[f+1].x,d[f+1].y,d[f+2].x,d[f+2].y,d[f+3].x,d[f+3].y)}c.stroke()}c.beginPath();c.moveTo(d[0].x,d[0].y);r&&(e.beginPath(),e.moveTo(d[0].x,d[0].y));for(f=0;f<d.length-3;f+=3)c.bezierCurveTo(d[f+1].x,d[f+1].y,d[f+2].x,d[f+2].y,d[f+3].x,d[f+3].y),r&&e.bezierCurveTo(d[f+1].x,d[f+1].y,d[f+2].x,d[f+2].y,d[f+3].x,d[f+3].y);d=v(z,2);c.lineTo(z[z.length-1].x,z[z.length-
1].y);for(f=d.length-1;2<f;f-=3)c.bezierCurveTo(d[f-1].x,d[f-1].y,d[f-2].x,d[f-2].y,d[f-3].x,d[f-3].y),r&&e.bezierCurveTo(d[f-1].x,d[f-1].y,d[f-2].x,d[f-2].y,d[f-3].x,d[f-3].y);c.closePath();c.globalAlpha=m.fillOpacity;c.fill();r&&(e.closePath(),e.fill());c.globalAlpha=1;if(0<m.lineThickness){c.strokeStyle=b;c.setLineDash&&c.setLineDash(a);c.beginPath();c.moveTo(d[0].x,d[0].y);for(var g=f=0;f<d.length-3;f+=3,g++){if(u[g].newStrokeStyle||u[g].newLineDashArray)c.stroke(),c.beginPath(),c.moveTo(d[f].x,
d[f].y),u[g].newStrokeStyle&&(c.strokeStyle=u[g].newStrokeStyle),u[g].newLineDashArray&&c.setLineDash(u[g].newLineDashArray);c.bezierCurveTo(d[f+1].x,d[f+1].y,d[f+2].x,d[f+2].y,d[f+3].x,d[f+3].y)}c.stroke()}c.beginPath()}}var b=a.targetCanvasCtx||this.plotArea.ctx,c=r?this._preRenderCtx:b;if(!(0>=a.dataSeriesIndexes.length)){var e=this._eventManager.ghostCtx,g=[],k=this.plotArea;c.save();r&&e.save();c.beginPath();c.rect(k.x1,k.y1,k.width,k.height);c.clip();r&&(e.beginPath(),e.rect(k.x1,k.y1,k.width,
k.height),e.clip());for(var l=0;l<a.dataSeriesIndexes.length;l++){var w=a.dataSeriesIndexes[l],m=this.data[w],h=m.dataPoints,g=m.id;this._eventManager.objectMap[g]={objectType:"dataSeries",dataSeriesIndex:w};g=N(g);e.fillStyle=g;var g=[],q=0,n,f,p,u=[],z=[];if(0<h.length){var t=m._colorSet[q%m._colorSet.length],C=m.lineColor=m.options.lineColor||t,x=C;c.fillStyle=t;c.lineWidth=m.lineThickness;var F="solid",y;if(c.setLineDash){var A=R(m.nullDataLineDashType,m.lineThickness),F=m.lineDashType;y=R(F,
m.lineThickness)}for(f=!1;q<h.length;q++)if(n=h[q].x.getTime?h[q].x.getTime():h[q].x,!(n<a.axisX.dataInfo.viewPortMin||n>a.axisX.dataInfo.viewPortMax&&(!m.connectNullData||!f)))if(null!==h[q].y&&h[q].y.length&&"number"===typeof h[q].y[0]&&"number"===typeof h[q].y[1]){n=a.axisX.convertValueToPixel(n);f=a.axisY.convertValueToPixel(h[q].y[0]);p=a.axisY.convertValueToPixel(h[q].y[1]);var E=m.dataPointIds[q];this._eventManager.objectMap[E]={id:E,objectType:"dataPoint",dataSeriesIndex:w,dataPointIndex:q,
x1:n,y1:f,y2:p};u[u.length]={x:n,y:f};z[z.length]={x:n,y:p};q<h.length-1&&(x!==(h[q].lineColor||C)||F!==(h[q].lineDashType||m.lineDashType))&&(x=h[q].lineColor||C,u[u.length-1].newStrokeStyle=x,c.setLineDash&&(h[q].lineDashType?(F=h[q].lineDashType,u[u.length-1].newLineDashArray=R(F,m.lineThickness)):(F=m.lineDashType,u[u.length-1].newLineDashArray=y)));if(0!==h[q].markerSize&&(0<h[q].markerSize||0<m.markerSize)){var aa=m.getMarkerProperties(q,n,f,c);g.push(aa);var T=N(E);r&&g.push({x:n,y:f,ctx:e,
type:aa.type,size:aa.size,color:T,borderColor:T,borderThickness:aa.borderThickness});aa=m.getMarkerProperties(q,n,p,c);g.push(aa);T=N(E);r&&g.push({x:n,y:p,ctx:e,type:aa.type,size:aa.size,color:T,borderColor:T,borderThickness:aa.borderThickness})}if(h[q].indexLabel||m.indexLabel||h[q].indexLabelFormatter||m.indexLabelFormatter)this._indexLabels.push({chartType:"rangeSplineArea",dataPoint:h[q],dataSeries:m,indexKeyword:0,point:{x:n,y:f},direction:h[q].y[0]<=h[q].y[1]?-1:1,color:t}),this._indexLabels.push({chartType:"rangeSplineArea",
dataPoint:h[q],dataSeries:m,indexKeyword:1,point:{x:n,y:p},direction:h[q].y[0]<=h[q].y[1]?1:-1,color:t});f=!1}else 0<q&&!f&&(m.connectNullData?c.setLineDash&&(0<u.length&&(m.options.nullDataLineDashType||!h[q-1].lineDashType))&&(u[u.length-1].newLineDashArray=A,F=m.nullDataLineDashType):(d(y,C),u=[],z=[])),f=!0;d(y,C);ia.drawMarkers(g)}}r&&(b.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&c.drawImage(a.axisX.maskCanvas,0,0,
this.width,this.height),a.axisY.maskCanvas&&c.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),c.clearRect(k.x1,k.y1,k.width,k.height),this._eventManager.ghostCtx.restore());c.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:M.xClipAnimation,easingFunction:M.easing.linear,animationBase:0}}};p.prototype.renderWaterfall=function(a){var d=a.targetCanvasCtx||this.plotArea.ctx,
b=r?this._preRenderCtx:d;if(!(0>=a.dataSeriesIndexes.length)){var c=this._eventManager.ghostCtx,e=null,g=this.plotArea,k=0,l,h,m,s,q=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),k=this.options.dataPointMinWidth?this.dataPointMinWidth:this.options.dataPointWidth?this.dataPointWidth:1;h=this.options.dataPointMaxWidth?this.dataPointMaxWidth:this.options.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width,0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0;var n=
a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.options.dataPointWidth?this.dataPointWidth:0.6*(g.width*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&k>h&&(k=Math.min(this.options.dataPointWidth?this.dataPointWidth:Infinity,h));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&h<k)&&(h=Math.max(this.options.dataPointWidth?this.dataPointWidth:-Infinity,k));n<k&&(n=k);
n>h&&(n=h);b.save();r&&this._eventManager.ghostCtx.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();r&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.clip());for(var f=0;f<a.dataSeriesIndexes.length;f++){var p=a.dataSeriesIndexes[f],u=this.data[p],v=u.dataPoints,e=u._colorSet[0];u.risingColor=u.options.risingColor?u.options.risingColor:e;u.fallingColor=u.options.fallingColor?u.options.fallingColor:"#e40a0a";
var t="number"===typeof u.options.lineThickness?Math.round(u.lineThickness):1,C=1===Math.round(t)%2?-0.5:0;if(0<v.length)for(var x=5<n&&u.bevelEnabled?!0:!1,z=!1,y=null,A=null,k=0;k<v.length;k++)if(v[k].getTime?s=v[k].x.getTime():s=v[k].x,"number"!==typeof v[k].y){if(0<k&&!z&&u.connectNullData)var D=u.options.nullDataLineDashType||!v[k-1].lineDashType?u.nullDataLineDashType:v[k-1].lineDashType;z=!0}else{l=a.axisX.convertValueToPixel(s);h=0===u.dataPointEOs[k].cumulativeSum?q:a.axisY.convertValueToPixel(u.dataPointEOs[k].cumulativeSum);
m=0===u.dataPointEOs[k].cumulativeSumYStartValue?q:a.axisY.convertValueToPixel(u.dataPointEOs[k].cumulativeSumYStartValue);l=a.axisX.reversed?l+a.plotType.totalDataSeries*n/2-(a.previousDataSeriesCount+f)*n<<0:l-a.plotType.totalDataSeries*n/2+(a.previousDataSeriesCount+f)*n<<0;var F=a.axisX.reversed?l-n<<0:l+n<<0;h>m&&(e=h,h=m,m=e);a.axisY.reversed&&(e=h,h=m,m=e);e=u.dataPointIds[k];this._eventManager.objectMap[e]={id:e,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:k,x1:l,y1:h,x2:F,y2:m};
var T=v[k].color?v[k].color:0<v[k].y?u.risingColor:u.fallingColor;ea(b,l,h,F,m,T,0,T,x,x,!1,!1,u.fillOpacity);e=N(e);r&&ea(this._eventManager.ghostCtx,l,h,F,m,e,0,null,!1,!1,!1,!1);var Y,T=l;Y="undefined"!==typeof v[k].isIntermediateSum&&!0===v[k].isIntermediateSum||"undefined"!==typeof v[k].isCumulativeSum&&!0===v[k].isCumulativeSum?0<v[k].y?h:m:0<v[k].y?m:h;0<k&&y&&(!z||u.connectNullData)&&(z&&b.setLineDash&&b.setLineDash(R(D,t)),b.beginPath(),b.moveTo(y,A-C),b.lineTo(T,Y-C),0<t&&b.stroke(),r&&
(c.beginPath(),c.moveTo(y,A-C),c.lineTo(T,Y-C),0<t&&c.stroke()));z=!1;y=F;A=0<v[k].y?h:m;T=v[k].lineDashType?v[k].lineDashType:u.options.lineDashType?u.options.lineDashType:"shortDash";b.strokeStyle=v[k].lineColor?v[k].lineColor:u.options.lineColor?u.options.lineColor:"#9e9e9e";b.lineWidth=t;b.setLineDash&&(T=R(T,t),b.setLineDash(T));(v[k].indexLabel||u.indexLabel||v[k].indexLabelFormatter||u.indexLabelFormatter)&&this._indexLabels.push({chartType:"waterfall",dataPoint:v[k],dataSeries:u,point:{x:l+
(F-l)/2,y:0<=v[k].y?h:m},direction:0>v[k].y===a.axisY.reversed?1:-1,bounds:{x1:l,y1:Math.min(h,m),x2:F,y2:Math.max(h,m)},color:e})}}r&&(d.drawImage(this._preRenderCanvas,0,0,this.width,this.height),b.globalCompositeOperation="source-atop",a.axisX.maskCanvas&&b.drawImage(a.axisX.maskCanvas,0,0,this.width,this.height),a.axisY.maskCanvas&&b.drawImage(a.axisY.maskCanvas,0,0,this.width,this.height),this._breaksCanvasCtx&&this._breaksCanvasCtx.drawImage(this._preRenderCanvas,0,0,this.width,this.height),
b.clearRect(g.x1,g.y1,g.width,g.height),this._eventManager.ghostCtx.restore());b.restore();return{source:d,dest:this.plotArea.ctx,animationCallback:M.fadeInAnimation,easingFunction:M.easing.easeInQuad,animationBase:0}}};var ja=function(a,d,b,c,e,g,k,l,h){if(!(0>b)){"undefined"===typeof l&&(l=1);if(!r){var m=Number((k%(2*Math.PI)).toFixed(8));Number((g%(2*Math.PI)).toFixed(8))===m&&(k-=1E-4)}a.save();a.globalAlpha=l;"pie"===e?(a.beginPath(),a.moveTo(d.x,d.y),a.arc(d.x,d.y,b,g,k,!1),a.fillStyle=c,a.strokeStyle=
"white",a.lineWidth=2,a.closePath(),a.fill()):"doughnut"===e&&(a.beginPath(),a.arc(d.x,d.y,b,g,k,!1),0<=h&&a.arc(d.x,d.y,h*b,k,g,!0),a.closePath(),a.fillStyle=c,a.strokeStyle="white",a.lineWidth=2,a.fill());a.globalAlpha=1;a.restore()}};p.prototype.renderPie=function(a){function d(){if(m&&s){for(var a=0,b=0,c=0,e=0,d=0;d<s.length;d++){var g=s[d],l=m.dataPointIds[d];f[d].id=l;f[d].objectType="dataPoint";f[d].dataPointIndex=d;f[d].dataSeriesIndex=0;var k=f[d],q={percent:null,total:null},p=null,q=h.getPercentAndTotal(m,
g);if(m.indexLabelFormatter||g.indexLabelFormatter)p={chart:h.options,dataSeries:m,dataPoint:g,total:q.total,percent:q.percent};q=g.indexLabelFormatter?g.indexLabelFormatter(p):g.indexLabel?h.replaceKeywordsWithValue(g.indexLabel,g,m,d):m.indexLabelFormatter?m.indexLabelFormatter(p):m.indexLabel?h.replaceKeywordsWithValue(m.indexLabel,g,m,d):g.label?g.label:"";h._eventManager.objectMap[l]=k;k.center={x:x.x,y:x.y};k.y=g.y;k.radius=A;k.percentInnerRadius=F;k.indexLabelText=q;k.indexLabelPlacement=m.indexLabelPlacement;
k.indexLabelLineColor=g.indexLabelLineColor?g.indexLabelLineColor:m.options.indexLabelLineColor?m.options.indexLabelLineColor:g.color?g.color:m._colorSet[d%m._colorSet.length];k.indexLabelLineThickness=u(g.indexLabelLineThickness)?m.indexLabelLineThickness:g.indexLabelLineThickness;k.indexLabelLineDashType=g.indexLabelLineDashType?g.indexLabelLineDashType:m.indexLabelLineDashType;k.indexLabelFontColor=g.indexLabelFontColor?g.indexLabelFontColor:m.indexLabelFontColor;k.indexLabelFontStyle=g.indexLabelFontStyle?
g.indexLabelFontStyle:m.indexLabelFontStyle;k.indexLabelFontWeight=g.indexLabelFontWeight?g.indexLabelFontWeight:m.indexLabelFontWeight;k.indexLabelFontSize=u(g.indexLabelFontSize)?m.indexLabelFontSize:g.indexLabelFontSize;k.indexLabelFontFamily=g.indexLabelFontFamily?g.indexLabelFontFamily:m.indexLabelFontFamily;k.indexLabelBackgroundColor=g.indexLabelBackgroundColor?g.indexLabelBackgroundColor:m.options.indexLabelBackgroundColor?m.options.indexLabelBackgroundColor:m.indexLabelBackgroundColor;k.indexLabelMaxWidth=
g.indexLabelMaxWidth?g.indexLabelMaxWidth:m.indexLabelMaxWidth?m.indexLabelMaxWidth:0.33*n.width;k.indexLabelWrap="undefined"!==typeof g.indexLabelWrap?g.indexLabelWrap:m.indexLabelWrap;k.startAngle=0===d?m.startAngle?m.startAngle/180*Math.PI:0:f[d-1].endAngle;k.startAngle=(k.startAngle+2*Math.PI)%(2*Math.PI);k.endAngle=k.startAngle+2*Math.PI/z*Math.abs(g.y);g=(k.endAngle+k.startAngle)/2;g=(g+2*Math.PI)%(2*Math.PI);k.midAngle=g;if(k.midAngle>Math.PI/2-t&&k.midAngle<Math.PI/2+t){if(0===a||f[c].midAngle>
k.midAngle)c=d;a++}else if(k.midAngle>3*Math.PI/2-t&&k.midAngle<3*Math.PI/2+t){if(0===b||f[e].midAngle>k.midAngle)e=d;b++}k.hemisphere=g>Math.PI/2&&g<=3*Math.PI/2?"left":"right";k.indexLabelTextBlock=new ka(h.plotArea.ctx,{fontSize:k.indexLabelFontSize,fontFamily:k.indexLabelFontFamily,fontColor:k.indexLabelFontColor,fontStyle:k.indexLabelFontStyle,fontWeight:k.indexLabelFontWeight,horizontalAlign:"left",backgroundColor:k.indexLabelBackgroundColor,maxWidth:k.indexLabelMaxWidth,maxHeight:k.indexLabelWrap?
5*k.indexLabelFontSize:1.5*k.indexLabelFontSize,text:k.indexLabelText,padding:0,textBaseline:"top"});k.indexLabelTextBlock.measureText()}l=g=0;q=!1;for(d=0;d<s.length;d++)k=f[(c+d)%s.length],1<a&&(k.midAngle>Math.PI/2-t&&k.midAngle<Math.PI/2+t)&&(g<=a/2&&!q?(k.hemisphere="right",g++):(k.hemisphere="left",q=!0));q=!1;for(d=0;d<s.length;d++)k=f[(e+d)%s.length],1<b&&(k.midAngle>3*Math.PI/2-t&&k.midAngle<3*Math.PI/2+t)&&(l<=b/2&&!q?(k.hemisphere="left",l++):(k.hemisphere="right",q=!0))}}function b(a){var b=
h.plotArea.ctx;b.clearRect(n.x1,n.y1,n.width,n.height);b.fillStyle=h.backgroundColor;b.fillRect(n.x1,n.y1,n.width,n.height);for(b=0;b<s.length;b++){var c=f[b].startAngle,e=f[b].endAngle;if(e>c){var d=0.07*A*Math.cos(f[b].midAngle),g=0.07*A*Math.sin(f[b].midAngle),k=!1;if(s[b].exploded){if(1E-9<Math.abs(f[b].center.x-(x.x+d))||1E-9<Math.abs(f[b].center.y-(x.y+g)))f[b].center.x=x.x+d*a,f[b].center.y=x.y+g*a,k=!0}else if(0<Math.abs(f[b].center.x-x.x)||0<Math.abs(f[b].center.y-x.y))f[b].center.x=x.x+
d*(1-a),f[b].center.y=x.y+g*(1-a),k=!0;k&&(d={},d.dataSeries=m,d.dataPoint=m.dataPoints[b],d.index=b,h.toolTip.highlightObjects([d]));ja(h.plotArea.ctx,f[b].center,f[b].radius,s[b].color?s[b].color:m._colorSet[b%m._colorSet.length],m.type,c,e,m.fillOpacity,f[b].percentInnerRadius)}}a=h.plotArea.ctx;a.save();a.fillStyle="black";a.strokeStyle="grey";a.textBaseline="middle";a.lineJoin="round";for(b=b=0;b<s.length;b++)c=f[b],c.indexLabelText&&(c.indexLabelTextBlock.y-=c.indexLabelTextBlock.height/2,e=
0,e="left"===c.hemisphere?"inside"!==m.indexLabelPlacement?-(c.indexLabelTextBlock.width+q):-c.indexLabelTextBlock.width/2:"inside"!==m.indexLabelPlacement?q:-c.indexLabelTextBlock.width/2,c.indexLabelTextBlock.x+=e,c.indexLabelTextBlock.render(!0),c.indexLabelTextBlock.x-=e,c.indexLabelTextBlock.y+=c.indexLabelTextBlock.height/2,"inside"!==c.indexLabelPlacement&&0<c.indexLabelLineThickness&&(e=c.center.x+A*Math.cos(c.midAngle),d=c.center.y+A*Math.sin(c.midAngle),a.strokeStyle=c.indexLabelLineColor,
a.lineWidth=c.indexLabelLineThickness,a.setLineDash&&a.setLineDash(R(c.indexLabelLineDashType,c.indexLabelLineThickness)),a.beginPath(),a.moveTo(e,d),a.lineTo(c.indexLabelTextBlock.x,c.indexLabelTextBlock.y),a.lineTo(c.indexLabelTextBlock.x+("left"===c.hemisphere?-q:q),c.indexLabelTextBlock.y),a.stroke()),a.lineJoin="miter");a.save()}function c(a,b){var c=0,c=a.indexLabelTextBlock.y-a.indexLabelTextBlock.height/2,e=a.indexLabelTextBlock.y+a.indexLabelTextBlock.height/2,d=b.indexLabelTextBlock.y-b.indexLabelTextBlock.height/
2,f=b.indexLabelTextBlock.y+b.indexLabelTextBlock.height/2;return c=b.indexLabelTextBlock.y>a.indexLabelTextBlock.y?d-e:c-f}function e(a){for(var b=null,e=1;e<s.length;e++)if(b=(a+e+f.length)%f.length,f[b].hemisphere!==f[a].hemisphere){b=null;break}else if(f[b].indexLabelText&&b!==a&&(0>c(f[b],f[a])||("right"===f[a].hemisphere?f[b].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y:f[b].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y)))break;else b=null;return b}function g(a,b,d){d=(d||0)+1;if(1E3<
d)return 0;b=b||0;var k=0,m=x.y-1*r,l=x.y+1*r;if(0<=a&&a<s.length){var n=f[a];if(0>b&&n.indexLabelTextBlock.y<m||0<b&&n.indexLabelTextBlock.y>l)return 0;var h=0,q=0,q=h=h=0;0>b?n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2>m&&n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2+b<m&&(b=-(m-(n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2+b))):n.indexLabelTextBlock.y+n.indexLabelTextBlock.height/2<m&&n.indexLabelTextBlock.y+n.indexLabelTextBlock.height/2+b>l&&(b=n.indexLabelTextBlock.y+
n.indexLabelTextBlock.height/2+b-l);b=n.indexLabelTextBlock.y+b;m=0;m="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(b-x.y,2));q=x.x+A*Math.cos(n.midAngle);h=x.y+A*Math.sin(n.midAngle);h=Math.sqrt(Math.pow(m-q,2)+Math.pow(b-h,2));q=Math.acos(A/r);h=Math.acos((r*r+A*A-h*h)/(2*A*r));b=h<q?b-n.indexLabelTextBlock.y:0;m=null;for(l=1;l<s.length;l++)if(m=(a-l+f.length)%f.length,f[m].hemisphere!==f[a].hemisphere){m=null;break}else if(f[m].indexLabelText&&
f[m].hemisphere===f[a].hemisphere&&m!==a&&(0>c(f[m],f[a])||("right"===f[a].hemisphere?f[m].indexLabelTextBlock.y<=f[a].indexLabelTextBlock.y:f[m].indexLabelTextBlock.y>=f[a].indexLabelTextBlock.y)))break;else m=null;q=m;h=e(a);l=m=0;0>b?(l="right"===n.hemisphere?q:h,k=b,null!==l&&(q=-b,b=n.indexLabelTextBlock.y-n.indexLabelTextBlock.height/2-(f[l].indexLabelTextBlock.y+f[l].indexLabelTextBlock.height/2),b-q<p&&(m=-q,l=g(l,m,d+1),+l.toFixed(C)>+m.toFixed(C)&&(k=b>p?-(b-p):-(q-(l-m)))))):0<b&&(l="right"===
n.hemisphere?h:q,k=b,null!==l&&(q=b,b=f[l].indexLabelTextBlock.y-f[l].indexLabelTextBlock.height/2-(n.indexLabelTextBlock.y+n.indexLabelTextBlock.height/2),b-q<p&&(m=q,l=g(l,m,d+1),+l.toFixed(C)<+m.toFixed(C)&&(k=b>p?b-p:q-(m-l)))));k&&(d=n.indexLabelTextBlock.y+k,b=0,b="right"===n.hemisphere?x.x+Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)):x.x-Math.sqrt(Math.pow(r,2)-Math.pow(d-x.y,2)),n.midAngle>Math.PI/2-t&&n.midAngle<Math.PI/2+t?(m=(a-1+f.length)%f.length,m=f[m],a=f[(a+1+f.length)%f.length],"left"===
n.hemisphere&&"right"===m.hemisphere&&b>m.indexLabelTextBlock.x?b=m.indexLabelTextBlock.x-15:"right"===n.hemisphere&&("left"===a.hemisphere&&b<a.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x+15)):n.midAngle>3*Math.PI/2-t&&n.midAngle<3*Math.PI/2+t&&(m=(a-1+f.length)%f.length,m=f[m],a=f[(a+1+f.length)%f.length],"right"===n.hemisphere&&"left"===m.hemisphere&&b<m.indexLabelTextBlock.x?b=m.indexLabelTextBlock.x+15:"left"===n.hemisphere&&("right"===a.hemisphere&&b>a.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x-
15)),n.indexLabelTextBlock.y=d,n.indexLabelTextBlock.x=b,n.indexLabelAngle=Math.atan2(n.indexLabelTextBlock.y-x.y,n.indexLabelTextBlock.x-x.x))}return k}function k(){var a=h.plotArea.ctx;a.fillStyle="grey";a.strokeStyle="grey";a.font="16px Arial";a.textBaseline="middle";for(var b=a=0,d=0,k=!0,b=0;10>b&&(1>b||0<d);b++){if(m.radius||!m.radius&&"undefined"!==typeof m.innerRadius&&null!==m.innerRadius&&A-d<=D)k=!1;k&&(A-=d);d=0;if("inside"!==m.indexLabelPlacement){r=A*v;for(a=0;a<s.length;a++){var l=
f[a];l.indexLabelTextBlock.x=x.x+r*Math.cos(l.midAngle);l.indexLabelTextBlock.y=x.y+r*Math.sin(l.midAngle);l.indexLabelAngle=l.midAngle;l.radius=A;l.percentInnerRadius=F}for(var t,u,a=0;a<s.length;a++){var l=f[a],y=e(a);if(null!==y){t=f[a];u=f[y];var z=0,z=c(t,u)-p;if(0>z){for(var E=u=0,H=0;H<s.length;H++)H!==a&&f[H].hemisphere===l.hemisphere&&(f[H].indexLabelTextBlock.y<l.indexLabelTextBlock.y?u++:E++);u=z/(u+E||1)*E;var E=-1*(z-u),I=H=0;"right"===l.hemisphere?(H=g(a,u),E=-1*(z-H),I=g(y,E),+I.toFixed(C)<
+E.toFixed(C)&&+H.toFixed(C)<=+u.toFixed(C)&&g(a,-(E-I))):(H=g(y,u),E=-1*(z-H),I=g(a,E),+I.toFixed(C)<+E.toFixed(C)&&+H.toFixed(C)<=+u.toFixed(C)&&g(y,-(E-I)))}}}}else for(a=0;a<s.length;a++)l=f[a],r="pie"===m.type?0.7*A:0.8*A,y=x.x+r*Math.cos(l.midAngle),u=x.y+r*Math.sin(l.midAngle),l.indexLabelTextBlock.x=y,l.indexLabelTextBlock.y=u;for(a=0;a<s.length;a++)if(l=f[a],y=l.indexLabelTextBlock.measureText(),0!==y.height&&0!==y.width)y=y=0,"right"===l.hemisphere?(y=n.x2-(l.indexLabelTextBlock.x+l.indexLabelTextBlock.width+
q),y*=-1):y=n.x1-(l.indexLabelTextBlock.x-l.indexLabelTextBlock.width-q),0<y&&(!k&&l.indexLabelText&&(u="right"===l.hemisphere?n.x2-l.indexLabelTextBlock.x:l.indexLabelTextBlock.x-n.x1,0.3*l.indexLabelTextBlock.maxWidth>u?l.indexLabelText="":l.indexLabelTextBlock.maxWidth=0.85*u,0.3*l.indexLabelTextBlock.maxWidth<u&&(l.indexLabelTextBlock.x-="right"===l.hemisphere?2:-2)),Math.abs(l.indexLabelTextBlock.y-l.indexLabelTextBlock.height/2-x.y)<A||Math.abs(l.indexLabelTextBlock.y+l.indexLabelTextBlock.height/
2-x.y)<A)&&(y/=Math.abs(Math.cos(l.indexLabelAngle)),9<y&&(y*=0.3),y>d&&(d=y)),y=y=0,0<l.indexLabelAngle&&l.indexLabelAngle<Math.PI?(y=n.y2-(l.indexLabelTextBlock.y+l.indexLabelTextBlock.height/2+5),y*=-1):y=n.y1-(l.indexLabelTextBlock.y-l.indexLabelTextBlock.height/2-5),0<y&&(!k&&l.indexLabelText&&(u=0<l.indexLabelAngle&&l.indexLabelAngle<Math.PI?-1:1,0===g(a,y*u)&&g(a,2*u)),Math.abs(l.indexLabelTextBlock.x-x.x)<A&&(y/=Math.abs(Math.sin(l.indexLabelAngle)),9<y&&(y*=0.3),y>d&&(d=y)));var K=function(a,
b,c){for(var e=[],d=0;e.push(f[b]),b!==c;b=(b+1+s.length)%s.length);e.sort(function(a,b){return a.y-b.y});for(b=0;b<e.length;b++)if(c=e[b],d<0.7*a)d+=c.indexLabelTextBlock.height,c.indexLabelTextBlock.text="",c.indexLabelText="",c.indexLabelTextBlock.measureText();else break};(function(){for(var a=-1,b=-1,d=0,g=!1,l=0;l<s.length;l++)if(g=!1,t=f[l],t.indexLabelText){var k=e(l);if(null!==k){var m=f[k];z=0;z=c(t,m);var n;if(n=0>z){n=t.indexLabelTextBlock.x;var h=t.indexLabelTextBlock.y-t.indexLabelTextBlock.height/
2,w=t.indexLabelTextBlock.y+t.indexLabelTextBlock.height/2,p=m.indexLabelTextBlock.y-m.indexLabelTextBlock.height/2,u=m.indexLabelTextBlock.x+m.indexLabelTextBlock.width,r=m.indexLabelTextBlock.y+m.indexLabelTextBlock.height/2;n=t.indexLabelTextBlock.x+t.indexLabelTextBlock.width<m.indexLabelTextBlock.x-q||n>u+q||h>r+q||w<p-q?!1:!0}n?(0>a&&(a=l),k!==a&&(b=k,d+=-z),0===l%Math.max(s.length/10,3)&&(g=!0)):g=!0;g&&(0<d&&0<=a&&0<=b)&&(K(d,a,b),b=a=-1,d=0)}}0<d&&K(d,a,b)})()}}function l(){h.plotArea.layoutManager.reset();
h.title&&(h.title.dockInsidePlotArea||"center"===h.title.horizontalAlign&&"center"===h.title.verticalAlign)&&h.title.render();if(h.subtitles)for(var a=0;a<h.subtitles.length;a++){var b=h.subtitles[a];(b.dockInsidePlotArea||"center"===b.horizontalAlign&&"center"===b.verticalAlign)&&b.render()}h.legend&&(h.legend.dockInsidePlotArea||"center"===h.legend.horizontalAlign&&"center"===h.legend.verticalAlign)&&(h.legend.setLayout(),h.legend.render())}var h=this;if(!(0>=a.dataSeriesIndexes.length)){var m=
this.data[a.dataSeriesIndexes[0]],s=m.dataPoints,q=10,n=this.plotArea,f=m.dataPointEOs,p=2,r,v=1.3,t=20/180*Math.PI,C=6,x={x:(n.x2+n.x1)/2,y:(n.y2+n.y1)/2},z=0;a=!1;for(var y=0;y<s.length;y++)z+=Math.abs(s[y].y),!a&&("undefined"!==typeof s[y].indexLabel&&null!==s[y].indexLabel&&0<s[y].indexLabel.toString().length)&&(a=!0),!a&&("undefined"!==typeof s[y].label&&null!==s[y].label&&0<s[y].label.toString().length)&&(a=!0);if(0!==z){a=a||"undefined"!==typeof m.indexLabel&&null!==m.indexLabel&&0<m.indexLabel.toString().length;
var A="inside"!==m.indexLabelPlacement&&a?0.75*Math.min(n.width,n.height)/2:0.92*Math.min(n.width,n.height)/2;m.radius&&(A=I(m.radius,A));var D="undefined"!==typeof m.innerRadius&&null!==m.innerRadius?I(m.innerRadius,A):0.7*A;m.radius=A;"doughnut"===m.type&&(m.innerRadius=D);var F=Math.min(D/A,(A-1)/A);this.pieDoughnutClickHandler=function(a){h.isAnimating||!u(a.dataSeries.explodeOnClick)&&!a.dataSeries.explodeOnClick||(a=a.dataPoint,a.exploded=a.exploded?!1:!0,1<this.dataPoints.length&&h._animator.animate(0,
500,function(a){b(a);l()}))};d();k();k();k();k();this.disableToolTip=!0;this._animator.animate(0,this.animatedRender?this.animationDuration:0,function(a){var b=h.plotArea.ctx;b.clearRect(n.x1,n.y1,n.width,n.height);b.fillStyle=h.backgroundColor;b.fillRect(n.x1,n.y1,n.width,n.height);a=f[0].startAngle+2*Math.PI*a;for(b=0;b<s.length;b++){var c=0===b?f[b].startAngle:e,e=c+(f[b].endAngle-f[b].startAngle),d=!1;e>a&&(e=a,d=!0);var g=s[b].color?s[b].color:m._colorSet[b%m._colorSet.length];e>c&&ja(h.plotArea.ctx,
f[b].center,f[b].radius,g,m.type,c,e,m.fillOpacity,f[b].percentInnerRadius);if(d)break}l()},function(){h.disableToolTip=!1;h._animator.animate(0,h.animatedRender?500:0,function(a){b(a);l()})})}}};var ra=function(a,d,b,c){"undefined"===typeof b&&(b=1);0>=Math.round(d.y4-d.y1)||(a.save(),a.globalAlpha=b,a.beginPath(),a.moveTo(Math.round(d.x1),Math.round(d.y1)),a.lineTo(Math.round(d.x2),Math.round(d.y2)),a.lineTo(Math.round(d.x3),Math.round(d.y3)),a.lineTo(Math.round(d.x4),Math.round(d.y4)),"undefined"!==
d.x5&&(a.lineTo(Math.round(d.x5),Math.round(d.y5)),a.lineTo(Math.round(d.x6),Math.round(d.y6))),a.closePath(),a.fillStyle=c?c:d.color,a.fill(),a.globalAplha=1,a.restore())};p.prototype.renderFunnel=function(a){function d(){for(var a=0,b=[],c=0;c<C.length;c++){if("undefined"===typeof C[c].y)return-1;C[c].y="number"===typeof C[c].y?C[c].y:0;a+=Math.abs(C[c].y)}if(0===a)return-1;for(c=b[0]=0;c<C.length;c++)b.push(Math.abs(C[c].y)*F/a);return b}function b(){var a=$,b=V,c=K,e=ea,d,f;d=O;f=Z-N;e=Math.abs((f-
d)*(b-a+(e-c))/2);c=ea-K;d=f-d;f=c*(f-Z);f=Math.abs(f);f=e+f;for(var e=[],g=0,l=0;l<C.length;l++){if("undefined"===typeof C[l].y)return-1;C[l].y="number"===typeof C[l].y?C[l].y:0;g+=Math.abs(C[l].y)}if(0===g)return-1;for(var m=e[0]=0,k=0,n,h,b=b-a,m=!1,l=0;l<C.length;l++)a=Math.abs(C[l].y)*f/g,m?n=0==Number(c.toFixed(3))?0:a/c:(h=ba*ba*b*b-4*Math.abs(ba)*a,0>h?(h=c,m=(b+h)*(d-k)/2,a-=m,n=d-k,k+=d-k,n+=0==h?0:a/h,k+=a/h,m=!0):(n=(Math.abs(ba)*b-Math.sqrt(h))/2,h=b-2*n/Math.abs(ba),k+=n,k>d&&(k-=n,
h=c,m=(b+h)*(d-k)/2,a-=m,n=d-k,k+=d-k,n+=a/h,k+=a/h,m=!0),b=h)),e.push(n);return e}function c(){if(t&&C){for(var a,b,c,e,d,g,l,k,m,n,h,q,s,w,p=[],B=[],x={percent:null,total:null},v=null,y=0;y<C.length;y++)w=P[y],w="undefined"!==typeof w.x5?(w.y2+w.y4)/2:(w.y2+w.y3)/2,w=f(w).x2+1,p[y]=L-w-S;w=0.5*S;for(var y=0,A=C.length-1;y<C.length||0<=A;y++,A--){b=t.reversed?C[A]:C[y];a=b.color?b.color:t.reversed?t._colorSet[(C.length-1-y)%t._colorSet.length]:t._colorSet[y%t._colorSet.length];c=b.indexLabelPlacement||
t.indexLabelPlacement||"outside";e=b.indexLabelBackgroundColor||t.indexLabelBackgroundColor||(r?"transparent":null);d=b.indexLabelFontColor||t.indexLabelFontColor||"#979797";g=u(b.indexLabelFontSize)?t.indexLabelFontSize:b.indexLabelFontSize;l=b.indexLabelFontStyle||t.indexLabelFontStyle||"normal";k=b.indexLabelFontFamily||t.indexLabelFontFamily||"arial";m=b.indexLabelFontWeight||t.indexLabelFontWeight||"normal";a=b.indexLabelLineColor||t.options.indexLabelLineColor||a;n="number"===typeof b.indexLabelLineThickness?
b.indexLabelLineThickness:"number"===typeof t.indexLabelLineThickness?t.indexLabelLineThickness:2;h=b.indexLabelLineDashType||t.indexLabelLineDashType||"solid";q="undefined"!==typeof b.indexLabelWrap?b.indexLabelWrap:"undefined"!==typeof t.indexLabelWrap?t.indexLabelWrap:!0;s=t.dataPointIds[y];z._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataPointIndex:y,dataSeriesIndex:0,funnelSection:P[t.reversed?C.length-1-y:y]};"inside"===t.indexLabelPlacement&&(p[y]=y!==fa?t.reversed?P[y].x2-P[y].x1:
P[y].x3-P[y].x4:P[y].x3-P[y].x6,20>p[y]&&(p[y]=y!==fa?t.reversed?P[y].x3-P[y].x4:P[y].x2-P[y].x1:P[y].x2-P[y].x1,p[y]/=2));s=b.indexLabelMaxWidth?b.indexLabelMaxWidth:t.options.indexLabelMaxWidth?t.indexLabelMaxWidth:p[y];if(s>p[y]||0>s)s=p[y];B[y]="inside"===t.indexLabelPlacement?P[y].height:!1;x=z.getPercentAndTotal(t,b);if(t.indexLabelFormatter||b.indexLabelFormatter)v={chart:z.options,dataSeries:t,dataPoint:b,total:x.total,percent:x.percent};b=b.indexLabelFormatter?b.indexLabelFormatter(v):b.indexLabel?
z.replaceKeywordsWithValue(b.indexLabel,b,t,y):t.indexLabelFormatter?t.indexLabelFormatter(v):t.indexLabel?z.replaceKeywordsWithValue(t.indexLabel,b,t,y):b.label?b.label:"";0>=n&&(n=0);1E3>s&&1E3-s<w&&(s+=1E3-s);Q.roundRect||Ea(Q);c=new ka(Q,{fontSize:g,fontFamily:k,fontColor:d,fontStyle:l,fontWeight:m,horizontalAlign:c,backgroundColor:e,maxWidth:s,maxHeight:!1===B[y]?q?4.28571429*g:1.5*g:B[y],text:b,padding:ga});c.measureText();J.push({textBlock:c,id:t.reversed?A:y,isDirty:!1,lineColor:a,lineThickness:n,
lineDashType:h,height:c.height<c.maxHeight?c.height:c.maxHeight,width:c.width<c.maxWidth?c.width:c.maxWidth})}}}function e(){var a,b,c,e,d,f=[];d=!1;c=0;for(var g,l=L-V-S/2,l=t.options.indexLabelMaxWidth?t.indexLabelMaxWidth>l?l:t.indexLabelMaxWidth:l,k=J.length-1;0<=k;k--){g=C[J[k].id];c=J[k];e=c.textBlock;b=(a=n(k)<P.length?J[n(k)]:null)?a.textBlock:null;c=c.height;a&&e.y+c+ga>b.y&&(d=!0);c=g.indexLabelMaxWidth||l;if(c>l||0>c)c=l;f.push(c)}if(d)for(k=J.length-1;0<=k;k--)a=P[k],J[k].textBlock.maxWidth=
f[f.length-(k+1)],J[k].textBlock.measureText(),J[k].textBlock.x=L-l,c=J[k].textBlock.height<J[k].textBlock.maxHeight?J[k].textBlock.height:J[k].textBlock.maxHeight,d=J[k].textBlock.width<J[k].textBlock.maxWidth?J[k].textBlock.width:J[k].textBlock.maxWidth,J[k].height=c,J[k].width=d,c="undefined"!==typeof a.x5?(a.y2+a.y4)/2:(a.y2+a.y3)/2,J[k].textBlock.y=c-J[k].height/2,t.reversed?(J[k].textBlock.y+J[k].height>pa+D&&(J[k].textBlock.y=pa+D-J[k].height),J[k].textBlock.y<wa-D&&(J[k].textBlock.y=wa-D)):
(J[k].textBlock.y<pa-D&&(J[k].textBlock.y=pa-D),J[k].textBlock.y+J[k].height>wa+D&&(J[k].textBlock.y=wa+D-J[k].height))}function g(){var a,b,c,e;if("inside"!==t.indexLabelPlacement)for(var d=0;d<P.length;d++)0==J[d].textBlock.text.length?J[d].isDirty=!0:(a=P[d],c="undefined"!==typeof a.x5?(a.y2+a.y4)/2:(a.y2+a.y3)/2,b=t.reversed?"undefined"!==typeof a.x5?c>Da?f(c).x2+1:(a.x2+a.x3)/2+1:(a.x2+a.x3)/2+1:"undefined"!==typeof a.x5?c<Da?f(c).x2+1:(a.x4+a.x3)/2+1:(a.x2+a.x3)/2+1,J[d].textBlock.x=b+S,J[d].textBlock.y=
c-J[d].height/2,t.reversed?(J[d].textBlock.y+J[d].height>pa+D&&(J[d].textBlock.y=pa+D-J[d].height),J[d].textBlock.y<wa-D&&(J[d].textBlock.y=wa-D)):(J[d].textBlock.y<pa-D&&(J[d].textBlock.y=pa-D),J[d].textBlock.y+J[d].height>wa+D&&(J[d].textBlock.y=wa+D-J[d].height)));else for(d=0;d<P.length;d++)0==J[d].textBlock.text.length?J[d].isDirty=!0:(a=P[d],b=a.height,c=J[d].height,e=J[d].width,b>=c?(b=d!=fa?(a.x4+a.x3)/2-e/2:(a.x5+a.x4)/2-e/2,c=d!=fa?(a.y1+a.y3)/2-c/2:(a.y1+a.y4)/2-c/2,J[d].textBlock.x=b,
J[d].textBlock.y=c):J[d].isDirty=!0)}function k(){function a(b,c){var d;if(0>b||b>=J.length)return 0;var e,f=J[b].textBlock;if(0>c){c*=-1;e=q(b);d=l(e,b);if(d>=c)return f.y-=c,c;if(0==b)return 0<d&&(f.y-=d),d;d+=a(e,-(c-d));0<d&&(f.y-=d);return d}e=n(b);d=l(b,e);if(d>=c)return f.y+=c,c;if(b==P.length-1)return 0<d&&(f.y+=d),d;d+=a(e,c-d);0<d&&(f.y+=d);return d}function b(){var a,d,e,f,g=0,k;f=(Z-O+2*D)/h;k=h;for(var l,m=1;m<k;m++){e=m*f;for(var s=J.length-1;0<=s;s--)!J[s].isDirty&&(J[s].textBlock.y<
e&&J[s].textBlock.y+J[s].height>e)&&(l=n(s),!(l>=J.length-1)&&J[s].textBlock.y+J[s].height+ga>J[l].textBlock.y&&(J[s].textBlock.y=J[s].textBlock.y+J[s].height-e>e-J[s].textBlock.y?e+1:e-J[s].height-1))}for(l=P.length-1;0<l;l--)if(!J[l].isDirty){e=q(l);if(0>e&&(e=0,J[e].isDirty))break;if(J[l].textBlock.y<J[e].textBlock.y+J[e].height){d=d||l;f=l;for(k=0;J[f].textBlock.y<J[e].textBlock.y+J[e].height+ga;){a=a||J[f].textBlock.y+J[f].height;k+=J[f].height;k+=ga;f=e;if(0>=f){f=0;k+=J[f].height;break}e=q(f);
if(0>e){f=0;k+=J[f].height;break}}if(f!=l){g=J[f].textBlock.y;a-=g;a=k-a;g=c(a,d,f);break}}}return g}function c(a,b,d){var e=[],f=0,g=0;for(a=Math.abs(a);d<=b;d++)e.push(P[d]);e.sort(function(a,b){return a.height-b.height});for(d=0;d<e.length;d++)if(b=e[d],f<a)g++,f+=J[b.id].height+ga,J[b.id].textBlock.text="",J[b.id].indexLabelText="",J[b.id].isDirty=!0,J[b.id].textBlock.measureText();else break;return g}for(var d,e,f,g,k,m,h=1,s=0;s<2*h;s++){for(var w=J.length-1;0<=w&&!(0<=q(w)&&q(w),f=J[w],g=f.textBlock,
m=(k=n(w)<P.length?J[n(w)]:null)?k.textBlock:null,d=+f.height.toFixed(6),e=+g.y.toFixed(6),!f.isDirty&&(k&&e+d+ga>+m.y.toFixed(6))&&(d=g.y+d+ga-m.y,e=a(w,-d),e<d&&(0<e&&(d-=e),e=a(n(w),d),e!=d)));w--);b()}}function l(a,b){return(b<P.length?J[b].textBlock.y:t.reversed?pa+D:wa+D)-(0>a?t.reversed?wa-D:pa-D:J[a].textBlock.y+J[a].height+ga)}function h(a,b,c){var d,e,f,k=[],l=D,n=[];-1!==b&&(0<=W.indexOf(b)?(e=W.indexOf(b),W.splice(e,1)):(W.push(b),W=W.sort(function(a,b){return a-b})));if(0===W.length)k=
ia;else{e=D*(1!=W.length||0!=W[0]&&W[0]!=P.length-1?2:1)/m();for(var q=0;q<P.length;q++){if(1==W.length&&0==W[0]){if(0===q){k.push(ia[q]);d=l;continue}}else 0===q&&(d=-1*l);k.push(ia[q]+d);if(0<=W.indexOf(q)||q<P.length&&0<=W.indexOf(q+1))d+=e}}f=function(){for(var a=[],b=0;b<P.length;b++)a.push(k[b]-P[b].y1);return a}();var w={startTime:(new Date).getTime(),duration:c||500,easingFunction:function(a,b,c,d){return M.easing.easeOutQuart(a,b,c,d)},changeSection:function(a){for(var b,c,d=0;d<P.length;d++)b=
f[d],c=P[d],b*=a,"undefined"===typeof n[d]&&(n[d]=0),0>n&&(n*=-1),c.y1+=b-n[d],c.y2+=b-n[d],c.y3+=b-n[d],c.y4+=b-n[d],c.y5&&(c.y5+=b-n[d],c.y6+=b-n[d]),n[d]=b}};a._animator.animate(0,c,function(c){var d=a.plotArea.ctx||a.ctx;ja=!0;d.clearRect(x.x1,x.y1,x.x2-x.x1,x.y2-x.y1);d.fillStyle=a.backgroundColor;d.fillRect(x.x1,x.y1,x.width,x.height);w.changeSection(c,b);var e={};e.dataSeries=t;e.dataPoint=t.reversed?t.dataPoints[C.length-1-b]:t.dataPoints[b];e.index=t.reversed?C.length-1-b:b;a.toolTip.highlightObjects([e]);
for(e=0;e<P.length;e++)ra(d,P[e],t.fillOpacity);v(d);H&&("inside"!==t.indexLabelPlacement?s(d):g(),p(d));1<=c&&(ja=!1)},null,M.easing.easeOutQuart)}function m(){for(var a=0,b=0;b<P.length-1;b++)(0<=W.indexOf(b)||0<=W.indexOf(b+1))&&a++;return a}function s(a){for(var b,c,d,e,g=0;g<P.length;g++)e=1===J[g].lineThickness%2?0.5:0,c=((P[g].y2+P[g].y4)/2<<0)+e,b=f(c).x2-1,d=J[g].textBlock.x,e=(J[g].textBlock.y+J[g].height/2<<0)+e,J[g].isDirty||0==J[g].lineThickness||(a.strokeStyle=J[g].lineColor,a.lineWidth=
J[g].lineThickness,a.setLineDash&&a.setLineDash(R(J[g].lineDashType,J[g].lineThickness)),a.beginPath(),a.moveTo(b,c),a.lineTo(d,e),a.stroke())}function q(a){for(a-=1;-1<=a&&-1!=a&&J[a].isDirty;a--);return a}function n(a){for(a+=1;a<=P.length&&a!=P.length&&J[a].isDirty;a++);return a}function f(a){for(var b,c=0;c<C.length;c++)if(P[c].y1<a&&P[c].y4>a){b=P[c];break}return b?(a=b.y6?a>b.y6?b.x3+(b.x4-b.x3)/(b.y4-b.y3)*(a-b.y3):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2):b.x2+(b.x3-b.x2)/(b.y3-b.y2)*(a-b.y2),
{x1:a,x2:a}):-1}function p(a){for(var b=0;b<P.length;b++)J[b].isDirty||(a&&(J[b].textBlock.ctx=a),J[b].textBlock.render(!0))}function v(a){z.plotArea.layoutManager.reset();a.roundRect||Ea(a);z.title&&(z.title.dockInsidePlotArea||"center"===z.title.horizontalAlign&&"center"===z.title.verticalAlign)&&(z.title.ctx=a,z.title.render());if(z.subtitles)for(var b=0;b<z.subtitles.length;b++){var c=z.subtitles[b];if(c.dockInsidePlotArea||"center"===c.horizontalAlign&&"center"===c.verticalAlign)z.subtitles.ctx=
a,c.render()}z.legend&&(z.legend.dockInsidePlotArea||"center"===z.legend.horizontalAlign&&"center"===z.legend.verticalAlign)&&(z.legend.ctx=a,z.legend.setLayout(),z.legend.render());U.fNg&&U.fNg(z)}var z=this;if(!(0>=a.dataSeriesIndexes.length)){for(var t=this.data[a.dataSeriesIndexes[0]],C=t.dataPoints,x=this.plotArea,D=0.025*x.width,y=0.01*x.width,A=0,F=x.height-2*D,E=Math.min(x.width-2*y,2.8*x.height),H=!1,I=0;I<C.length;I++)if(!H&&("undefined"!==typeof C[I].indexLabel&&null!==C[I].indexLabel&&
0<C[I].indexLabel.toString().length)&&(H=!0),!H&&("undefined"!==typeof C[I].label&&null!==C[I].label&&0<C[I].label.toString().length)&&(H=!0),!H&&"function"===typeof t.indexLabelFormatter||"function"===typeof C[I].indexLabelFormatter)H=!0;H=H||"undefined"!==typeof t.indexLabel&&null!==t.indexLabel&&0<t.indexLabel.toString().length;"inside"!==t.indexLabelPlacement&&H||(y=(x.width-0.75*E)/2);var I=x.x1+y,L=x.x2-y,O=x.y1+D,Z=x.y2-D,Q=a.targetCanvasCtx||this.plotArea.ctx||this.ctx;if(0!=t.length&&(t.dataPoints&&
t.visible)&&0!==C.length){var N,G;a=75*E/100;var S=30*(L-a)/100;"funnel"===t.type?(N=u(t.options.neckHeight)?0.35*F:t.neckHeight,G=u(t.options.neckWidth)?0.25*a:t.neckWidth,"string"===typeof N&&N.match(/%$/)?(N=parseInt(N),N=N*F/100):N=parseInt(N),"string"===typeof G&&G.match(/%$/)?(G=parseInt(G),G=G*a/100):G=parseInt(G),N>F?N=F:0>=N&&(N=0),G>a?G=a-0.5:0>=G&&(G=0)):"pyramid"===t.type&&(G=N=0,t.reversed=t.reversed?!1:!0);var y=I+a/2,$=I,V=I+a,pa=t.reversed?Z:O,K=y-G/2,ea=y+G/2,Da=t.reversed?O+N:Z-
N,wa=t.reversed?O:Z;a=[];var y=[],P=[],E=[],X=O,fa,ba=(Da-pa)/(K-$),ha=-ba,I="area"===(t.valueRepresents?t.valueRepresents:"height")?b():d();if(-1!==I){if(t.reversed)for(E.push(X),G=I.length-1;0<G;G--)X+=I[G],E.push(X);else for(G=0;G<I.length;G++)X+=I[G],E.push(X);if(t.reversed)for(G=0;G<I.length;G++)E[G]<Da?(a.push(K),y.push(ea),fa=G):(a.push((E[G]-pa+ba*$)/ba),y.push((E[G]-pa+ha*V)/ha));else for(G=0;G<I.length;G++)E[G]<Da?(a.push((E[G]-pa+ba*$)/ba),y.push((E[G]-pa+ha*V)/ha),fa=G):(a.push(K),y.push(ea));
for(G=0;G<I.length-1;G++)X=t.reversed?C[C.length-1-G].color?C[C.length-1-G].color:t._colorSet[(C.length-1-G)%t._colorSet.length]:C[G].color?C[G].color:t._colorSet[G%t._colorSet.length],G===fa?P.push({x1:a[G],y1:E[G],x2:y[G],y2:E[G],x3:ea,y3:Da,x4:y[G+1],y4:E[G+1],x5:a[G+1],y5:E[G+1],x6:K,y6:Da,id:G,height:E[G+1]-E[G],color:X}):P.push({x1:a[G],y1:E[G],x2:y[G],y2:E[G],x3:y[G+1],y3:E[G+1],x4:a[G+1],y4:E[G+1],id:G,height:E[G+1]-E[G],color:X});var ga=2,J=[],ja=!1,W=[],ia=[],I=!1;a=a=0;Fa(W);for(G=0;G<
C.length;G++)C[G].exploded&&(I=!0,t.reversed?W.push(C.length-1-G):W.push(G));Q.clearRect(x.x1,x.y1,x.width,x.height);Q.fillStyle=z.backgroundColor;Q.fillRect(x.x1,x.y1,x.width,x.height);if(H&&t.visible&&(c(),g(),e(),"inside"!==t.indexLabelPlacement)){k();for(G=0;G<C.length;G++)J[G].isDirty||(a=J[G].textBlock.x+J[G].width,a=(L-a)/2,0==G&&(A=a),A>a&&(A=a));for(G=0;G<P.length;G++)P[G].x1+=A,P[G].x2+=A,P[G].x3+=A,P[G].x4+=A,P[G].x5&&(P[G].x5+=A,P[G].x6+=A),J[G].textBlock.x+=A}for(G=0;G<P.length;G++)A=
P[G],ra(Q,A,t.fillOpacity),ia.push(A.y1);v(Q);H&&t.visible&&("inside"===t.indexLabelPlacement||z.animationEnabled||s(Q),z.animationEnabled||p());if(!H)for(G=0;G<C.length;G++)A=t.dataPointIds[G],a={id:A,objectType:"dataPoint",dataPointIndex:G,dataSeriesIndex:0,funnelSection:P[t.reversed?C.length-1-G:G]},z._eventManager.objectMap[A]=a;!z.animationEnabled&&I?h(z,-1,0):z.animationEnabled&&!z.animatedRender&&h(z,-1,0);this.funnelPyramidClickHandler=function(a){var b=-1;if(!ja&&!z.isAnimating&&(u(a.dataSeries.explodeOnClick)||
a.dataSeries.explodeOnClick)&&(b=t.reversed?C.length-1-a.dataPointIndex:a.dataPointIndex,0<=b)){a=b;if("funnel"===t.type||"pyramid"===t.type)t.reversed?C[C.length-1-a].exploded=C[C.length-1-a].exploded?!1:!0:C[a].exploded=C[a].exploded?!1:!0;h(z,b,500)}};return{source:Q,dest:this.plotArea.ctx,animationCallback:function(a,b){M.fadeInAnimation(a,b);1<=a&&(h(z,-1,500),v(z.plotArea.ctx||z.ctx))},easingFunction:M.easing.easeInQuad,animationBase:0}}}}};p.prototype.requestAnimFrame=function(){return window.requestAnimationFrame||
window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}();p.prototype.cancelRequestAnimFrame=window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout;p.prototype.set=function(a,d,b){b="undefined"===typeof b?!0:b;"options"===a?(this.options=d,
b&&this.render()):p.base.set.call(this,a,d,b)};p.prototype.exportChart=function(a){a="undefined"===typeof a?{}:a;var d=a.format?a.format:"png",b=a.fileName?a.fileName:this.exportFileName;if(a.toDataURL)return this.canvas.toDataURL("image/"+d);Ta(this.canvas,d,b)};p.prototype.print=function(){var a=this.exportChart({toDataURL:!0}),d=document.createElement("iframe");d.setAttribute("class","canvasjs-chart-print-frame");d.setAttribute("style","position:absolute; width:100%; border: 0px; margin: 0px 0px 0px 0px; padding 0px 0px 0px 0px;");
d.style.height=this.height+"px";this._canvasJSContainer.appendChild(d);var b=this,c=d.contentWindow||d.contentDocument.document||d.contentDocument;c.document.open();c.document.write('<!DOCTYPE HTML>\n<html><body style="margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px;"><img src="'+a+'"/><body/></html>');c.document.close();setTimeout(function(){c.focus();c.print();setTimeout(function(){b._canvasJSContainer.removeChild(d)},1E3)},500)};p.prototype.getPercentAndTotal=function(a,d){var b=null,c=null,
e=null;if(0<=a.type.indexOf("stacked"))c=0,b=d.x.getTime?d.x.getTime():d.x,b in a.plotUnit.yTotals&&(c=a.plotUnit.yTotals[b],e=isNaN(d.y)?0:100*(d.y/c));else if("pie"===a.type||"doughnut"===a.type||"funnel"===a.type||"pyramid"===a.type){for(b=c=0;b<a.dataPoints.length;b++)isNaN(a.dataPoints[b].y)||(c+=a.dataPoints[b].y);e=isNaN(d.y)?0:100*(d.y/c)}return{percent:e,total:c}};p.prototype.replaceKeywordsWithValue=function(a,d,b,c,e){var g=this;e="undefined"===typeof e?0:e;if((0<=b.type.indexOf("stacked")||
"pie"===b.type||"doughnut"===b.type||"funnel"===b.type||"pyramid"===b.type)&&(0<=a.indexOf("#percent")||0<=a.indexOf("#total"))){var k="#percent",l="#total",h=this.getPercentAndTotal(b,d),l=isNaN(h.total)?l:h.total,k=isNaN(h.percent)?k:h.percent;do{h="";if(b.percentFormatString)h=b.percentFormatString;else{var h="#,##0.",m=Math.max(Math.ceil(Math.log(1/Math.abs(k))/Math.LN10),2);if(isNaN(m)||!isFinite(m))m=2;for(var s=0;s<m;s++)h+="#";b.percentFormatString=h}a=a.replace("#percent",ba(k,h,g._cultureInfo));
a=a.replace("#total",ba(l,b.yValueFormatString?b.yValueFormatString:"#,##0.########",g._cultureInfo))}while(0<=a.indexOf("#percent")||0<=a.indexOf("#total"))}return a.replace(/\{.*?\}|"[^"]*"|'[^']*'/g,function(a){if('"'===a[0]&&'"'===a[a.length-1]||"'"===a[0]&&"'"===a[a.length-1])return a.slice(1,a.length-1);a=Ia(a.slice(1,a.length-1));a=a.replace("#index",e);var k=null;try{var f=a.match(/(.*?)\s*\[\s*(.*?)\s*\]/);f&&0<f.length&&(k=Ia(f[2]),a=Ia(f[1]))}catch(l){}f=null;if("color"===a)return"waterfall"===
b.type?d.color?d.color:0<d.y?b.risingColor:b.fallingColor:"error"===b.type?b.color?b.color:b._colorSet[k%b._colorSet.length]:d.color?d.color:b.color?b.color:b._colorSet[c%b._colorSet.length];if(d.hasOwnProperty(a))f=d;else if(b.hasOwnProperty(a))f=b;else return"";f=f[a];null!==k&&(f=f[k]);if("x"===a)if("dateTime"===b.axisX.valueType||"dateTime"===b.xValueType||d.x&&d.x.getTime){if(g.plotInfo.plotTypes[0].plotUnits[0].axisX&&!g.plotInfo.plotTypes[0].plotUnits[0].axisX.logarithmic)return Ca(f,d.xValueFormatString?
d.xValueFormatString:b.xValueFormatString?b.xValueFormatString:b.xValueFormatString=g.axisX&&g.axisX.autoValueFormatString?g.axisX.autoValueFormatString:"DD MMM YY",g._cultureInfo)}else return ba(f,d.xValueFormatString?d.xValueFormatString:b.xValueFormatString?b.xValueFormatString:b.xValueFormatString="#,##0.########",g._cultureInfo);else return"y"===a?ba(f,d.yValueFormatString?d.yValueFormatString:b.yValueFormatString?b.yValueFormatString:b.yValueFormatString="#,##0.########",g._cultureInfo):"z"===
a?ba(f,d.zValueFormatString?d.zValueFormatString:b.zValueFormatString?b.zValueFormatString:b.zValueFormatString="#,##0.########",g._cultureInfo):f})};qa(H,V);H.prototype.setLayout=function(){var a=this.dockInsidePlotArea?this.chart.plotArea:this.chart,d=a.layoutManager.getFreeSpace(),b=null,c=0,e=0,g=0,k=0,l=this.markerMargin=this.chart.options.legend&&!u(this.chart.options.legend.markerMargin)?this.chart.options.legend.markerMargin:0.3*this.fontSize;this.height=0;var h=[],m=[];"top"===this.verticalAlign||
"bottom"===this.verticalAlign?(this.orientation="horizontal",b=this.verticalAlign,g=this.maxWidth=null!==this.maxWidth?this.maxWidth:d.width,k=this.maxHeight=null!==this.maxHeight?this.maxHeight:0.5*d.height):"center"===this.verticalAlign&&(this.orientation="vertical",b=this.horizontalAlign,g=this.maxWidth=null!==this.maxWidth?this.maxWidth:0.5*d.width,k=this.maxHeight=null!==this.maxHeight?this.maxHeight:d.height);this.errorMarkerColor=[];for(var s=0;s<this.dataSeries.length;s++){var q=this.dataSeries[s];
if(q.dataPoints&&q.dataPoints.length)if("pie"!==q.type&&"doughnut"!==q.type&&"funnel"!==q.type&&"pyramid"!==q.type){var n=q.legendMarkerType=q.legendMarkerType?q.legendMarkerType:"line"!==q.type&&"stepLine"!==q.type&&"spline"!==q.type&&"scatter"!==q.type&&"bubble"!==q.type||!q.markerType?"error"===q.type&&q._linkedSeries?q._linkedSeries.legendMarkerType?q._linkedSeries.legendMarkerType:F.getDefaultLegendMarker(q._linkedSeries.type):F.getDefaultLegendMarker(q.type):q.markerType,f=q.legendText?q.legendText:
this.itemTextFormatter?this.itemTextFormatter({chart:this.chart,legend:this.options,dataSeries:q,dataPoint:null}):q.name,p=q.legendMarkerColor=q.legendMarkerColor?q.legendMarkerColor:q.markerColor?q.markerColor:"error"===q.type?u(q.whiskerColor)?q._colorSet[0]:q.whiskerColor:q._colorSet[0],r=q.markerSize||"line"!==q.type&&"stepLine"!==q.type&&"spline"!==q.type?0.75*this.lineHeight:0,v=q.legendMarkerBorderColor?q.legendMarkerBorderColor:q.markerBorderColor,t=q.legendMarkerBorderThickness?q.legendMarkerBorderThickness:
q.markerBorderThickness?Math.max(1,Math.round(0.2*r)):0;"error"===q.type&&this.errorMarkerColor.push(p);f=this.chart.replaceKeywordsWithValue(f,q.dataPoints[0],q,s);n={markerType:n,markerColor:p,text:f,textBlock:null,chartType:q.type,markerSize:r,lineColor:q._colorSet[0],dataSeriesIndex:q.index,dataPointIndex:null,markerBorderColor:v,markerBorderThickness:t};h.push(n)}else for(var z=0;z<q.dataPoints.length;z++){var x=q.dataPoints[z],n=x.legendMarkerType?x.legendMarkerType:q.legendMarkerType?q.legendMarkerType:
F.getDefaultLegendMarker(q.type),f=x.legendText?x.legendText:q.legendText?q.legendText:this.itemTextFormatter?this.itemTextFormatter({chart:this.chart,legend:this.options,dataSeries:q,dataPoint:x}):x.name?x.name:"DataPoint: "+(z+1),p=x.legendMarkerColor?x.legendMarkerColor:q.legendMarkerColor?q.legendMarkerColor:x.color?x.color:q.color?q.color:q._colorSet[z%q._colorSet.length],r=0.75*this.lineHeight,v=x.legendMarkerBorderColor?x.legendMarkerBorderColor:q.legendMarkerBorderColor?q.legendMarkerBorderColor:
x.markerBorderColor?x.markerBorderColor:q.markerBorderColor,t=x.legendMarkerBorderThickness?x.legendMarkerBorderThickness:q.legendMarkerBorderThickness?q.legendMarkerBorderThickness:x.markerBorderThickness||q.markerBorderThickness?Math.max(1,Math.round(0.2*r)):0,f=this.chart.replaceKeywordsWithValue(f,x,q,z),n={markerType:n,markerColor:p,text:f,textBlock:null,chartType:q.type,markerSize:r,dataSeriesIndex:s,dataPointIndex:z,markerBorderColor:v,markerBorderThickness:t};(x.showInLegend||q.showInLegend&&
!1!==x.showInLegend)&&h.push(n)}}!0===this.reversed&&h.reverse();if(0<h.length){q=null;p=f=x=z=0;x=null!==this.itemWidth?null!==this.itemMaxWidth?Math.min(this.itemWidth,this.itemMaxWidth,g):this.itemMaxWidth=Math.min(this.itemWidth,g):null!==this.itemMaxWidth?Math.min(this.itemMaxWidth,g):this.itemMaxWidth=g;r=0===r?0.75*this.lineHeight:r;x-=r+l;for(s=0;s<h.length;s++){n=h[s];v=x;if("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType)v-=2*0.1*this.lineHeight;if(!(0>=k||"undefined"===
typeof k||0>=v||"undefined"===typeof v)){if("horizontal"===this.orientation){n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:v,maxHeight:this.itemWrap?k:this.lineHeight,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"});n.textBlock.measureText();null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"===
n.chartType?2*0.1*this.lineHeight:0)));if(!q||q.width+Math.round(n.textBlock.width+r+l+(0===q.width?0:this.horizontalSpacing)+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))>g)q={items:[],width:0},m.push(q),this.height+=f,f=0;f=Math.max(f,n.textBlock.height)}else n.textBlock=new ka(this.ctx,{x:0,y:0,maxWidth:x,maxHeight:!0===this.itemWrap?k:1.5*this.fontSize,angle:0,text:n.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,
fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"}),n.textBlock.measureText(),null!==this.itemWidth&&(n.textBlock.width=this.itemWidth-(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))),this.height<k-this.lineHeight?(q={items:[],width:0},m.push(q)):(q=m[z],z=(z+1)%m.length),this.height+=n.textBlock.height;n.textBlock.x=q.width;n.textBlock.y=0;q.width+=Math.round(n.textBlock.width+r+l+(0===q.width?
0:this.horizontalSpacing)+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0));q.items.push(n);this.width=Math.max(q.width,this.width);p=n.textBlock.width+(r+l+("line"===n.chartType||"spline"===n.chartType||"stepLine"===n.chartType?2*0.1*this.lineHeight:0))}}this.itemWidth=p;this.height=!1===this.itemWrap?m.length*this.lineHeight:this.height+f;this.height=Math.min(k,this.height);this.width=Math.min(g,this.width)}"top"===this.verticalAlign?(e="left"===this.horizontalAlign?
d.x1:"right"===this.horizontalAlign?d.x2-this.width:d.x1+d.width/2-this.width/2,c=d.y1):"center"===this.verticalAlign?(e="left"===this.horizontalAlign?d.x1:"right"===this.horizontalAlign?d.x2-this.width:d.x1+d.width/2-this.width/2,c=d.y1+d.height/2-this.height/2):"bottom"===this.verticalAlign&&(e="left"===this.horizontalAlign?d.x1:"right"===this.horizontalAlign?d.x2-this.width:d.x1+d.width/2-this.width/2,c=d.y2-this.height);this.items=h;for(s=0;s<this.items.length;s++)n=h[s],n.id=++this.chart._eventManager.lastObjectId,
this.chart._eventManager.objectMap[n.id]={id:n.id,objectType:"legendItem",legendItemIndex:s,dataSeriesIndex:n.dataSeriesIndex,dataPointIndex:n.dataPointIndex};this.markerSize=r;this.rows=m;0<h.length&&a.layoutManager.registerSpace(b,{width:this.width+2+2,height:this.height+5+5});this.bounds={x1:e,y1:c,x2:e+this.width,y2:c+this.height}};H.prototype.render=function(){var a=this.bounds.x1,d=this.bounds.y1,b=this.markerMargin,c=this.maxWidth,e=this.maxHeight,g=this.markerSize,k=this.rows;(0<this.borderThickness&&
this.borderColor||this.backgroundColor)&&this.ctx.roundRect(a,d,this.width,this.height,this.cornerRadius,this.borderThickness,this.backgroundColor,this.borderColor);for(var l=0,h=0;h<k.length;h++){for(var m=k[h],s=0,q=0;q<m.items.length;q++){var n=m.items[q],f=n.textBlock.x+a+(0===q?0.2*g:this.horizontalSpacing),p=d+l,u=f;this.chart.data[n.dataSeriesIndex].visible||(this.ctx.globalAlpha=0.5);this.ctx.save();this.ctx.beginPath();this.ctx.rect(a,d,c,Math.max(e-e%this.lineHeight,0));this.ctx.clip();
if("line"===n.chartType||"stepLine"===n.chartType||"spline"===n.chartType)this.ctx.strokeStyle=n.lineColor,this.ctx.lineWidth=Math.ceil(this.lineHeight/8),this.ctx.beginPath(),this.ctx.moveTo(f-0.1*this.lineHeight,p+this.lineHeight/2),this.ctx.lineTo(f+0.85*this.lineHeight,p+this.lineHeight/2),this.ctx.stroke(),u-=0.1*this.lineHeight;if("error"===n.chartType){this.ctx.strokeStyle=this.errorMarkerColor[0];this.ctx.lineWidth=g/8;this.ctx.beginPath();var r=f-0.08*this.lineHeight+0.1*this.lineHeight,
t=p+0.15*this.lineHeight,v=0.7*this.lineHeight,x=v+0.02*this.lineHeight;this.ctx.moveTo(r,t);this.ctx.lineTo(r+v,t);this.ctx.stroke();this.ctx.beginPath();this.ctx.moveTo(r+v/2,t);this.ctx.lineTo(r+v/2,t+x);this.ctx.stroke();this.ctx.beginPath();this.ctx.moveTo(r,t+x);this.ctx.lineTo(r+v,t+x);this.ctx.stroke();this.errorMarkerColor.shift()}ia.drawMarker(f+g/2,p+this.lineHeight/2,this.ctx,n.markerType,"error"===n.chartType||"line"===n.chartType||"spline"===n.chartType?n.markerSize/2:n.markerSize,n.markerColor,
n.markerBorderColor,n.markerBorderThickness);n.textBlock.x=f+b+g;if("line"===n.chartType||"stepLine"===n.chartType||"spline"===n.chartType)n.textBlock.x+=0.1*this.lineHeight;n.textBlock.y=Math.round(p+this.lineHeight/2);n.textBlock.render(!0);this.ctx.restore();s=0<q?Math.max(s,n.textBlock.height):n.textBlock.height;this.chart.data[n.dataSeriesIndex].visible||(this.ctx.globalAlpha=1);f=N(n.id);this.ghostCtx.fillStyle=f;this.ghostCtx.beginPath();this.ghostCtx.fillRect(u,n.textBlock.y-this.lineHeight/
2,n.textBlock.x+n.textBlock.width-u,n.textBlock.height);n.x1=this.chart._eventManager.objectMap[n.id].x1=u;n.y1=this.chart._eventManager.objectMap[n.id].y1=n.textBlock.y-this.lineHeight/2;n.x2=this.chart._eventManager.objectMap[n.id].x2=n.textBlock.x+n.textBlock.width;n.y2=this.chart._eventManager.objectMap[n.id].y2=n.textBlock.y+n.textBlock.height-this.lineHeight/2}l+=s}};qa(F,V);F.prototype.getDefaultAxisPlacement=function(){var a=this.type;if("column"===a||"line"===a||"stepLine"===a||"spline"===
a||"area"===a||"stepArea"===a||"splineArea"===a||"stackedColumn"===a||"stackedLine"===a||"bubble"===a||"scatter"===a||"stackedArea"===a||"stackedColumn100"===a||"stackedLine100"===a||"stackedArea100"===a||"candlestick"===a||"ohlc"===a||"rangeColumn"===a||"rangeArea"===a||"rangeSplineArea"===a||"boxAndWhisker"===a||"waterfall"===a)return"normal";if("bar"===a||"stackedBar"===a||"stackedBar100"===a||"rangeBar"===a)return"xySwapped";if("pie"===a||"doughnut"===a||"funnel"===a||"pyramid"===a)return"none";
"error"!==a&&window.console.log("Unknown Chart Type: "+a);return null};F.getDefaultLegendMarker=function(a){if("column"===a||"stackedColumn"===a||"stackedLine"===a||"bar"===a||"stackedBar"===a||"stackedBar100"===a||"bubble"===a||"scatter"===a||"stackedColumn100"===a||"stackedLine100"===a||"stepArea"===a||"candlestick"===a||"ohlc"===a||"rangeColumn"===a||"rangeBar"===a||"rangeArea"===a||"rangeSplineArea"===a||"boxAndWhisker"===a||"waterfall"===a)return"square";if("line"===a||"stepLine"===a||"spline"===
a||"pie"===a||"doughnut"===a)return"circle";if("area"===a||"splineArea"===a||"stackedArea"===a||"stackedArea100"===a||"funnel"===a||"pyramid"===a)return"triangle";if("error"===a)return"none";window.console.log("Unknown Chart Type: "+a);return null};F.prototype.getDataPointAtX=function(a,d){if(!this.dataPoints||0===this.dataPoints.length)return null;var b={dataPoint:null,distance:Infinity,index:NaN},c=null,e=0,g=0,k=1,l=Infinity,h=0,m=0,s=0;"none"!==this.chart.plotInfo.axisPlacement&&(this.axisX.logarithmic?
(s=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),s=1<s?Math.min(Math.max((this.dataPoints.length-1)/s*Math.log(a/this.dataPoints[0].x)>>0,0),this.dataPoints.length):0):(s=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,s=0<s?Math.min(Math.max((this.dataPoints.length-1)/s*(a-this.dataPoints[0].x)>>0,0),this.dataPoints.length):0));for(;;){g=0<k?s+e:s-e;if(0<=g&&g<this.dataPoints.length){var c=this.dataPoints[g],q=this.axisX.logarithmic?c.x>a?c.x/a:a/c.x:
Math.abs(c.x-a);q<b.distance&&(b.dataPoint=c,b.distance=q,b.index=g);c=q;c<=l?l=c:0<k?h++:m++;if(1E3<h&&1E3<m)break}else if(0>s-e&&s+e>=this.dataPoints.length)break;-1===k?(e++,k=1):k=-1}return d||b.dataPoint.x!==a?d&&null!==b.dataPoint?b:null:b};F.prototype.getDataPointAtXY=function(a,d,b){if(!this.dataPoints||0===this.dataPoints.length||a<this.chart.plotArea.x1||a>this.chart.plotArea.x2||d<this.chart.plotArea.y1||d>this.chart.plotArea.y2)return null;b=b||!1;var c=[],e=0,g=0,k=1,l=!1,h=Infinity,
m=0,s=0,q=0;if("none"!==this.chart.plotInfo.axisPlacement)if(q=(this.chart.axisX[0]?this.chart.axisX[0]:this.chart.axisX2[0]).getXValueAt({x:a,y:d}),this.axisX.logarithmic)var n=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),q=1<n?Math.min(Math.max((this.dataPoints.length-1)/n*Math.log(q/this.dataPoints[0].x)>>0,0),this.dataPoints.length):0;else n=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,q=0<n?Math.min(Math.max((this.dataPoints.length-1)/n*(q-this.dataPoints[0].x)>>
0,0),this.dataPoints.length):0;for(;;){g=0<k?q+e:q-e;if(0<=g&&g<this.dataPoints.length){var n=this.chart._eventManager.objectMap[this.dataPointIds[g]],f=this.dataPoints[g],p=null;if(n){switch(this.type){case "column":case "stackedColumn":case "stackedColumn100":case "bar":case "stackedBar":case "stackedBar100":case "rangeColumn":case "rangeBar":case "waterfall":case "error":a>=n.x1&&(a<=n.x2&&d>=n.y1&&d<=n.y2)&&(c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-
a),Math.abs(n.x2-a),Math.abs(n.y1-d),Math.abs(n.y2-d))}),l=!0);break;case "line":case "stepLine":case "spline":case "area":case "stepArea":case "stackedArea":case "stackedArea100":case "splineArea":case "scatter":var u=na("markerSize",f,this)||4,r=b?20:u,p=Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y1-d,2));p<=r&&c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:p});n=Math.abs(n.x1-a);n<=h?h=n:0<k?m++:s++;p<=u/2&&(l=!0);break;case "rangeArea":case "rangeSplineArea":u=na("markerSize",f,this)||
4;r=b?20:u;p=Math.min(Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y1-d,2)),Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y2-d,2)));p<=r&&c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:p});n=Math.abs(n.x1-a);n<=h?h=n:0<k?m++:s++;p<=u/2&&(l=!0);break;case "bubble":u=n.size;p=Math.sqrt(Math.pow(n.x1-a,2)+Math.pow(n.y1-d,2));p<=u/2&&(c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:p}),l=!0);break;case "pie":case "doughnut":u=n.center;r="doughnut"===this.type?n.percentInnerRadius*n.radius:
0;p=Math.sqrt(Math.pow(u.x-a,2)+Math.pow(u.y-d,2));p<n.radius&&p>r&&(p=Math.atan2(d-u.y,a-u.x),0>p&&(p+=2*Math.PI),p=Number(((180*(p/Math.PI)%360+360)%360).toFixed(12)),u=Number(((180*(n.startAngle/Math.PI)%360+360)%360).toFixed(12)),r=Number(((180*(n.endAngle/Math.PI)%360+360)%360).toFixed(12)),0===r&&1<n.endAngle&&(r=360),u>=r&&0!==f.y&&(r+=360,p<u&&(p+=360)),p>u&&p<r&&(c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:0}),l=!0));break;case "funnel":case "pyramid":p=n.funnelSection;
d>p.y1&&d<p.y4&&(p.y6?d>p.y6?(g=p.x6+(p.x5-p.x6)/(p.y5-p.y6)*(d-p.y6),p=p.x3+(p.x4-p.x3)/(p.y4-p.y3)*(d-p.y3)):(g=p.x1+(p.x6-p.x1)/(p.y6-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)):(g=p.x1+(p.x4-p.x1)/(p.y4-p.y1)*(d-p.y1),p=p.x2+(p.x3-p.x2)/(p.y3-p.y2)*(d-p.y2)),a>g&&a<p&&(c.push({dataPoint:f,dataPointIndex:n.dataPointIndex,dataSeries:this,distance:0}),l=!0));break;case "boxAndWhisker":if(a>=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y4-n.borderThickness/2&&d<=n.y1+n.borderThickness/
2||Math.abs(n.x2-a+n.x1-a)<n.borderThickness&&d>=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "candlestick":if(a>=n.x1-n.borderThickness/2&&a<=n.x2+n.borderThickness/2&&d>=n.y2-n.borderThickness/2&&d<=n.y3+n.borderThickness/2||Math.abs(n.x2-a+n.x1-a)<n.borderThickness&&d>=n.y1&&d<=n.y4)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),
Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0;break;case "ohlc":if(Math.abs(n.x2-a+n.x1-a)<n.borderThickness&&d>=n.y2&&d<=n.y3||a>=n.x1&&a<=(n.x2+n.x1)/2&&d>=n.y1-n.borderThickness/2&&d<=n.y1+n.borderThickness/2||a>=(n.x1+n.x2)/2&&a<=n.x2&&d>=n.y4-n.borderThickness/2&&d<=n.y4+n.borderThickness/2)c.push({dataPoint:f,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(n.x1-a),Math.abs(n.x2-a),Math.abs(n.y2-d),Math.abs(n.y3-d))}),l=!0}if(l||1E3<m&&1E3<s)break}}else if(0>q-e&&q+e>=
this.dataPoints.length)break;-1===k?(e++,k=1):k=-1}a=null;for(d=0;d<c.length;d++)a?c[d].distance<=a.distance&&(a=c[d]):a=c[d];return a};F.prototype.getMarkerProperties=function(a,d,b,c){var e=this.dataPoints;return{x:d,y:b,ctx:c,type:e[a].markerType?e[a].markerType:this.markerType,size:e[a].markerSize?e[a].markerSize:this.markerSize,color:e[a].markerColor?e[a].markerColor:this.markerColor?this.markerColor:e[a].color?e[a].color:this.color?this.color:this._colorSet[a%this._colorSet.length],borderColor:e[a].markerBorderColor?
e[a].markerBorderColor:this.markerBorderColor?this.markerBorderColor:null,borderThickness:e[a].markerBorderThickness?e[a].markerBorderThickness:this.markerBorderThickness?this.markerBorderThickness:null}};qa(z,V);z.prototype.createExtraLabelsForLog=function(a){a=(a||0)+1;if(!(5<a)){var d=this.logLabelValues[0]||this.intervalStartPosition;if(Math.log(this.range)/Math.log(d/this.viewportMinimum)<this.noTicks-1){for(var b=z.getNiceNumber((d-this.viewportMinimum)/Math.min(Math.max(2,this.noTicks-this.logLabelValues.length),
3),!0),c=Math.ceil(this.viewportMinimum/b)*b;c<d;c+=b)c<this.viewportMinimum||this.logLabelValues.push(c);this.logLabelValues.sort(Sa);this.createExtraLabelsForLog(a)}}};z.prototype.createLabels=function(){var a,d,b=0,c=0,e,g=0,k=0,c=0,c=this.interval,l=0,h,m=0.6*this.chart.height,p;a=!1;var q=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[],n=q.length?u(this.scaleBreaks.firstBreakIndex)?0:this.scaleBreaks.firstBreakIndex:0;if("axisX"!==this.type||"dateTime"!==this.valueType||this.logarithmic){e=
this.viewportMaximum;if(this.labels){a=Math.ceil(c);for(var c=Math.ceil(this.intervalStartPosition),f=!1,b=c;b<this.viewportMaximum;b+=a)if(this.labels[b])f=!0;else{f=!1;break}f&&(this.interval=a,this.intervalStartPosition=c)}if(this.logarithmic&&!this.equidistantInterval)for(this.logLabelValues||(this.logLabelValues=[],this.createExtraLabelsForLog()),c=0,f=n;c<this.logLabelValues.length;c++)if(b=this.logLabelValues[c],b<this.viewportMinimum)c++;else{for(;f<q.length&&b>q[f].endValue;f++);a=f<q.length&&
b>=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:k,angle:this.labelAngle,text:this.prefix+a+this.suffix,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,
horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle",borderThickness:0}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}f=n;for(b=this.intervalStartPosition;b<=e;b=parseFloat(1E-12>this.interval?this.logarithmic&&this.equidistantInterval?b*Math.pow(this.logarithmBase,this.interval):b+this.interval:(this.logarithmic&&this.equidistantInterval?
b*Math.pow(this.logarithmBase,this.interval):b+this.interval).toFixed(12))){for(;f<q.length&&b>q[f].endValue;f++);a=f<q.length&&b>=q[f].startValue&&b<=q[f].endValue;p=b;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:p,label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:ba(p,this.valueFormatString,this.chart._cultureInfo),a=new ka(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:k,angle:this.labelAngle,text:this.prefix+a+this.suffix,
horizontalAlign:"left",backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null}))}}else for(this.intervalStartPosition=this.getLabelStartPoint(new Date(this.viewportMinimum),
this.intervalType,this.interval),e=Ya(new Date(this.viewportMaximum),this.interval,this.intervalType),f=n,b=this.intervalStartPosition;b<e;Ya(b,c,this.intervalType)){for(a=b.getTime();f<q.length&&a>q[f].endValue;f++);p=a;a=f<q.length&&a>=q[f].startValue&&a<=q[f].endValue;a||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:new Date(p),label:this.labels[p]?this.labels[p]:null}):"axisX"===this.type&&this.labels[p]?this.labels[p]:Ca(p,this.valueFormatString,this.chart._cultureInfo),
a=new ka(this.ctx,{x:0,y:0,maxWidth:g,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,maxHeight:k,angle:this.labelAngle,text:this.prefix+a+this.suffix,horizontalAlign:"left",fontSize:this.labelFontSize,fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"}),this._labels.push({position:p,textBlock:a,effectiveHeight:null,
breaksLabelType:void 0}))}if("bottom"===this._position||"top"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.width*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length-2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.width/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+
"Duration"]*this.interval,g="undefined"===typeof this.options.labelMaxWidth?0.5*this.chart.width>>0:this.options.labelMaxWidth,this.chart.panEnabled||(k="undefined"===typeof this.options.labelWrap||this.labelWrap?0.8*this.chart.height>>0:1.5*this.labelFontSize);else if("left"===this._position||"right"===this._position)l=this.logarithmic&&!this.equidistantInterval&&2<=this._labels.length?this.lineCoordinates.height*Math.log(Math.min(this._labels[this._labels.length-1].position/this._labels[this._labels.length-
2].position,this._labels[1].position/this._labels[0].position))/Math.log(this.range):this.lineCoordinates.height/(this.logarithmic&&this.equidistantInterval?Math.log(this.range)/Math.log(this.logarithmBase):Math.abs(this.range))*S[this.intervalType+"Duration"]*this.interval,this.chart.panEnabled||(g="undefined"===typeof this.options.labelMaxWidth?0.3*this.chart.width>>0:this.options.labelMaxWidth),k="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize;
for(c=0;c<this._labels.length;c++){a=this._labels[c].textBlock;a.maxWidth=g;a.maxHeight=k;var B=a.measureText();h=B.height}e=[];n=q=0;if(this.labelAutoFit||this.options.labelAutoFit)if(u(this.labelAngle)||(this.labelAngle=(this.labelAngle%360+360)%360,90<this.labelAngle&&270>this.labelAngle?this.labelAngle-=180:270<=this.labelAngle&&360>=this.labelAngle&&(this.labelAngle-=360)),"bottom"===this._position||"top"===this._position)if(g=0.9*l>>0,n=0,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize=
this.labelFontSize;this.sessionVariables.labelMaxWidth=g;this.sessionVariables.labelMaxHeight=k;this.sessionVariables.labelAngle=this.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b<this._labels.length;b++)if(!this._labels[b].breaksLabelType){a=this._labels[b].textBlock;for(var v,f=a.text.split(" "),c=0;c<f.length;c++)p=f[c],this.ctx.font=a.fontStyle+" "+a.fontWeight+" "+a.fontSize+"px "+a.fontFamily,p=this.ctx.measureText(p),p.width>n&&(v=b,n=p.width)}b=0;for(b=this.intervalStartPosition<
this.viewportMinimum?1:0;b<this._labels.length;b++)if(!this._labels[b].breaksLabelType){a=this._labels[b].textBlock;B=a.measureText();for(f=b+1;f<this._labels.length;f++)if(!this._labels[f].breaksLabelType){d=this._labels[f].textBlock;d=d.measureText();break}e.push(a.height);this.sessionVariables.labelMaxHeight=Math.max.apply(Math,e);Math.cos(Math.PI/180*Math.abs(this.labelAngle));Math.sin(Math.PI/180*Math.abs(this.labelAngle));c=g*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(k-a.fontSize/2)*
Math.cos(Math.PI/180*Math.abs(this.labelAngle));if(u(this.options.labelAngle)&&isNaN(this.options.labelAngle)&&0!==this.options.labelAngle)if(this.sessionVariables.labelMaxHeight=0===this.labelAngle?k:Math.min((c-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)),c),p=(m-(h+a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(-25)))/Math.sin(Math.PI/180*Math.abs(-25)),!u(this.options.labelWrap))this.labelWrap?u(this.options.labelMaxWidth)?(this.sessionVariables.labelMaxWidth=
Math.min(Math.max(g,n),p),this.sessionVariables.labelWrap=this.labelWrap,B.width+d.width>>0>2*g&&(this.sessionVariables.labelAngle=-25)):(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle):u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelMaxWidth=
g,B.width+d.width>>0>2*g&&(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p)):(this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelWrap=this.labelWrap);else{if(u(this.options.labelWrap))if(!u(this.options.labelMaxWidth))this.options.labelMaxWidth<g?(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,
this.sessionVariables.labelMaxHeight=c):(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=k);else if(!u(d))if(c=B.width+d.width>>0,f=this.labelFontSize,n<g)c-2*g>q&&(q=c-2*g,c>=2*g&&c<2.2*g?(this.sessionVariables.labelMaxWidth=g,u(this.options.labelFontSize)&&12<f&&(f=Math.floor(12/13*f),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelAngle=
this.labelAngle):c>=2.2*g&&c<2.8*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=f):c>=2.8*g&&c<3.2*g?(this.sessionVariables.labelMaxWidth=Math.max(g,n),this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelAngle=
this.labelAngle):c>=3.2*g&&c<3.6*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelFontSize=this.labelFontSize):c>3.6*g&&c<5*g?(u(this.options.labelFontSize)&&12<f&&(f=Math.floor(12/13*f),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p):
c>5*g&&(this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelFontSize=f,this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelAngle=this.labelAngle));else if(v===b&&(0===v&&n+this._labels[v+1].textBlock.measureText().width-2*g>q||v===this._labels.length-1&&n+this._labels[v-1].textBlock.measureText().width-2*g>q||0<v&&v<this._labels.length-1&&n+this._labels[v+1].textBlock.measureText().width-2*g>q&&n+this._labels[v-1].textBlock.measureText().width-
2*g>q))q=0===v?n+this._labels[v+1].textBlock.measureText().width-2*g:n+this._labels[v-1].textBlock.measureText().width-2*g,this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=p;else if(0===q)for(this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?f:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,c=0;c<this._labels.length;c++)a=
this._labels[c].textBlock,a.maxWidth=this.sessionVariables.labelMaxWidth=Math.min(Math.max(g,n),p),B=a.measureText(),c<this._labels.length-1&&(f=c+1,d=this._labels[f].textBlock,d.maxWidth=this.sessionVariables.labelMaxWidth=Math.min(Math.max(g,n),p),d=d.measureText(),B.width+d.width>>0>2*g&&(this.sessionVariables.labelAngle=-25))}else(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxHeight=0===this.labelAngle?k:Math.min((c-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/
Math.sin(Math.PI/180*Math.abs(this.labelAngle)),c),p=0!=this.labelAngle?(m-(h+a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)):g,this.sessionVariables.labelMaxHeight=k=this.labelWrap?(m-p*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/Math.cos(Math.PI/180*Math.abs(this.labelAngle)):1.5*this.labelFontSize,u(this.options.labelWrap))?u(this.options.labelWrap)&&(this.labelWrap&&!u(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=
this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelMaxHeight=k):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=p,this.sessionVariables.labelMaxHeight=c<0.9*l?0.9*l:c,this.sessionVariables.labelWrap=this.labelWrap)):(this.options.labelWrap?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p):
(u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:p,this.sessionVariables.labelWrap=this.labelWrap),this.sessionVariables.labelMaxHeight=k)}for(c=0;c<this._labels.length;c++)a=this._labels[c].textBlock,a.maxWidth=this.labelMaxWidth=this.sessionVariables.labelMaxWidth,a.fontSize=this.sessionVariables.labelFontSize,a.angle=this.labelAngle=this.sessionVariables.labelAngle,a.wrap=this.labelWrap=this.sessionVariables.labelWrap,a.maxHeight=
this.sessionVariables.labelMaxHeight,a.measureText()}else for(b=0;b<this._labels.length;b++)a=this._labels[b].textBlock,a.maxWidth=this.labelMaxWidth=u(this.options.labelMaxWidth)?this.sessionVariables.labelMaxWidth:this.options.labelMaxWidth,a.fontSize=this.labelFontSize=u(this.options.labelFontSize)?this.sessionVariables.labelFontSize:this.options.labelFontSize,a.angle=this.labelAngle=u(this.options.labelAngle)?this.sessionVariables.labelAngle:this.labelAngle,a.wrap=this.labelWrap=u(this.options.labelWrap)?
this.sessionVariables.labelWrap:this.options.labelWrap,a.maxHeight=this.sessionVariables.labelMaxHeight,a.measureText();else if("left"===this._position||"right"===this._position)if(g=u(this.options.labelMaxWidth)?0.3*this.chart.width>>0:this.options.labelMaxWidth,k="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize=this.labelFontSize;this.sessionVariables.labelMaxWidth=
g;this.sessionVariables.labelMaxHeight=k;this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b<this._labels.length;b++)if(!this._labels[b].breaksLabelType){a=this._labels[b].textBlock;B=a.measureText();for(f=b+1;f<this._labels.length;f++)if(!this._labels[f].breaksLabelType){d=this._labels[f].textBlock;d=d.measureText();break}e.push(a.height);this.sessionVariables.labelMaxHeight=Math.max.apply(Math,
e);c=g*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(k-a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle));Math.cos(Math.PI/180*Math.abs(this.labelAngle));Math.sin(Math.PI/180*Math.abs(this.labelAngle));u(this.options.labelAngle)&&isNaN(this.options.labelAngle)&&0!==this.options.labelAngle?u(this.options.labelWrap)?u(this.options.labelWrap)&&(u(this.options.labelMaxWidth)?u(d)||(l=B.height+d.height>>0,l-2*k>n&&(n=l-2*k,l>=2*k&&l<2.4*k?(u(this.options.labelFontSize)&&12<this.labelFontSize&&
(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize):l>=2.4*k&&l<2.8*k?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelFontSize=this.labelFontSize,this.sessionVariables.labelWrap=!0):l>=2.8*k&&l<3.2*k?(this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelWrap=!0,u(this.options.labelFontSize)&&12<
this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):l>=3.2*k&&l<3.6*k?(this.sessionVariables.labelMaxHeight=c,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelFontSize=this.labelFontSize):l>3.6*k&&l<10*k?(u(this.options.labelFontSize)&&
12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):l>10*k&&l<50*k&&(u(this.options.labelFontSize)&&12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),
a.measureText()),this.sessionVariables.labelFontSize=u(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelAngle=u(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle))):(this.sessionVariables.labelMaxHeight=k,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth)):(this.sessionVariables.labelMaxWidth=
this.labelWrap?this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth:this.labelMaxWidth?this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth:g,this.sessionVariables.labelMaxHeight=k):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=0===this.labelAngle?g:Math.min((c-k*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/Math.cos(Math.PI/180*Math.abs(this.labelAngle)),k),u(this.options.labelWrap))?
u(this.options.labelWrap)&&(this.labelWrap&&!u(this.options.labelMaxWidth)?(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth>this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=c):(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:g,this.sessionVariables.labelMaxHeight=0===this.labelAngle?k:c,u(this.options.labelMaxWidth)&&
(this.sessionVariables.labelAngle=this.labelAngle))):this.options.labelWrap?(this.sessionVariables.labelMaxHeight=0===this.labelAngle?k:c,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=g):(this.sessionVariables.labelMaxHeight=k,u(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=this.labelWrap)}for(c=0;c<this._labels.length;c++)a=
this._labels[c].textBlock,a.maxWidth=this.labelMaxWidth=this.sessionVariables.labelMaxWidth,a.fontSize=this.labelFontSize=this.sessionVariables.labelFontSize,a.angle=this.labelAngle=this.sessionVariables.labelAngle,a.wrap=this.labelWrap=this.sessionVariables.labelWrap,a.maxHeight=this.sessionVariables.labelMaxHeight,a.measureText()}else for(b=0;b<this._labels.length;b++)a=this._labels[b].textBlock,a.maxWidth=this.labelMaxWidth=u(this.options.labelMaxWidth)?this.sessionVariables.labelMaxWidth:this.options.labelMaxWidth,
a.fontSize=this.labelFontSize=u(this.options.labelFontSize)?this.sessionVariables.labelFontSize:this.options.labelFontSize,a.angle=this.labelAngle=u(this.options.labelAngle)?this.sessionVariables.labelAngle:this.labelAngle,a.wrap=this.labelWrap=u(this.options.labelWrap)?this.sessionVariables.labelWrap:this.options.labelWrap,a.maxHeight=this.sessionVariables.labelMaxHeight,a.measureText();for(b=0;b<this.stripLines.length;b++){var g=this.stripLines[b],z;if("outside"===g.labelPlacement){k=this.sessionVariables.labelMaxWidth;
if("bottom"===this._position||"top"===this._position)z=u(g.options.labelWrap)?this.sessionVariables.labelMaxHeight:g.labelWrap?0.8*this.chart.height>>0:1.5*this.labelFontSize;if("left"===this._position||"right"===this._position)z=u(g.options.labelWrap)?this.sessionVariables.labelMaxHeight:g.labelWrap?0.8*this.chart.width>>0:1.5*this.labelFontSize;u(g.labelBackgroundColor)&&(g.labelBackgroundColor="#EEEEEE")}else k="bottom"===this._position||"top"===this._position?0.9*this.chart.width>>0:0.9*this.chart.height>>
0,z=u(g.options.labelWrap)||g.labelWrap?"bottom"===this._position||"top"===this._position?0.8*this.chart.width>>0:0.8*this.chart.height>>0:1.5*this.labelFontSize,u(g.labelBackgroundColor)&&(u(g.startValue)&&0!==g.startValue?g.labelBackgroundColor=r?"transparent":null:g.labelBackgroundColor="#EEEEEE");a=new ka(this.ctx,{x:0,y:0,backgroundColor:g.labelBackgroundColor,borderColor:g.labelBorderColor,borderThickness:g.labelBorderThickness,cornerRadius:g.labelCornerRadius,maxWidth:g.options.labelMaxWidth?
g.options.labelMaxWidth:k,maxHeight:z,angle:this.labelAngle,text:g.labelFormatter?g.labelFormatter({chart:this.chart,axis:this,stripLine:g}):g.label,horizontalAlign:"left",fontSize:"outside"===g.labelPlacement?g.options.labelFontSize?g.labelFontSize:this.labelFontSize:g.labelFontSize,fontFamily:"outside"===g.labelPlacement?g.options.labelFontFamily?g.labelFontFamily:this.labelFontFamily:g.labelFontFamily,fontWeight:"outside"===g.labelPlacement?g.options.labelFontWeight?g.labelFontWeight:this.labelFontWeight:
g.labelFontWeight,fontColor:g.labelFontColor||g.color,fontStyle:"outside"===g.labelPlacement?g.options.labelFontStyle?g.labelFontStyle:this.fontWeight:g.labelFontStyle,textBaseline:"middle"});this._stripLineLabels.push({position:g.value,textBlock:a,effectiveHeight:null,stripLine:g})}};z.prototype.createLabelsAndCalculateWidth=function(){var a=0,d=0;this._labels=[];this._stripLineLabels=[];var b=this.chart.isNavigator?0:5;if("left"===this._position||"right"===this._position){this.createLabels();for(d=
0;d<this._labels.length;d++){var c=this._labels[d].textBlock,e=c.measureText(),g=0,g=0===this.labelAngle?e.width:e.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(e.height-c.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle));a<g&&(a=g);this._labels[d].effectiveWidth=g}for(d=0;d<this._stripLineLabels.length;d++)"outside"===this._stripLineLabels[d].stripLine.labelPlacement&&(this._stripLineLabels[d].stripLine.value>=this.viewportMinimum&&this._stripLineLabels[d].stripLine.value<=this.viewportMaximum)&&
(c=this._stripLineLabels[d].textBlock,e=c.measureText(),g=0===this.labelAngle?e.width:e.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(e.height-c.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle)),a<g&&(a=g),this._stripLineLabels[d].effectiveWidth=g)}d=this.title?this._titleTextBlock.measureText().height+2:0;return c="inside"===this.labelPlacement?c=d+b:d+a+this.tickLength+b};z.prototype.createLabelsAndCalculateHeight=function(){var a=0;this._labels=[];this._stripLineLabels=[];var d,
b=0,c=this.chart.isNavigator?0:5;this.createLabels();if("bottom"===this._position||"top"===this._position){for(b=0;b<this._labels.length;b++){d=this._labels[b].textBlock;var e=d.measureText(),g=0,g=0===this.labelAngle?e.height:e.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(e.height-d.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle));a<g&&(a=g);this._labels[b].effectiveHeight=g}for(b=0;b<this._stripLineLabels.length;b++)"outside"===this._stripLineLabels[b].stripLine.labelPlacement&&
(this._stripLineLabels[b].stripLine.value>=this.viewportMinimum&&this._stripLineLabels[b].stripLine.value<=this.viewportMaximum)&&(d=this._stripLineLabels[b].textBlock,e=d.measureText(),g=0===this.labelAngle?e.height:e.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(e.height-d.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)),a<g&&(a=g),this._stripLineLabels[b].effectiveHeight=g)}d=this.title?this._titleTextBlock.measureText().height+2:0;return b="inside"===this.labelPlacement?b=
d+c:d+a+this.tickLength+c};z.setLayout=function(a,d,b,c,e,g){var k,l,h,m,p=a[0]?a[0].chart:d[0].chart,q=p.isNavigator?0:10,n=p._axes;if(a&&0<a.length)for(var f=0;f<a.length;f++)a[f]&&a[f].calculateAxisParameters();if(d&&0<d.length)for(f=0;f<d.length;f++)d[f].calculateAxisParameters();if(b&&0<b.length)for(f=0;f<b.length;f++)b[f].calculateAxisParameters();if(c&&0<c.length)for(f=0;f<c.length;f++)c[f].calculateAxisParameters();for(f=0;f<n.length;f++)if(n[f]&&n[f].scaleBreaks&&n[f].scaleBreaks._appliedBreaks.length)for(var r=
n[f].scaleBreaks._appliedBreaks,v=0;v<r.length&&!(r[v].startValue>n[f].viewportMaximum);v++)r[v].endValue<n[f].viewportMinimum||(u(n[f].scaleBreaks.firstBreakIndex)&&(n[f].scaleBreaks.firstBreakIndex=v),r[v].startValue>=n[f].viewPortMinimum&&(n[f].scaleBreaks.lastBreakIndex=v));for(var z=v=0,t=0,C=0,x=0,D=0,y=0,A,E,F=l=0,H,I,L,r=H=I=L=!1,f=0;f<n.length;f++)n[f]&&n[f].title&&(n[f]._titleTextBlock=new ka(n[f].ctx,{text:n[f].title,horizontalAlign:"center",fontSize:n[f].titleFontSize,fontFamily:n[f].titleFontFamily,
fontWeight:n[f].titleFontWeight,fontColor:n[f].titleFontColor,fontStyle:n[f].titleFontStyle,borderColor:n[f].titleBorderColor,borderThickness:n[f].titleBorderThickness,backgroundColor:n[f].titleBackgroundColor,cornerRadius:n[f].titleCornerRadius,textBaseline:"top"}));for(f=0;f<n.length;f++)if(n[f].title)switch(n[f]._position){case "left":n[f]._titleTextBlock.maxWidth=n[f].titleMaxWidth||g.height;n[f]._titleTextBlock.maxHeight=n[f].titleWrap?0.8*g.width:1.5*n[f].titleFontSize;n[f]._titleTextBlock.angle=
-90;break;case "right":n[f]._titleTextBlock.maxWidth=n[f].titleMaxWidth||g.height;n[f]._titleTextBlock.maxHeight=n[f].titleWrap?0.8*g.width:1.5*n[f].titleFontSize;n[f]._titleTextBlock.angle=90;break;default:n[f]._titleTextBlock.maxWidth=n[f].titleMaxWidth||g.width,n[f]._titleTextBlock.maxHeight=n[f].titleWrap?0.8*g.height:1.5*n[f].titleFontSize,n[f]._titleTextBlock.angle=0}if("normal"===e){for(var C=[],x=[],D=[],y=[],M=[],N=[],O=[],Q=[];4>v;){var G=0,R=0,S=0,U=0,W=e=0,K=0,$=0,V=0,X=0,P=0,ba=0;if(b&&
0<b.length)for(D=[],f=P=0;f<b.length;f++)D.push(Math.ceil(b[f]?b[f].createLabelsAndCalculateWidth():0)),P+=D[f],K+=b[f]&&!p.isNavigator?b[f].margin:0;else D.push(Math.ceil(b[0]?b[0].createLabelsAndCalculateWidth():0));O.push(D);if(c&&0<c.length)for(y=[],f=ba=0;f<c.length;f++)y.push(Math.ceil(c[f]?c[f].createLabelsAndCalculateWidth():0)),ba+=y[f],$+=c[f]?c[f].margin:0;else y.push(Math.ceil(c[0]?c[0].createLabelsAndCalculateWidth():0));Q.push(y);k=Math.round(g.x1+P+K);h=Math.round(g.x2-ba-$>p.width-
q?p.width-q:g.x2-ba-$);if(a&&0<a.length)for(C=[],f=V=0;f<a.length;f++)a[f]&&(a[f].lineCoordinates={}),a[f].lineCoordinates.width=Math.abs(h-k),a[f].title&&(a[f]._titleTextBlock.maxWidth=0<a[f].titleMaxWidth&&a[f].titleMaxWidth<a[f].lineCoordinates.width?a[f].titleMaxWidth:a[f].lineCoordinates.width),C.push(Math.ceil(a[f]?a[f].createLabelsAndCalculateHeight():0)),V+=C[f],e+=a[f]&&!p.isNavigator?a[f].margin:0;else C.push(Math.ceil(a[0]?a[0].createLabelsAndCalculateHeight():0));M.push(C);if(d&&0<d.length)for(x=
[],f=X=0;f<d.length;f++)d[f]&&(d[f].lineCoordinates={}),d[f].lineCoordinates.width=Math.abs(h-k),d[f].title&&(d[f]._titleTextBlock.maxWidth=0<d[f].titleMaxWidth&&d[f].titleMaxWidth<d[f].lineCoordinates.width?d[f].titleMaxWidth:d[f].lineCoordinates.width),x.push(Math.ceil(d[f]?d[f].createLabelsAndCalculateHeight():0)),X+=x[f],W+=d[f]&&!p.isNavigator?d[f].margin:0;else x.push(Math.ceil(d[0]?d[0].createLabelsAndCalculateHeight():0));N.push(x);if(a&&0<a.length)for(f=0;f<a.length;f++)a[f]&&(a[f].lineCoordinates.x1=
k,h=Math.round(g.x2-ba-$>p.width-q?p.width-q:g.x2-ba-$),a[f]._labels&&1<a[f]._labels.length&&(l=m=0,m=a[f]._labels[1],l="dateTime"===a[f].valueType?a[f]._labels[a[f]._labels.length-2]:a[f]._labels[a[f]._labels.length-1],z=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),t=l.textBlock.width*Math.cos(Math.PI/180*Math.abs(l.textBlock.angle))+(l.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/
180*Math.abs(l.textBlock.angle))),!a[f]||(!a[f].labelAutoFit||u(A)||u(E)||p.isNavigator)||(l=0,0<a[f].labelAngle?E+t>h&&(l+=0<a[f].labelAngle?E+t-h-ba:0):0>a[f].labelAngle?A-z<k&&A-z<a[f].viewportMinimum&&(F=k-(K+a[f].tickLength+D+A-z+a[f].labelFontSize/2)):0===a[f].labelAngle&&(E+t>h&&(l=E+t/2-h-ba),A-z<k&&A-z<a[f].viewportMinimum&&(F=k-K-a[f].tickLength-D-A+z/2)),a[f].viewportMaximum===a[f].maximum&&a[f].viewportMinimum===a[f].minimum&&0<a[f].labelAngle&&0<l?h-=l:a[f].viewportMaximum===a[f].maximum&&
a[f].viewportMinimum===a[f].minimum&&0>a[f].labelAngle&&0<F?k+=F:a[f].viewportMaximum===a[f].maximum&&a[f].viewportMinimum===a[f].minimum&&0===a[f].labelAngle&&(0<F&&(k+=F),0<l&&(h-=l))),p.panEnabled?V=p.sessionVariables.axisX.height:p.sessionVariables.axisX.height=V,l=Math.round(g.y2-V-e+G),m=Math.round(g.y2),a[f].lineCoordinates.x2=h,a[f].lineCoordinates.width=h-k,a[f].lineCoordinates.y1=l,a[f].lineCoordinates.y2=l,a[f].bounds={x1:k,y1:l,x2:h,y2:m-(V+e-C[f]-G),width:h-k,height:m-l}),G+=C[f]+a[f].margin;
if(d&&0<d.length)for(f=0;f<d.length;f++)d[f].lineCoordinates.x1=Math.round(g.x1+P+K),d[f].lineCoordinates.x2=Math.round(g.x2-ba-$>p.width-q?p.width-q:g.x2-ba-$),d[f].lineCoordinates.width=Math.abs(h-k),d[f]._labels&&1<d[f]._labels.length&&(m=d[f]._labels[1],l="dateTime"===d[f].valueType?d[f]._labels[d[f]._labels.length-2]:d[f]._labels[d[f]._labels.length-1],z=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),
t=l.textBlock.width*Math.cos(Math.PI/180*Math.abs(l.textBlock.angle))+(l.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(l.textBlock.angle))),p.panEnabled?X=p.sessionVariables.axisX2.height:p.sessionVariables.axisX2.height=X,l=Math.round(g.y1),m=Math.round(g.y2+d[f].margin),d[f].lineCoordinates.y1=l+X+W-R,d[f].lineCoordinates.y2=l,d[f].bounds={x1:k,y1:l+(X+W-x[f]-R),x2:h,y2:m,width:h-k,height:m-l},R+=x[f]+d[f].margin;if(b&&0<b.length)for(f=0;f<b.length;f++)K=p.isNavigator?0:
10,b[f]&&(k=Math.round(a[0]?a[0].lineCoordinates.x1:d[0].lineCoordinates.x1),K=b[f]._labels&&0<b[f]._labels.length?b[f]._labels[b[f]._labels.length-1].textBlock.height/2:q,l=Math.round(g.y1+X+W<Math.max(K,q)?Math.max(K,q):g.y1+X+W),h=Math.round(a[0]?a[0].lineCoordinates.x1:d[0].lineCoordinates.x1),K=0<a.length?0:b[f]._labels&&0<b[f]._labels.length?b[f]._labels[0].textBlock.height/2:q,m=Math.round(g.y2-V-e-K),b[f].lineCoordinates={x1:h-S,y1:l,x2:h-S,y2:m,height:Math.abs(m-l)},b[f].bounds={x1:k-(D[f]+
S),y1:l,x2:h,y2:m,width:h-k,height:m-l},b[f].title&&(b[f]._titleTextBlock.maxWidth=0<b[f].titleMaxWidth&&b[f].titleMaxWidth<b[f].lineCoordinates.height?b[f].titleMaxWidth:b[f].lineCoordinates.height),S+=D[f]+b[f].margin);if(c&&0<c.length)for(f=0;f<c.length;f++)c[f]&&(k=Math.round(a[0]?a[0].lineCoordinates.x2:d[0].lineCoordinates.x2),h=Math.round(k),K=c[f]._labels&&0<c[f]._labels.length?c[f]._labels[c[f]._labels.length-1].textBlock.height/2:0,l=Math.round(g.y1+X+W<Math.max(K,q)?Math.max(K,q):g.y1+
X+W),K=0<a.length?0:c[f]._labels&&0<c[f]._labels.length?c[f]._labels[0].textBlock.height/2:0,m=Math.round(g.y2-(V+e+K)),c[f].lineCoordinates={x1:k+U,y1:l,x2:k+U,y2:m,height:Math.abs(m-l)},c[f].bounds={x1:k,y1:l,x2:h+(y[f]+U),y2:m,width:h-k,height:m-l},c[f].title&&(c[f]._titleTextBlock.maxWidth=0<c[f].titleMaxWidth&&c[f].titleMaxWidth<c[f].lineCoordinates.height?c[f].titleMaxWidth:c[f].lineCoordinates.height),U+=y[f]+c[f].margin);if(a&&0<a.length)for(f=0;f<a.length;f++)a[f]&&(a[f].calculateValueToPixelConversionParameters(),
a[f].calculateBreaksSizeInValues(),a[f]._labels&&1<a[f]._labels.length&&(A=(a[f].logarithmic?Math.log(a[f]._labels[1].position/a[f].viewportMinimum)/a[f].conversionParameters.lnLogarithmBase:a[f]._labels[1].position-a[f].viewportMinimum)*Math.abs(a[f].conversionParameters.pixelPerUnit)+a[f].lineCoordinates.x1,k=a[f]._labels[a[f]._labels.length-("dateTime"===a[f].valueType?2:1)].position,k=a[f].getApparentDifference(a[f].viewportMinimum,k),E=a[f].logarithmic?(1<k?Math.log(k)/a[f].conversionParameters.lnLogarithmBase*
Math.abs(a[f].conversionParameters.pixelPerUnit):0)+a[f].lineCoordinates.x1:(0<k?k*Math.abs(a[f].conversionParameters.pixelPerUnit):0)+a[f].lineCoordinates.x1));if(d&&0<d.length)for(f=0;f<d.length;f++)d[f].calculateValueToPixelConversionParameters(),d[f].calculateBreaksSizeInValues(),d[f]._labels&&1<d[f]._labels.length&&(A=(d[f].logarithmic?Math.log(d[f]._labels[1].position/d[f].viewportMinimum)/d[f].conversionParameters.lnLogarithmBase:d[f]._labels[1].position-d[f].viewportMinimum)*Math.abs(d[f].conversionParameters.pixelPerUnit)+
d[f].lineCoordinates.x1,k=d[f]._labels[d[f]._labels.length-("dateTime"===d[f].valueType?2:1)].position,k=d[f].getApparentDifference(d[f].viewportMinimum,k),E=d[f].logarithmic?(1<k?Math.log(k)/d[f].conversionParameters.lnLogarithmBase*Math.abs(d[f].conversionParameters.pixelPerUnit):0)+d[f].lineCoordinates.x1:(0<k?k*Math.abs(d[f].conversionParameters.pixelPerUnit):0)+d[f].lineCoordinates.x1);for(f=0;f<n.length;f++)"axisY"===n[f].type&&(n[f].calculateValueToPixelConversionParameters(),n[f].calculateBreaksSizeInValues());
if(0<v){if(a&&0<a.length)for(f=0;f<a.length;f++)r=M[v-1][f]===M[v][f]?!0:!1;else r=!0;if(d&&0<d.length)for(f=0;f<d.length;f++)H=N[v-1][f]===N[v][f]?!0:!1;else H=!0;if(b&&0<b.length)for(f=0;f<b.length;f++)I=O[v-1][f]===O[v][f]?!0:!1;else I=!0;if(c&&0<c.length)for(f=0;f<c.length;f++)L=Q[v-1][f]===Q[v][f]?!0:!1;else L=!0}if(r&&H&&I&&L)break;v++}if(a&&0<a.length)for(f=0;f<a.length;f++)a[f].calculateStripLinesThicknessInValues(),a[f].calculateBreaksInPixels();if(d&&0<d.length)for(f=0;f<d.length;f++)d[f].calculateStripLinesThicknessInValues(),
d[f].calculateBreaksInPixels();if(b&&0<b.length)for(f=0;f<b.length;f++)b[f].calculateStripLinesThicknessInValues(),b[f].calculateBreaksInPixels();if(c&&0<c.length)for(f=0;f<c.length;f++)c[f].calculateStripLinesThicknessInValues(),c[f].calculateBreaksInPixels()}else{q=[];A=[];F=[];z=[];E=[];t=[];M=[];for(N=[];4>v;){V=U=R=S=$=K=W=e=Q=O=G=X=0;if(a&&0<a.length)for(F=[],f=U=0;f<a.length;f++)F.push(Math.ceil(a[f]?a[f].createLabelsAndCalculateWidth():0)),U+=F[f],e+=a[f]&&!p.isNavigator?a[f].margin:0;else F.push(Math.ceil(a[0]?
a[0].createLabelsAndCalculateWidth():0));M.push(F);if(d&&0<d.length)for(z=[],f=V=0;f<d.length;f++)z.push(Math.ceil(d[f]?d[f].createLabelsAndCalculateWidth():0)),V+=z[f],W+=d[f]?d[f].margin:0;else z.push(Math.ceil(d[0]?d[0].createLabelsAndCalculateWidth():0));N.push(z);if(b&&0<b.length)for(f=0;f<b.length;f++)b[f].lineCoordinates={},k=Math.round(g.x1+U+e),h=Math.round(g.x2-V-W>p.width-10?p.width-10:g.x2-V-W),b[f].labelAutoFit&&!u(C)&&(0<!a.length&&(k=0>b[f].labelAngle?Math.max(k,C):0===b[f].labelAngle?
Math.max(k,C/2):k),0<!d.length&&(h=0<b[f].labelAngle?h-x/2:0===b[f].labelAngle?h-x/2:h)),b[f].lineCoordinates.x1=k,b[f].lineCoordinates.x2=h,b[f].lineCoordinates.width=Math.abs(h-k),b[f].title&&(b[f]._titleTextBlock.maxWidth=0<b[f].titleMaxWidth&&b[f].titleMaxWidth<b[f].lineCoordinates.width?b[f].titleMaxWidth:b[f].lineCoordinates.width);if(c&&0<c.length)for(f=0;f<c.length;f++)c[f].lineCoordinates={},k=Math.round(g.x1+U+e),h=Math.round(g.x2-V-W>c[f].chart.width-10?c[f].chart.width-10:g.x2-V-W),c[f]&&
c[f].labelAutoFit&&!u(D)&&(0<!a.length&&(k=0<c[f].labelAngle?Math.max(k,D):0===c[f].labelAngle?Math.max(k,D/2):k),0<!d.length&&(h-=y/2)),c[f].lineCoordinates.x1=k,c[f].lineCoordinates.x2=h,c[f].lineCoordinates.width=Math.abs(h-k),c[f].title&&(c[f]._titleTextBlock.maxWidth=0<c[f].titleMaxWidth&&c[f].titleMaxWidth<c[f].lineCoordinates.width?c[f].titleMaxWidth:c[f].lineCoordinates.width);if(b&&0<b.length)for(q=[],f=S=0;f<b.length;f++)q.push(Math.ceil(b[f]?b[f].createLabelsAndCalculateHeight():0)),S+=
q[f]+b[f].margin,K+=b[f].margin;else q.push(Math.ceil(b[0]?b[0].createLabelsAndCalculateHeight():0));E.push(q);if(c&&0<c.length)for(A=[],f=R=0;f<c.length;f++)A.push(Math.ceil(c[f]?c[f].createLabelsAndCalculateHeight():0)),R+=A[f],$+=c[f].margin;else A.push(Math.ceil(c[0]?c[0].createLabelsAndCalculateHeight():0));t.push(A);if(b&&0<b.length)for(f=0;f<b.length;f++)0<b[f]._labels.length&&(m=b[f]._labels[0],l=b[f]._labels[b[f]._labels.length-1],C=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+
(m.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),x=l.textBlock.width*Math.cos(Math.PI/180*Math.abs(l.textBlock.angle))+(l.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(l.textBlock.angle)));if(c&&0<c.length)for(f=0;f<c.length;f++)c[f]&&0<c[f]._labels.length&&(m=c[f]._labels[0],l=c[f]._labels[c[f]._labels.length-1],D=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/
180*Math.abs(m.textBlock.angle)),y=l.textBlock.width*Math.cos(Math.PI/180*Math.abs(l.textBlock.angle))+(l.textBlock.height-l.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(l.textBlock.angle)));if(p.panEnabled)for(f=0;f<b.length;f++)q[f]=p.sessionVariables.axisY.height;else for(f=0;f<b.length;f++)p.sessionVariables.axisY.height=q[f];if(b&&0<b.length)for(f=b.length-1;0<=f;f--)l=Math.round(g.y2),m=Math.round(g.y2>b[f].chart.height-10?b[f].chart.height-10:g.y2),b[f].lineCoordinates.y1=l-(q[f]+b[f].margin+
X),b[f].lineCoordinates.y2=l-(q[f]+b[f].margin+X),b[f].bounds={x1:k,y1:l-(q[f]+X+b[f].margin),x2:h,y2:m-(X+b[f].margin),width:h-k,height:q[f]},b[f].title&&(b[f]._titleTextBlock.maxWidth=0<b[f].titleMaxWidth&&b[f].titleMaxWidth<b[f].lineCoordinates.width?b[f].titleMaxWidth:b[f].lineCoordinates.width),X+=q[f]+b[f].margin;if(c&&0<c.length)for(f=c.length-1;0<=f;f--)c[f]&&(l=Math.round(g.y1),m=Math.round(g.y1+(A[f]+c[f].margin+G)),c[f].lineCoordinates.y1=m,c[f].lineCoordinates.y2=m,c[f].bounds={x1:k,y1:l+
(c[f].margin+G),x2:h,y2:m,width:h-k,height:R},c[f].title&&(c[f]._titleTextBlock.maxWidth=0<c[f].titleMaxWidth&&c[f].titleMaxWidth<c[f].lineCoordinates.width?c[f].titleMaxWidth:c[f].lineCoordinates.width),G+=A[f]+c[f].margin);if(a&&0<a.length)for(f=0;f<a.length;f++){K=a[f]._labels&&0<a[f]._labels.length?a[f]._labels[0].textBlock.fontSize/2:0;k=Math.round(g.x1+e);l=c&&0<c.length?Math.round(c[0]?c[0].lineCoordinates.y2:g.y1<Math.max(K,10)?Math.max(K,10):g.y1):g.y1<Math.max(K,10)?Math.max(K,10):g.y1;
h=Math.round(g.x1+U+e);m=b&&0<b.length?Math.round(b[0]?b[0].lineCoordinates.y1:g.y2-S>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0<b.length)for(K=0;K<b.length;K++)b[K]&&b[K].labelAutoFit&&(h=0>b[K].labelAngle?Math.max(h,C):0===b[K].labelAngle?Math.max(h,C/2):h,k=0>b[K].labelAngle||0===b[K].labelAngle?h-U:k);if(c&&0<c.length)for(K=0;K<c.length;K++)c[K]&&c[K].labelAutoFit&&(h=c[K].lineCoordinates.x1,k=h-U);a[f].lineCoordinates=
{x1:h-O,y1:l,x2:h-O,y2:m,height:Math.abs(m-l)};a[f].bounds={x1:h-(F[f]+O),y1:l,x2:h,y2:m,width:h-k,height:m-l};a[f].title&&(a[f]._titleTextBlock.maxWidth=0<a[f].titleMaxWidth&&a[f].titleMaxWidth<a[f].lineCoordinates.height?a[f].titleMaxWidth:a[f].lineCoordinates.height);a[f].calculateValueToPixelConversionParameters();a[f].calculateBreaksSizeInValues();O+=F[f]+a[f].margin}if(d&&0<d.length)for(f=0;f<d.length;f++){K=d[f]._labels&&0<d[f]._labels.length?d[f]._labels[0].textBlock.fontSize/2:0;k=Math.round(g.x1-
e);l=c&&0<c.length?Math.round(c[0]?c[0].lineCoordinates.y2:g.y1<Math.max(K,10)?Math.max(K,10):g.y1):g.y1<Math.max(K,10)?Math.max(K,10):g.y1;h=Math.round(g.x2-V-W);m=b&&0<b.length?Math.round(b[0]?b[0].lineCoordinates.y1:g.y2-S>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2-S):g.y2>p.height-Math.max(K,10)?p.height-Math.max(K,10):g.y2;if(b&&0<b.length)for(K=0;K<b.length;K++)b[K]&&b[K].labelAutoFit&&(h=0>b[K].labelAngle?Math.max(h,C):0===b[K].labelAngle?Math.max(h,C/2):h,k=0>b[K].labelAngle||0===
b[K].labelAngle?h-V:k);if(c&&0<c.length)for(K=0;K<c.length;K++)c[K]&&c[K].labelAutoFit&&(h=c[K].lineCoordinates.x2,k=h-V);d[f].lineCoordinates={x1:h+Q,y1:l,x2:h+Q,y2:m,height:Math.abs(m-l)};d[f].bounds={x1:k,y1:l,x2:h+z[f]+Q,y2:m,width:h-k,height:m-l};d[f].title&&(d[f]._titleTextBlock.maxWidth=0<d[f].titleMaxWidth&&d[f].titleMaxWidth<d[f].lineCoordinates.height?d[f].titleMaxWidth:d[f].lineCoordinates.height);d[f].calculateValueToPixelConversionParameters();d[f].calculateBreaksSizeInValues();Q+=z[f]+
d[f].margin}for(f=0;f<n.length;f++)"axisY"===n[f].type&&(n[f].calculateValueToPixelConversionParameters(),n[f].calculateBreaksSizeInValues());if(0<v){if(a&&0<a.length)for(f=0;f<a.length;f++)r=M[v-1][f]===M[v][f]?!0:!1;else r=!0;if(d&&0<d.length)for(f=0;f<d.length;f++)H=N[v-1][f]===N[v][f]?!0:!1;else H=!0;if(b&&0<b.length)for(f=0;f<b.length;f++)I=E[v-1][f]===E[v][f]?!0:!1;else I=!0;if(c&&0<c.length)for(f=0;f<c.length;f++)L=t[v-1][f]===t[v][f]?!0:!1;else L=!0}if(r&&H&&I&&L)break;v++}if(b&&0<b.length)for(f=
0;f<b.length;f++)b[f].calculateStripLinesThicknessInValues(),b[f].calculateBreaksInPixels();if(c&&0<c.length)for(f=0;f<c.length;f++)c[f].calculateStripLinesThicknessInValues(),c[f].calculateBreaksInPixels();if(a&&0<a.length)for(f=0;f<a.length;f++)a[f].calculateStripLinesThicknessInValues(),a[f].calculateBreaksInPixels();if(d&&0<d.length)for(f=0;f<d.length;f++)d[f].calculateStripLinesThicknessInValues(),d[f].calculateBreaksInPixels()}};z.render=function(a,d,b,c,e){var g=a[0]?a[0].chart:d[0].chart;
e=g.ctx;var k=g._axes;g.alignVerticalAxes&&g.alignVerticalAxes();e.save();e.beginPath();a[0]&&e.rect(5,a[0].bounds.y1,a[0].chart.width-10,a[0].bounds.height);d[0]&&e.rect(5,d[d.length-1].bounds.y1,d[0].chart.width-10,d[0].bounds.height);e.clip();if(a&&0<a.length)for(var l=0;l<a.length;l++)a[l].renderLabelsTicksAndTitle();if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderLabelsTicksAndTitle();e.restore();if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderLabelsTicksAndTitle();if(c&&0<c.length)for(l=
0;l<c.length;l++)c[l].renderLabelsTicksAndTitle();g.preparePlotArea();g=g.plotArea;e.save();e.beginPath();e.rect(g.x1,g.y1,Math.abs(g.x2-g.x1),Math.abs(g.y2-g.y1));e.clip();if(a&&0<a.length)for(l=0;l<k.length;l++)k[l].renderStripLinesOfThicknessType("value");if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderStripLinesOfThicknessType("value");if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderStripLinesOfThicknessType("value");if(c&&0<c.length)for(l=0;l<c.length;l++)c[l].renderStripLinesOfThicknessType("value");
if(a&&0<a.length)for(l=0;l<a.length;l++)a[l].renderInterlacedColors();if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderInterlacedColors();if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderInterlacedColors();if(c&&0<c.length)for(l=0;l<c.length;l++)c[l].renderInterlacedColors();e.restore();if(a&&0<a.length)for(l=0;l<a.length;l++)a[l].renderGrid(),r&&(a[l].createMask(),a[l].renderBreaksBackground());if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderGrid(),r&&(d[l].createMask(),d[l].renderBreaksBackground());
if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderGrid(),r&&(b[l].createMask(),b[l].renderBreaksBackground());if(c&&0<c.length)for(l=0;l<c.length;l++)c[l].renderGrid(),r&&(c[l].createMask(),c[l].renderBreaksBackground());if(a&&0<a.length)for(l=0;l<a.length;l++)a[l].renderAxisLine();if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderAxisLine();if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderAxisLine();if(c&&0<c.length)for(l=0;l<c.length;l++)c[l].renderAxisLine();if(a&&0<a.length)for(l=0;l<a.length;l++)a[l].renderStripLinesOfThicknessType("pixel");
if(d&&0<d.length)for(l=0;l<d.length;l++)d[l].renderStripLinesOfThicknessType("pixel");if(b&&0<b.length)for(l=0;l<b.length;l++)b[l].renderStripLinesOfThicknessType("pixel");if(c&&0<c.length)for(l=0;l<c.length;l++)c[l].renderStripLinesOfThicknessType("pixel")};z.prototype.calculateStripLinesThicknessInValues=function(){for(var a=0;a<this.stripLines.length;a++)if(null!==this.stripLines[a].startValue&&null!==this.stripLines[a].endValue){var d=Math.min(this.stripLines[a].startValue,this.stripLines[a].endValue),
b=Math.max(this.stripLines[a].startValue,this.stripLines[a].endValue),d=this.getApparentDifference(d,b);this.stripLines[a].value=this.logarithmic?this.stripLines[a].value*Math.sqrt(Math.log(this.stripLines[a].endValue/this.stripLines[a].startValue)/Math.log(d)):this.stripLines[a].value+(Math.abs(this.stripLines[a].endValue-this.stripLines[a].startValue)-d)/2;this.stripLines[a].thickness=d;this.stripLines[a]._thicknessType="value"}};z.prototype.calculateBreaksSizeInValues=function(){for(var a="left"===
this._position||"right"===this._position?this.lineCoordinates.height||this.chart.height:this.lineCoordinates.width||this.chart.width,d=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[],b=this.conversionParameters.pixelPerUnit||a/(this.logarithmic?this.conversionParameters.maximum/this.conversionParameters.minimum:this.conversionParameters.maximum-this.conversionParameters.minimum),c=this.scaleBreaks&&!u(this.scaleBreaks.options.spacing),e,g=0;g<d.length;g++)e=c||!u(d[g].options.spacing),d[g].spacing=
I(d[g].spacing,a,8,e?0.1*a:8,e?0:3)<<0,d[g].size=0>d[g].spacing?0:Math.abs(d[g].spacing/b),this.logarithmic&&(d[g].size=Math.pow(this.logarithmBase,d[g].size))};z.prototype.calculateBreaksInPixels=function(){if(!(this.scaleBreaks&&0>=this.scaleBreaks._appliedBreaks.length)){var a=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];a.length&&(this.scaleBreaks.firstBreakIndex=this.scaleBreaks.lastBreakIndex=null);for(var d=0;d<a.length&&!(a[d].startValue>this.conversionParameters.maximum);d++)a[d].endValue<
this.conversionParameters.minimum||(u(this.scaleBreaks.firstBreakIndex)&&(this.scaleBreaks.firstBreakIndex=d),a[d].startValue>=this.conversionParameters.minimum&&(a[d].startPixel=this.convertValueToPixel(a[d].startValue),this.scaleBreaks.lastBreakIndex=d),a[d].endValue<=this.conversionParameters.maximum&&(a[d].endPixel=this.convertValueToPixel(a[d].endValue)))}};z.prototype.renderLabelsTicksAndTitle=function(){var a=this,d=!1,b=0,c=0,e=1,g=0;0!==this.labelAngle&&360!==this.labelAngle&&(e=1.2);if("undefined"===
typeof this.options.interval){if("bottom"===this._position||"top"===this._position)if(this.logarithmic&&!this.equidistantInterval&&this.labelAutoFit){for(var b=[],e=0!==this.labelAngle&&360!==this.labelAngle?1:1.2,k,l=this.viewportMaximum,h=this.lineCoordinates.width/Math.log(this.range),m=this._labels.length-1;0<=m;m--){q=this._labels[m];if(q.position<this.viewportMinimum)break;q.position>this.viewportMaximum||!(m===this._labels.length-1||k<Math.log(l/q.position)*h/e)||(b.push(q),l=q.position,k=
q.textBlock.width*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+q.textBlock.height*Math.abs(Math.sin(Math.PI/180*this.labelAngle)))}this._labels=b}else{for(m=0;m<this._labels.length;m++)q=this._labels[m],q.position<this.viewportMinimum||(k=q.textBlock.width*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+q.textBlock.height*Math.abs(Math.sin(Math.PI/180*this.labelAngle)),b+=k);b>this.lineCoordinates.width*e&&this.labelAutoFit&&(d=!0)}if("left"===this._position||"right"===this._position)if(this.logarithmic&&
!this.equidistantInterval&&this.labelAutoFit){for(var b=[],p,l=this.viewportMaximum,h=this.lineCoordinates.height/Math.log(this.range),m=this._labels.length-1;0<=m;m--){q=this._labels[m];if(q.position<this.viewportMinimum)break;q.position>this.viewportMaximum||!(m===this._labels.length-1||p<Math.log(l/q.position)*h)||(b.push(q),l=q.position,p=q.textBlock.height*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+q.textBlock.width*Math.abs(Math.sin(Math.PI/180*this.labelAngle)))}this._labels=b}else{for(m=
0;m<this._labels.length;m++)q=this._labels[m],q.position<this.viewportMinimum||(p=q.textBlock.height*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+q.textBlock.width*Math.abs(Math.sin(Math.PI/180*this.labelAngle)),c+=p);c>this.lineCoordinates.height*e&&this.labelAutoFit&&(d=!0)}}this.logarithmic&&(!this.equidistantInterval&&this.labelAutoFit)&&this._labels.sort(function(a,b){return a.position-b.position});var m=0,q,n;if("bottom"===this._position){for(m=0;m<this._labels.length;m++)q=this._labels[m],
q.position<this.viewportMinimum||(q.position>this.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y+this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement?
n.y-(this.tickLength+q.textBlock.fontSize/2):n.y+this.tickLength+q.textBlock.fontSize/2):(n.x="inside"===this.labelPlacement?0>this.labelAngle?n.x:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):n.x-(0>this.labelAngle?q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),n.y="inside"===this.labelPlacement?0>this.labelAngle?n.y-this.tickLength-5:n.y-this.tickLength-Math.abs(q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)+5):n.y+this.tickLength+Math.abs(0>this.labelAngle?q.textBlock.width*
Math.sin(Math.PI/180*this.labelAngle)-5:5)),q.textBlock.x=n.x,q.textBlock.y=n.y);"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(m=0;m<a._labels.length;m++)if(q=a._labels[m],!(q.position<a.viewportMinimum||q.position>a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save();
a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y-a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y2-this._titleTextBlock.height-3,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("top"===this._position){for(m=0;m<this._labels.length;m++)q=this._labels[m],
q.position<this.viewportMinimum||(q.position>this.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0,this.ctx.beginPath(),this.ctx.moveTo(c,n.y<<0),this.ctx.lineTo(c,n.y-this.tickLength<<0),this.ctx.stroke()),0===q.textBlock.angle?(n.x-=q.textBlock.width/2,n.y="inside"===this.labelPlacement?
n.y+this.labelFontSize/2+this.tickLength+5:n.y-(this.tickLength+q.textBlock.height-q.textBlock.fontSize/2)):(n.x="inside"===this.labelPlacement?0<this.labelAngle?n.x:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):n.x+(q.textBlock.height-this.tickLength-this.labelFontSize)*Math.sin(Math.PI/180*this.labelAngle)-(0<this.labelAngle?q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),n.y="inside"===this.labelPlacement?0<this.labelAngle?n.y+this.tickLength+5:n.y-q.textBlock.width*Math.sin(Math.PI/
180*this.labelAngle)+this.tickLength+5:n.y-(this.tickLength+((q.textBlock.height-q.textBlock.fontSize/2)*Math.cos(Math.PI/180*this.labelAngle)+(0<this.labelAngle?q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0)))),q.textBlock.x=n.x,q.textBlock.y=n.y);"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(m=0;m<a._labels.length;m++)if(q=a._labels[m],!(q.position<a.viewportMinimum||q.position>a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&
(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===this.ctx.lineWidth%2?(n.x<<0)+0.5:n.x<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(b,n.y<<0);a.ctx.lineTo(b,n.y+a.tickLength<<0);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.lineCoordinates.x1+this.lineCoordinates.width/2-this._titleTextBlock.width/2,this._titleTextBlock.y=this.bounds.y1+1,this.titleMaxWidth=
this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("left"===this._position){for(m=0;m<this._labels.length;m++)q=this._labels[m],q.position<this.viewportMinimum||(q.position>this.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<<
0,c),this.ctx.lineTo(n.x-this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle?(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5):(q.textBlock.y="inside"===this.labelPlacement?n.y:n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x+this.tickLength+5:0<this.labelAngle?n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-
this.tickLength-5:n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)+(q.textBlock.height-q.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)-this.tickLength));"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(m=0;m<a._labels.length;m++)if(q=a._labels[m],!(q.position<a.viewportMinimum||q.position>a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;
a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x+a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x1+1,this._titleTextBlock.y=this.lineCoordinates.height/2+this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}else if("right"===
this._position){for(m=0;m<this._labels.length;m++)q=this._labels[m],q.position<this.viewportMinimum||(q.position>this.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||(n=this.getPixelCoordinatesOnAxis(q.position),this.tickThickness&&"inside"!=this.labelPlacement&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,c=1===this.ctx.lineWidth%2?(n.y<<0)+0.5:n.y<<0,this.ctx.beginPath(),this.ctx.moveTo(n.x<<0,c),this.ctx.lineTo(n.x+this.tickLength<<0,c),this.ctx.stroke()),0===this.labelAngle?
(q.textBlock.y=n.y,q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width-this.tickLength-5:n.x+this.tickLength+5):(q.textBlock.y="inside"===this.labelPlacement?n.y-q.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0>this.labelAngle?n.y:n.y-(q.textBlock.height-q.textBlock.fontSize/2-5)*Math.cos(Math.PI/180*this.labelAngle),q.textBlock.x="inside"===this.labelPlacement?n.x-q.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:0<this.labelAngle?n.x+(q.textBlock.height-
q.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)+this.tickLength:n.x+this.tickLength+5));"inside"===this.labelPlacement&&this.chart.addEventListener("dataAnimationIterationEnd",function(){for(m=0;m<a._labels.length;m++)if(q=a._labels[m],!(q.position<a.viewportMinimum||q.position>a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)&&(n=a.getPixelCoordinatesOnAxis(q.position),a.tickThickness)){a.ctx.lineWidth=a.tickThickness;a.ctx.strokeStyle=a.tickColor;var b=1===a.ctx.lineWidth%2?(n.y<<
0)+0.5:n.y<<0;a.ctx.save();a.ctx.beginPath();a.ctx.moveTo(n.x<<0,b);a.ctx.lineTo(n.x-a.tickLength<<0,b);a.ctx.stroke();a.ctx.restore()}},this);this.title&&(this._titleTextBlock.measureText(),this._titleTextBlock.x=this.bounds.x2-1,this._titleTextBlock.y=this.lineCoordinates.height/2-this._titleTextBlock.width/2+this.lineCoordinates.y1,this.titleMaxWidth=this._titleTextBlock.maxWidth,this._titleTextBlock.render(!0))}g=0;if("inside"===this.labelPlacement)this.chart.addEventListener("dataAnimationIterationEnd",
function(){for(m=0;m<a._labels.length;m++)q=a._labels[m],q.position<a.viewportMinimum||(q.position>a.viewportMaximum||d&&0!==g++%2&&a.labelAutoFit)||(a.ctx.save(),a.ctx.beginPath(),q.textBlock.render(!0),a.ctx.restore())},this);else for(m=0;m<this._labels.length;m++)q=this._labels[m],q.position<this.viewportMinimum||(q.position>this.viewportMaximum||d&&0!==g++%2&&this.labelAutoFit)||q.textBlock.render(!0)};z.prototype.renderInterlacedColors=function(){var a=this.chart.plotArea.ctx,d,b,c=this.chart.plotArea,
e=0;d=!0;if(("bottom"===this._position||"top"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,e=0;e<this._labels.length;e++)d?(d=this.getPixelCoordinatesOnAxis(this._labels[e].position),b=e+1>this._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(Math.min(b.x,d.x),c.y1,Math.abs(b.x-d.x),Math.abs(c.y1-c.y2)),d=!1):d=!0;else if(("left"===this._position||"right"===this._position)&&this.interlacedColor)for(a.fillStyle=
this.interlacedColor,e=0;e<this._labels.length;e++)d?(b=this.getPixelCoordinatesOnAxis(this._labels[e].position),d=e+1>this._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[e+1].position),a.fillRect(c.x1,Math.min(b.y,d.y),Math.abs(c.x1-c.x2),Math.abs(d.y-b.y)),d=!1):d=!0;a.beginPath()};z.prototype.renderStripLinesOfThicknessType=function(a){if(this.stripLines&&0<this.stripLines.length&&a){for(var d=this,b,c=0,e=0,g=!1,k=!1,l=[],h=[],
k=!1,c=0;c<this.stripLines.length;c++){var m=this.stripLines[c];m._thicknessType===a&&("pixel"===a&&(m.value<this.viewportMinimum||m.value>this.viewportMaximum||u(m.value)||isNaN(this.range))||l.push(m))}for(c=0;c<this._stripLineLabels.length;c++)if(m=this.stripLines[c],b=this._stripLineLabels[c],!(b.position<this.viewportMinimum||b.position>this.viewportMaximum||isNaN(this.range))){a=this.getPixelCoordinatesOnAxis(b.position);if("outside"===b.stripLine.labelPlacement)if(m&&(this.ctx.strokeStyle=
m.color,"pixel"===m._thicknessType&&(this.ctx.lineWidth=m.thickness)),"bottom"===this._position){var p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0;this.ctx.beginPath();this.ctx.moveTo(p,a.y<<0);this.ctx.lineTo(p,a.y+this.tickLength<<0);this.ctx.stroke();0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y+=this.tickLength+b.textBlock.fontSize/2):(a.x-=0>this.labelAngle?b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,a.y+=this.tickLength+Math.abs(0>this.labelAngle?b.textBlock.width*Math.sin(Math.PI/
180*this.labelAngle)-5:5))}else"top"===this._position?(p=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0,this.ctx.beginPath(),this.ctx.moveTo(p,a.y<<0),this.ctx.lineTo(p,a.y-this.tickLength<<0),this.ctx.stroke(),0===this.labelAngle?(a.x-=b.textBlock.width/2,a.y-=this.tickLength+b.textBlock.height):(a.x+=(b.textBlock.height-this.tickLength-this.labelFontSize/2)*Math.sin(Math.PI/180*this.labelAngle)-(0<this.labelAngle?b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),a.y-=this.tickLength+(b.textBlock.height*
Math.cos(Math.PI/180*this.labelAngle)+(0<this.labelAngle?b.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0)))):"left"===this._position?(p=1===this.ctx.lineWidth%2?(a.y<<0)+0.5:a.y<<0,this.ctx.beginPath(),this.ctx.moveTo(a.x<<0,p),this.ctx.lineTo(a.x-this.tickLength<<0,p),this.ctx.stroke(),0===this.labelAngle?a.x=a.x-b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:(a.y-=b.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),a.x=0<this.labelAngle?a.x-b.textBlock.width*
Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:a.x-b.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)+(b.textBlock.height-b.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)-this.tickLength)):"right"===this._position&&(p=1===this.ctx.lineWidth%2?(a.y<<0)+0.5:a.y<<0,this.ctx.beginPath(),this.ctx.moveTo(a.x<<0,p),this.ctx.lineTo(a.x+this.tickLength<<0,p),this.ctx.stroke(),0===this.labelAngle?a.x=a.x+this.tickLength+5:(a.y=0>this.labelAngle?a.y:a.y-(b.textBlock.height-b.textBlock.fontSize/
2-5)*Math.cos(Math.PI/180*this.labelAngle),a.x=0<this.labelAngle?a.x+(b.textBlock.height-b.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)+this.tickLength:a.x+this.tickLength+5));else b.textBlock.angle=-90,"bottom"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?u(m.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/
2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(m.startValue)?a.x+=b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y1+b.textBlock.width+3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y2-b.textBlock.width-3:"center"===
b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y1+3):"top"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?u(m.startValue)?a.x-=b.textBlock.height-b.textBlock.fontSize/2:a.x-=b.textBlock.height/2-b.textBlock.fontSize/2+3:(b.textBlock.angle=90,u(m.startValue)?a.x+=
b.textBlock.height-b.textBlock.fontSize/2:a.x+=b.textBlock.height/2-b.textBlock.fontSize/2+3),a.y=-90===b.textBlock.angle?"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+b.textBlock.width+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1+b.textBlock.width)/2:this.chart.plotArea.y2-3:"near"===b.stripLine.labelAlign?this.chart.plotArea.y1+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.y2+this.chart.plotArea.y1-b.textBlock.width)/2:this.chart.plotArea.y2-
b.textBlock.width-3):"left"===this._position?(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-b.textBlock.height>this.chart.plotArea.y1?u(m.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize+3:a.y-b.textBlock.height<this.chart.plotArea.y2?a.y+=b.textBlock.fontSize/2+3:u(m.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/
2:a.y-=b.textBlock.height/2-b.textBlock.fontSize+3,a.x="near"===b.stripLine.labelAlign?this.chart.plotArea.x1+3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.x2+this.chart.plotArea.x1)/2-b.textBlock.width/2:this.chart.plotArea.x2-b.textBlock.width-3):"right"===this._position&&(b.textBlock.maxWidth=this.options.stripLines[c].labelMaxWidth?this.options.stripLines[c].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-+b.textBlock.height>this.chart.plotArea.y1?
u(m.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize/2-3:a.y-b.textBlock.height<this.chart.plotArea.y2?a.y+=b.textBlock.fontSize/2+3:u(m.startValue)?a.y-=b.textBlock.height-b.textBlock.fontSize/2:a.y-=b.textBlock.height/2-b.textBlock.fontSize/2+3,a.x="near"===b.stripLine.labelAlign?this.chart.plotArea.x2-b.textBlock.width-3:"center"===b.stripLine.labelAlign?(this.chart.plotArea.x2+this.chart.plotArea.x1)/2-b.textBlock.width/2:this.chart.plotArea.x1+
3);b.textBlock.x=a.x;b.textBlock.y=a.y;h.push(b)}if(!k){k=!1;this.ctx.save();this.ctx.beginPath();this.ctx.rect(this.chart.plotArea.x1,this.chart.plotArea.y1,this.chart.plotArea.width,this.chart.plotArea.height);this.ctx.clip();for(c=0;c<l.length;c++)m=l[c],m.showOnTop?g||(g=!0,this.chart.addEventListener("dataAnimationIterationEnd",function(){this.ctx.save();this.ctx.beginPath();this.ctx.rect(this.chart.plotArea.x1,this.chart.plotArea.y1,this.chart.plotArea.width,this.chart.plotArea.height);this.ctx.clip();
for(e=0;e<l.length;e++)m=l[e],m.showOnTop&&m.render();this.ctx.restore()},m)):m.render();for(c=0;c<h.length;c++)b=h[c],b.stripLine.showOnTop?k||(k=!0,this.chart.addEventListener("dataAnimationIterationEnd",function(){for(e=0;e<h.length;e++)b=h[e],"inside"===b.stripLine.labelPlacement&&b.stripLine.showOnTop&&(d.ctx.save(),d.ctx.beginPath(),d.ctx.rect(d.chart.plotArea.x1,d.chart.plotArea.y1,d.chart.plotArea.width,d.chart.plotArea.height),d.ctx.clip(),b.textBlock.render(!0),d.ctx.restore())},b.textBlock)):
"inside"===b.stripLine.labelPlacement&&b.textBlock.render(!0);this.ctx.restore();k=!0}if(k)for(k=!1,c=0;c<h.length;c++)b=h[c],b.stripLine.showOnTop?k||(k=!0,this.chart.addEventListener("dataAnimationIterationEnd",function(){for(e=0;e<h.length;e++)b=h[e],"outside"===b.stripLine.labelPlacement&&b.stripLine.showOnTop&&b.textBlock.render(!0)},b.textBlock)):"outside"===b.stripLine.labelPlacement&&b.textBlock.render(!0)}};z.prototype.renderBreaksBackground=function(){this.chart._breaksCanvas&&(this.scaleBreaks&&
0<this.scaleBreaks._appliedBreaks.length&&this.maskCanvas)&&(this.chart._breaksCanvasCtx.save(),this.chart._breaksCanvasCtx.beginPath(),this.chart._breaksCanvasCtx.rect(this.chart.plotArea.x1,this.chart.plotArea.y1,this.chart.plotArea.width,this.chart.plotArea.height),this.chart._breaksCanvasCtx.clip(),this.chart._breaksCanvasCtx.drawImage(this.maskCanvas,0,0,this.chart.width,this.chart.height),this.chart._breaksCanvasCtx.restore())};z.prototype.createMask=function(){if(this.scaleBreaks&&0<this.scaleBreaks._appliedBreaks.length){var a=
this.scaleBreaks._appliedBreaks;r?(this.maskCanvas=ta(this.chart.width,this.chart.height),this.maskCtx=this.maskCanvas.getContext("2d")):(this.maskCanvas=this.chart.plotArea.canvas,this.maskCtx=this.chart.plotArea.ctx);this.maskCtx.save();this.maskCtx.beginPath();this.maskCtx.rect(this.chart.plotArea.x1,this.chart.plotArea.y1,this.chart.plotArea.width,this.chart.plotArea.height);this.maskCtx.clip();for(var d=0;d<a.length;d++)a[d].endValue<this.viewportMinimum||(a[d].startValue>this.viewportMaximum||
isNaN(this.range))||a[d].render(this.maskCtx);this.maskCtx.restore()}};z.prototype.renderCrosshair=function(a,d){this.crosshair.render(a,d)};z.prototype.renderGrid=function(){if(this.gridThickness&&0<this.gridThickness){var a=this.chart.ctx;a.save();var d,b=this.chart.plotArea;a.lineWidth=this.gridThickness;a.strokeStyle=this.gridColor;a.setLineDash&&a.setLineDash(R(this.gridDashType,this.gridThickness));if("bottom"===this._position||"top"===this._position)for(c=0;c<this._labels.length;c++)this._labels[c].position<
this.viewportMinimum||(this._labels[c].position>this.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(),d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.x<<0)+0.5:d.x<<0,a.moveTo(d,b.y1<<0),a.lineTo(d,b.y2<<0),a.stroke());else if("left"===this._position||"right"===this._position)for(var c=0;c<this._labels.length;c++)this._labels[c].position<this.viewportMinimum||(this._labels[c].position>this.viewportMaximum||this._labels[c].breaksLabelType)||(a.beginPath(),
d=this.getPixelCoordinatesOnAxis(this._labels[c].position),d=1===a.lineWidth%2?(d.y<<0)+0.5:d.y<<0,a.moveTo(b.x1<<0,d),a.lineTo(b.x2<<0,d),a.stroke());a.restore()}};z.prototype.renderAxisLine=function(){var a=this.chart.ctx,d=r?this.chart._preRenderCtx:a,b=Math.ceil(this.tickThickness/(this.reversed?-2:2)),c=Math.ceil(this.tickThickness/(this.reversed?2:-2)),e,g;d.save();if("bottom"===this._position||"top"===this._position){if(this.lineThickness){this.reversed?(e=this.lineCoordinates.x2,g=this.lineCoordinates.x1):
(e=this.lineCoordinates.x1,g=this.lineCoordinates.x2);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor?this.lineColor:"black";d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));var k=1===this.lineThickness%2?(this.lineCoordinates.y1<<0)+0.5:this.lineCoordinates.y1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+c;else for(var l=
this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(e,k),d.lineTo(this.scaleBreaks._appliedBreaks[l].startPixel+b,k),e=this.scaleBreaks._appliedBreaks[l].endPixel+c;e&&(d.moveTo(e,k),d.lineTo(g,k));d.stroke()}}else if(("left"===this._position||"right"===this._position)&&this.lineThickness){this.reversed?(e=this.lineCoordinates.y1,g=this.lineCoordinates.y2):(e=this.lineCoordinates.y2,g=this.lineCoordinates.y1);d.lineWidth=this.lineThickness;d.strokeStyle=this.lineColor;
d.setLineDash&&d.setLineDash(R(this.lineDashType,this.lineThickness));k=1===this.lineThickness%2?(this.lineCoordinates.x1<<0)+0.5:this.lineCoordinates.x1<<0;d.beginPath();if(this.scaleBreaks&&!u(this.scaleBreaks.firstBreakIndex))if(u(this.scaleBreaks.lastBreakIndex))e=this.scaleBreaks._appliedBreaks[this.scaleBreaks.firstBreakIndex].endPixel+b;else for(l=this.scaleBreaks.firstBreakIndex;l<=this.scaleBreaks.lastBreakIndex;l++)d.moveTo(k,e),d.lineTo(k,this.scaleBreaks._appliedBreaks[l].startPixel+c),
e=this.scaleBreaks._appliedBreaks[l].endPixel+b;e&&(d.moveTo(k,e),d.lineTo(k,g));d.stroke()}r&&(a.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),this.chart._breaksCanvasCtx&&this.chart._breaksCanvasCtx.drawImage(this.chart._preRenderCanvas,0,0,this.chart.width,this.chart.height),d.clearRect(0,0,this.chart.width,this.chart.height));d.restore()};z.prototype.getPixelCoordinatesOnAxis=function(a){var d={};if("bottom"===this._position||"top"===this._position)d.x=this.convertValueToPixel(a),
d.y=this.lineCoordinates.y1;if("left"===this._position||"right"===this._position)d.y=this.convertValueToPixel(a),d.x=this.lineCoordinates.x2;return d};z.prototype.convertPixelToValue=function(a){if("undefined"===typeof a)return null;var d=0,b=0,c,d=!0,e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[],b="number"===typeof a?a:"left"===this._position||"right"===this._position?a.y:a.x;if(this.logarithmic){a=c=Math.pow(this.logarithmBase,(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit);
if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;b<e.length;b++){if(!(e[b].endValue<this.conversionParameters.minimum))if(d)if(e[b].startValue<this.conversionParameters.minimum){if(1<e[b].size&&this.conversionParameters.minimum*Math.pow(e[b].endValue/e[b].startValue,Math.log(c)/Math.log(e[b].size))<e[b].endValue){a=Math.pow(e[b].endValue/e[b].startValue,Math.log(c)/Math.log(e[b].size));break}else a*=e[b].endValue/this.conversionParameters.minimum/
Math.pow(e[b].size,Math.log(e[b].endValue/this.conversionParameters.minimum)/Math.log(e[b].endValue/e[b].startValue)),c/=Math.pow(e[b].size,Math.log(e[b].endValue/this.conversionParameters.minimum)/Math.log(e[b].endValue/e[b].startValue));d=!1}else if(c>e[b].startValue/this.conversionParameters.minimum){c/=e[b].startValue/this.conversionParameters.minimum;if(c<e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1===e[b].size?1:Math.log(c)/Math.log(e[b].size))/c;break}else a*=e[b].endValue/e[b].startValue/
e[b].size;c/=e[b].size;d=!1}else break;else if(c>e[b].startValue/e[b-1].endValue){c/=e[b].startValue/e[b-1].endValue;if(c<e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1===e[b].size?1:Math.log(c)/Math.log(e[b].size))/c;break}else a*=e[b].endValue/e[b].startValue/e[b].size;c/=e[b].size}else break}else for(b=e.length-1;0<=b;b--)if(!(e[b].startValue>this.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum){if(1<e[b].size&&this.conversionParameters.minimum*Math.pow(e[b].endValue/
e[b].startValue,Math.log(c)/Math.log(e[b].size))>e[b].startValue){a=Math.pow(e[b].endValue/e[b].startValue,Math.log(c)/Math.log(e[b].size));break}else a*=e[b].startValue/this.conversionParameters.minimum*Math.pow(e[b].size,Math.log(e[b].startValue/this.conversionParameters.minimum)/Math.log(e[b].endValue/e[b].startValue))*c,c*=Math.pow(e[b].size,Math.log(this.conversionParameters.minimum/e[b].startValue)/Math.log(e[b].endValue/e[b].startValue));d=!1}else if(c<e[b].endValue/this.conversionParameters.minimum){c/=
e[b].endValue/this.conversionParameters.minimum;if(c>1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size;d=!1}else break;else if(c<e[b].endValue/e[b+1].startValue){c/=e[b].endValue/e[b+1].startValue;if(c>1/e[b].size){a*=Math.pow(e[b].endValue/e[b].startValue,1>=e[b].size?1:Math.log(c)/Math.log(e[b].size))*c;break}else a/=e[b].endValue/e[b].startValue/e[b].size;c*=e[b].size}else break;
d=a*this.viewportMinimum}else{a=c=(b-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit;if(b<=this.conversionParameters.reference===("left"===this._position||"right"===this._position)!==this.reversed)for(b=0;b<e.length;b++){if(!(e[b].endValue<this.conversionParameters.minimum))if(d)if(e[b].startValue<this.conversionParameters.minimum){if(e[b].size&&this.conversionParameters.minimum+c*(e[b].endValue-e[b].startValue)/e[b].size<e[b].endValue){a=0>=e[b].size?0:c*(e[b].endValue-
e[b].startValue)/e[b].size;break}else a+=e[b].endValue-this.conversionParameters.minimum-e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue),c-=e[b].size*(e[b].endValue-this.conversionParameters.minimum)/(e[b].endValue-e[b].startValue);d=!1}else if(c>e[b].startValue-this.conversionParameters.minimum){c-=e[b].startValue-this.conversionParameters.minimum;if(c<e[b].size){a+=(e[b].endValue-e[b].startValue)*(0===e[b].size?1:c/e[b].size)-c;break}else a+=e[b].endValue-
e[b].startValue-e[b].size;c-=e[b].size;d=!1}else break;else if(c>e[b].startValue-e[b-1].endValue){c-=e[b].startValue-e[b-1].endValue;if(c<e[b].size){a+=(e[b].endValue-e[b].startValue)*(0===e[b].size?1:c/e[b].size)-c;break}else a+=e[b].endValue-e[b].startValue-e[b].size;c-=e[b].size}else break}else for(b=e.length-1;0<=b;b--)if(!(e[b].startValue>this.conversionParameters.minimum))if(d)if(e[b].endValue>this.conversionParameters.minimum)if(e[b].size&&this.conversionParameters.minimum+c*(e[b].endValue-
e[b].startValue)/e[b].size>e[b].startValue){a=0>=e[b].size?0:c*(e[b].endValue-e[b].startValue)/e[b].size;break}else a+=e[b].startValue-this.conversionParameters.minimum+e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),c+=e[b].size*(this.conversionParameters.minimum-e[b].startValue)/(e[b].endValue-e[b].startValue),d=!1;else if(c<e[b].endValue-this.conversionParameters.minimum){c-=e[b].endValue-this.conversionParameters.minimum;if(c>-1*e[b].size){a+=(e[b].endValue-
e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size;d=!1}else break;else if(c<e[b].endValue-e[b+1].startValue){c-=e[b].endValue-e[b+1].startValue;if(c>-1*e[b].size){a+=(e[b].endValue-e[b].startValue)*(0===e[b].size?1:c/e[b].size)+c;break}else a-=e[b].endValue-e[b].startValue-e[b].size;c+=e[b].size}else break;d=this.conversionParameters.minimum+a}return d};z.prototype.convertValueToPixel=function(a){a=this.getApparentDifference(this.conversionParameters.minimum,
a,a);return this.logarithmic?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*Math.log(a/this.conversionParameters.minimum)/this.conversionParameters.lnLogarithmBase+0.5<<0:"axisX"===this.type?this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5<<0:this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5};z.prototype.getApparentDifference=function(a,
d,b,c){var e=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];if(this.logarithmic){b=u(b)?d/a:b;for(var g=0;g<e.length&&!(d<e[g].startValue);g++)a>e[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*e[g].startValue*e[g].size:a>=e[g].startValue&&d>=e[g].endValue?b=b/e[g].endValue*a*Math.pow(e[g].size,Math.log(e[g].endValue/a)/Math.log(e[g].endValue/e[g].startValue)):a<=e[g].startValue&&d<=e[g].endValue?b=b/d*e[g].startValue*Math.pow(e[g].size,Math.log(d/e[g].startValue)/Math.log(e[g].endValue/
e[g].startValue)):!c&&(a>e[g].startValue&&d<e[g].endValue)&&(b=a*Math.pow(e[g].size,Math.log(d/a)/Math.log(e[g].endValue/e[g].startValue))))}else for(b=u(b)?Math.abs(d-a):b,g=0;g<e.length&&!(d<e[g].startValue);g++)a>e[g].endValue||(a<=e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+e[g].startValue+e[g].size:a>e[g].startValue&&d>=e[g].endValue?b=b-e[g].endValue+a+e[g].size*(e[g].endValue-a)/(e[g].endValue-e[g].startValue):a<=e[g].startValue&&d<e[g].endValue?b=b-d+e[g].startValue+e[g].size*(d-e[g].startValue)/
(e[g].endValue-e[g].startValue):!c&&(a>e[g].startValue&&d<e[g].endValue)&&(b=a+e[g].size*(d-a)/(e[g].endValue-e[g].startValue)));return b};z.prototype.setViewPortRange=function(a,d){this.sessionVariables.newViewportMinimum=this.viewportMinimum=Math.min(a,d);this.sessionVariables.newViewportMaximum=this.viewportMaximum=Math.max(a,d)};z.prototype.getXValueAt=function(a){if(!a)return null;var d=null;"left"===this._position?d=this.convertPixelToValue(a.y):"bottom"===this._position&&(d=this.convertPixelToValue(a.x));
return d};z.prototype.calculateValueToPixelConversionParameters=function(a){a=this.scaleBreaks?this.scaleBreaks._appliedBreaks:[];var d={pixelPerUnit:null,minimum:null,reference:null},b=this.lineCoordinates.width,c=this.lineCoordinates.height,b="bottom"===this._position||"top"===this._position?b:c,c=Math.abs(this.range);if(this.logarithmic)for(var e=0;e<a.length&&!(this.viewportMaximum<a[e].startValue);e++)this.viewportMinimum>a[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<=
a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*a[e].startValue,b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100):b-Math.min(a[e].spacing,0.1*b)):this.viewportMinimum>a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c/a[e].endValue*this.viewportMinimum,b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100*Math.log(a[e].endValue/this.viewportMinimum)/Math.log(a[e].endValue/a[e].startValue)):
b-Math.min(a[e].spacing,0.1*b)*Math.log(a[e].endValue/this.viewportMinimum)/Math.log(a[e].endValue/a[e].startValue)):this.viewportMinimum<=a[e].startValue&&this.viewportMaximum<a[e].endValue&&(c=c/this.viewportMaximum*a[e].startValue,b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100*Math.log(this.viewportMaximum/a[e].startValue)/Math.log(a[e].endValue/a[e].startValue)):b-Math.min(a[e].spacing,0.1*b)*Math.log(this.viewportMaximum/a[e].startValue)/Math.log(a[e].endValue/a[e].startValue)));
else for(e=0;e<a.length&&!(this.viewportMaximum<a[e].startValue);e++)this.viewportMinimum>a[e].endValue||(this.viewportMinimum>=a[e].startValue&&this.viewportMaximum<=a[e].endValue?b=0:this.viewportMinimum<=a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+a[e].startValue,b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100):b-Math.min(a[e].spacing,0.1*b)):this.viewportMinimum>a[e].startValue&&this.viewportMaximum>=a[e].endValue?(c=c-a[e].endValue+this.viewportMinimum,
b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100*(a[e].endValue-this.viewportMinimum)/(a[e].endValue-a[e].startValue)):b-Math.min(a[e].spacing,0.1*b)*(a[e].endValue-this.viewportMinimum)/(a[e].endValue-a[e].startValue)):this.viewportMinimum<=a[e].startValue&&this.viewportMaximum<a[e].endValue&&(c=c-this.viewportMaximum+a[e].startValue,b=0<a[e].spacing.toString().indexOf("%")?b*(1-parseFloat(a[e].spacing)/100*(this.viewportMaximum-a[e].startValue)/(a[e].endValue-a[e].startValue)):
b-Math.min(a[e].spacing,0.1*b)*(this.viewportMaximum-a[e].startValue)/(a[e].endValue-a[e].startValue)));d.minimum=this.viewportMinimum;d.maximum=this.viewportMaximum;d.range=c;if("bottom"===this._position||"top"===this._position)this.logarithmic?(d.lnLogarithmBase=Math.log(this.logarithmBase),d.pixelPerUnit=(this.reversed?-1:1)*b*d.lnLogarithmBase/Math.log(Math.abs(c))):d.pixelPerUnit=(this.reversed?-1:1)*b/Math.abs(c),d.reference=this.reversed?this.lineCoordinates.x2:this.lineCoordinates.x1;if("left"===
this._position||"right"===this._position)this.logarithmic?(d.lnLogarithmBase=Math.log(this.logarithmBase),d.pixelPerUnit=(this.reversed?1:-1)*b*d.lnLogarithmBase/Math.log(Math.abs(c))):d.pixelPerUnit=(this.reversed?1:-1)*b/Math.abs(c),d.reference=this.reversed?this.lineCoordinates.y1:this.lineCoordinates.y2;this.conversionParameters=d};z.prototype.calculateAxisParameters=function(){if(this.logarithmic)this.calculateLogarithmicAxisParameters();else{var a=this.chart.layoutManager.getFreeSpace(),d=!1,
b=!1;"bottom"===this._position||"top"===this._position?(this.maxWidth=a.width,this.maxHeight=a.height):(this.maxWidth=a.height,this.maxHeight=a.width);var a="axisX"===this.type?"xySwapped"===this.chart.plotInfo.axisPlacement?62:70:"xySwapped"===this.chart.plotInfo.axisPlacement?50:40,c=4;"axisX"===this.type&&(c=600>this.maxWidth?8:6);var a=Math.max(c,Math.floor(this.maxWidth/a)),e,g,k,c=0;!u(this.options.viewportMinimum)&&(!u(this.options.viewportMaximum)&&this.options.viewportMinimum>=this.options.viewportMaximum)&&
(this.viewportMinimum=this.viewportMaximum=null);if(u(this.options.viewportMinimum)&&!u(this.sessionVariables.newViewportMinimum)&&!isNaN(this.sessionVariables.newViewportMinimum))this.viewportMinimum=this.sessionVariables.newViewportMinimum;else if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(u(this.options.viewportMaximum)&&!u(this.sessionVariables.newViewportMaximum)&&!isNaN(this.sessionVariables.newViewportMaximum))this.viewportMaximum=this.sessionVariables.newViewportMaximum;
else if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(c=0;c<this.scaleBreaks._appliedBreaks.length;c++)if((!u(this.sessionVariables.newViewportMinimum)&&this.sessionVariables.newViewportMinimum>=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[c].startValue||!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[c].startValue)&&
(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[c].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[c].endValue)){this.scaleBreaks._appliedBreaks.splice(c,1);break}if("axisX"===this.type){if(this.dataSeries&&0<this.dataSeries.length)for(e=0;e<this.dataSeries.length;e++)"dateTime"===this.dataSeries[e].xValueType&&
(b=!0);e=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin;g=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax;0===g-e&&(c="undefined"===typeof this.options.interval?0.4:this.options.interval,g+=c,e-=c);Infinity!==this.dataInfo.minDiff?k=this.dataInfo.minDiff:1<g-e?k=0.5*Math.abs(g-e):(k=1,b&&(d=!0))}else"axisY"===this.type&&(e=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,g=null!==this.viewportMaximum?this.viewportMaximum:
this.dataInfo.viewPortMax,isFinite(e)||isFinite(g)?isFinite(e)?isFinite(g)||(g=e):e=g:(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,e="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),0===e&&0===g?(g+=9,e=0):0===g-e?(c=Math.min(Math.abs(0.01*Math.abs(g)),5),g+=c,e-=c):e>g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g,
null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)),k=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-e?0.5*Math.abs(g-e):1,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&0<e&&(e=0),this.includeZero&&(null===this.viewportMaximum||isNaN(this.viewportMaximum))&&0>g&&(g=0));c=this.getApparentDifference(isNaN(this.viewportMinimum)||null===this.viewportMinimum?e:this.viewportMinimum,isNaN(this.viewportMaximum)||null===this.viewportMaximum?g:this.viewportMaximum,null,
!0);if("axisX"===this.type&&b){this.intervalType||(c/1<=a?(this.interval=1,this.intervalType="millisecond"):c/2<=a?(this.interval=2,this.intervalType="millisecond"):c/5<=a?(this.interval=5,this.intervalType="millisecond"):c/10<=a?(this.interval=10,this.intervalType="millisecond"):c/20<=a?(this.interval=20,this.intervalType="millisecond"):c/50<=a?(this.interval=50,this.intervalType="millisecond"):c/100<=a?(this.interval=100,this.intervalType="millisecond"):c/200<=a?(this.interval=200,this.intervalType=
"millisecond"):c/250<=a?(this.interval=250,this.intervalType="millisecond"):c/300<=a?(this.interval=300,this.intervalType="millisecond"):c/400<=a?(this.interval=400,this.intervalType="millisecond"):c/500<=a?(this.interval=500,this.intervalType="millisecond"):c/(1*S.secondDuration)<=a?(this.interval=1,this.intervalType="second"):c/(2*S.secondDuration)<=a?(this.interval=2,this.intervalType="second"):c/(5*S.secondDuration)<=a?(this.interval=5,this.intervalType="second"):c/(10*S.secondDuration)<=a?(this.interval=
10,this.intervalType="second"):c/(15*S.secondDuration)<=a?(this.interval=15,this.intervalType="second"):c/(20*S.secondDuration)<=a?(this.interval=20,this.intervalType="second"):c/(30*S.secondDuration)<=a?(this.interval=30,this.intervalType="second"):c/(1*S.minuteDuration)<=a?(this.interval=1,this.intervalType="minute"):c/(2*S.minuteDuration)<=a?(this.interval=2,this.intervalType="minute"):c/(5*S.minuteDuration)<=a?(this.interval=5,this.intervalType="minute"):c/(10*S.minuteDuration)<=a?(this.interval=
10,this.intervalType="minute"):c/(15*S.minuteDuration)<=a?(this.interval=15,this.intervalType="minute"):c/(20*S.minuteDuration)<=a?(this.interval=20,this.intervalType="minute"):c/(30*S.minuteDuration)<=a?(this.interval=30,this.intervalType="minute"):c/(1*S.hourDuration)<=a?(this.interval=1,this.intervalType="hour"):c/(2*S.hourDuration)<=a?(this.interval=2,this.intervalType="hour"):c/(3*S.hourDuration)<=a?(this.interval=3,this.intervalType="hour"):c/(6*S.hourDuration)<=a?(this.interval=6,this.intervalType=
"hour"):c/(1*S.dayDuration)<=a?(this.interval=1,this.intervalType="day"):c/(2*S.dayDuration)<=a?(this.interval=2,this.intervalType="day"):c/(4*S.dayDuration)<=a?(this.interval=4,this.intervalType="day"):c/(1*S.weekDuration)<=a?(this.interval=1,this.intervalType="week"):c/(2*S.weekDuration)<=a?(this.interval=2,this.intervalType="week"):c/(3*S.weekDuration)<=a?(this.interval=3,this.intervalType="week"):c/(1*S.monthDuration)<=a?(this.interval=1,this.intervalType="month"):c/(2*S.monthDuration)<=a?(this.interval=
2,this.intervalType="month"):c/(3*S.monthDuration)<=a?(this.interval=3,this.intervalType="month"):c/(6*S.monthDuration)<=a?(this.interval=6,this.intervalType="month"):(this.interval=c/(1*S.yearDuration)<=a?1:c/(2*S.yearDuration)<=a?2:c/(4*S.yearDuration)<=a?4:Math.floor(z.getNiceNumber(c/(a-1),!0)/S.yearDuration),this.intervalType="year"));if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=e-k/2;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=
g+k/2;d?this.autoValueFormatString="MMM DD YYYY HH:mm":"year"===this.intervalType?this.autoValueFormatString="YYYY":"month"===this.intervalType?this.autoValueFormatString="MMM YYYY":"week"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"day"===this.intervalType?this.autoValueFormatString="MMM DD YYYY":"hour"===this.intervalType?this.autoValueFormatString="hh:mm TT":"minute"===this.intervalType?this.autoValueFormatString="hh:mm TT":"second"===this.intervalType?this.autoValueFormatString=
"hh:mm:ss TT":"millisecond"===this.intervalType&&(this.autoValueFormatString="fff'ms'");this.valueFormatString||(this.valueFormatString=this.autoValueFormatString)}else{this.intervalType="number";c=z.getNiceNumber(c,!1);this.interval=this.options&&0<this.options.interval?this.options.interval:z.getNiceNumber(c/(a-1),!0);if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum="axisX"===this.type?e-k/2:Math.floor(e/this.interval)*this.interval;if(null===this.viewportMaximum||
isNaN(this.viewportMaximum))this.viewportMaximum="axisX"===this.type?g+k/2:Math.ceil(g/this.interval)*this.interval;0===this.viewportMaximum&&0===this.viewportMinimum&&(0===this.options.viewportMinimum?this.viewportMaximum+=10:0===this.options.viewportMaximum&&(this.viewportMinimum-=10),this.options&&"undefined"===typeof this.options.interval&&(this.interval=z.getNiceNumber((this.viewportMaximum-this.viewportMinimum)/(a-1),!0)))}if(null===this.minimum||null===this.maximum)if("axisX"===this.type?(e=
null!==this.minimum?this.minimum:this.dataInfo.min,g=null!==this.maximum?this.maximum:this.dataInfo.max,0===g-e&&(c="undefined"===typeof this.options.interval?0.4:this.options.interval,g+=c,e-=c),k=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-e?0.5*Math.abs(g-e):1):"axisY"===this.type&&(e=null!==this.minimum?this.minimum:this.dataInfo.min,g=null!==this.maximum?this.maximum:this.dataInfo.max,isFinite(e)||isFinite(g)?0===e&&0===g?(g+=9,e=0):0===g-e?(c=Math.min(Math.abs(0.01*Math.abs(g)),
5),g+=c,e-=c):e>g?(c=Math.min(0.01*Math.abs(this.getApparentDifference(g,e,null,!0)),5),0<=g?e=g-c:g=isFinite(e)?e+c:0):(c=Math.min(0.01*Math.abs(this.getApparentDifference(e,g,null,!0)),0.05),0!==g&&(g+=c),0!==e&&(e-=c)):(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,e="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),k=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-e?0.5*Math.abs(g-e):1,this.includeZero&&(null===this.minimum||
isNaN(this.minimum))&&0<e&&(e=0),this.includeZero&&(null===this.maximum||isNaN(this.maximum))&&0>g&&(g=0)),Math.abs(this.getApparentDifference(e,g,null,!0)),"axisX"===this.type&&b){this.valueType="dateTime";if(null===this.minimum||isNaN(this.minimum))this.minimum=e-k/2;if(null===this.maximum||isNaN(this.maximum))this.maximum=g+k/2}else this.intervalType=this.valueType="number",null===this.minimum&&(this.minimum="axisX"===this.type?e-k/2:Math.floor(e/this.interval)*this.interval,this.minimum=Math.min(this.minimum,
null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?Infinity:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum="axisX"===this.type?g+k/2:Math.ceil(g/this.interval)*this.interval,this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?-Infinity:this.sessionVariables.viewportMaximum)),0===this.maximum&&0===this.minimum&&(0===this.options.minimum?this.maximum+=10:0===
this.options.maximum&&(this.minimum-=10));u(this.sessionVariables.newViewportMinimum)&&(this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum));u(this.sessionVariables.newViewportMaximum)&&(this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum));this.range=this.viewportMaximum-this.viewportMinimum;this.intervalStartPosition="axisX"===this.type&&b?this.getLabelStartPoint(new Date(this.viewportMinimum),this.intervalType,this.interval):Math.floor((this.viewportMinimum+0.2*this.interval)/
this.interval)*this.interval;this.valueFormatString||(this.valueFormatString=z.generateValueFormatString(this.range,2))}};z.prototype.calculateLogarithmicAxisParameters=function(){var a=this.chart.layoutManager.getFreeSpace(),d=Math.log(this.logarithmBase),b;"bottom"===this._position||"top"===this._position?(this.maxWidth=a.width,this.maxHeight=a.height):(this.maxWidth=a.height,this.maxHeight=a.width);var a="axisX"===this.type?500>this.maxWidth?7:Math.max(7,Math.floor(this.maxWidth/100)):Math.max(Math.floor(this.maxWidth/
50),3),c,e,g,k;k=1;if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=this.minimum;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;if(this.scaleBreaks)for(k=0;k<this.scaleBreaks._appliedBreaks.length;k++)if((!u(this.sessionVariables.newViewportMinimum)&&this.sessionVariables.newViewportMinimum>=this.scaleBreaks._appliedBreaks[k].startValue||!u(this.options.minimum)&&this.options.minimum>=this.scaleBreaks._appliedBreaks[k].startValue||
!u(this.options.viewportMinimum)&&this.viewportMinimum>=this.scaleBreaks._appliedBreaks[k].startValue)&&(!u(this.sessionVariables.newViewportMaximum)&&this.sessionVariables.newViewportMaximum<=this.scaleBreaks._appliedBreaks[k].endValue||!u(this.options.maximum)&&this.options.maximum<=this.scaleBreaks._appliedBreaks[k].endValue||!u(this.options.viewportMaximum)&&this.viewportMaximum<=this.scaleBreaks._appliedBreaks[k].endValue)){this.scaleBreaks._appliedBreaks.splice(k,1);break}"axisX"===this.type?
(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,1===e/c&&(k=Math.pow(this.logarithmBase,"undefined"===typeof this.options.interval?0.4:this.options.interval),e*=k,c/=k),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,
e=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,0>=c&&!isFinite(e)?(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1):0>=c?c=e:isFinite(e)||(e=c),1===c&&1===e?(e*=this.logarithmBase-1/this.logarithmBase,c=1):1===e/c?(k=Math.min(e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),e*=k,c/=k):c>e?(k=Math.min(c/e*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),1<=e?c=e/k:e=c*k):(k=Math.min(e/c*Math.pow(this.logarithmBase,
0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=k),1!==c&&(c/=k)),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&1<c&&(c=1),this.includeZero&&(null===this.viewportMaximum||isNaN(this.viewportMaximum))&&1>e&&(e=1));k=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)/(isNaN(this.viewportMinimum)||null===
this.viewportMinimum?c:this.viewportMinimum);var l=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?e:this.viewportMaximum)-(isNaN(this.viewportMinimum)||null===this.viewportMinimum?c:this.viewportMinimum);this.intervalType="number";k=Math.pow(this.logarithmBase,z.getNiceNumber(Math.abs(Math.log(k)/d),!1));this.options&&0<this.options.interval?this.interval=this.options.interval:(this.interval=z.getNiceExponent(Math.log(k)/d/(a-1),!0),b=z.getNiceNumber(l/(a-1),!0));if(null===this.viewportMinimum||
isNaN(this.viewportMinimum))this.viewportMinimum="axisX"===this.type?c/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(c)/d/this.interval));if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum="axisX"===this.type?e*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(e)/d/this.interval));1===this.viewportMaximum&&1===this.viewportMinimum&&(1===this.options.viewportMinimum?this.viewportMaximum*=this.logarithmBase-1/this.logarithmBase:
1===this.options.viewportMaximum&&(this.viewportMinimum/=this.logarithmBase-1/this.logarithmBase),this.options&&"undefined"===typeof this.options.interval&&(this.interval=z.getNiceExponent(Math.ceil(Math.log(k)/d)/(a-1)),b=z.getNiceNumber((this.viewportMaximum-this.viewportMinimum)/(a-1),!0)));if(null===this.minimum||null===this.maximum)"axisX"===this.type?(c=null!==this.minimum?this.minimum:this.dataInfo.min,e=null!==this.maximum?this.maximum:this.dataInfo.max,1===e/c&&(k=Math.pow(this.logarithmBase,
"undefined"===typeof this.options.interval?0.4:this.options.interval),e*=k,c/=k),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(c=null!==this.minimum?this.minimum:this.dataInfo.min,e=null!==this.maximum?this.maximum:this.dataInfo.max,isFinite(c)||isFinite(e)?1===c&&1===e?(e*=this.logarithmBase,c/=this.logarithmBase):1===e/c?(k=Math.pow(this.logarithmBase,this.interval),e*=k,c/=k):c>e?(k=
Math.min(0.01*(c/e),5),1<=e?c=e/k:e=c*k):(k=Math.min(e/c*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,0.04)),1!==e&&(e*=k),1!==c&&(c/=k)):(e="undefined"===typeof this.options.interval?0:this.options.interval,c=1),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:e/c>this.logarithmBase?e/c*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.minimum||isNaN(this.minimum))&&1<c&&(c=1),this.includeZero&&(null===this.maximum||isNaN(this.maximum))&&
1>e&&(e=1)),this.intervalType="number",null===this.minimum&&(this.minimum="axisX"===this.type?c/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(c)/d/this.interval)),this.minimum=Math.min(this.minimum,null===this.sessionVariables.viewportMinimum||isNaN(this.sessionVariables.viewportMinimum)?"undefined"===typeof this.sessionVariables.newViewportMinimum?Infinity:this.sessionVariables.newViewportMinimum:this.sessionVariables.viewportMinimum)),null===this.maximum&&(this.maximum=
"axisX"===this.type?e*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(e)/d/this.interval)),this.maximum=Math.max(this.maximum,null===this.sessionVariables.viewportMaximum||isNaN(this.sessionVariables.viewportMaximum)?"undefined"===typeof this.sessionVariables.newViewportMaximum?0:this.sessionVariables.newViewportMaximum:this.sessionVariables.viewportMaximum)),1===this.maximum&&1===this.minimum&&(1===this.options.minimum?this.maximum*=this.logarithmBase-1/this.logarithmBase:
1===this.options.maximum&&(this.minimum/=this.logarithmBase-1/this.logarithmBase));this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum);this.viewportMaximum=Math.min(this.viewportMaximum,this.maximum);this.viewportMinimum>this.viewportMaximum&&(!this.options.viewportMinimum&&!this.options.minimum||this.options.viewportMaximum||this.options.maximum?this.options.viewportMinimum||this.options.minimum||!this.options.viewportMaximum&&!this.options.maximum||(this.viewportMinimum=this.minimum=
(this.options.viewportMaximum||this.options.maximum)/Math.pow(this.logarithmBase,2*Math.ceil(this.interval))):this.viewportMaximum=this.maximum=this.options.viewportMinimum||this.options.minimum);c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval);this.range=this.viewportMaximum/this.viewportMinimum;this.noTicks=a;if(!this.options.interval&&this.range<Math.pow(this.logarithmBase,8>this.viewportMaximum||3>a?2:3)){for(d=Math.floor(this.viewportMinimum/
b+0.5)*b;d<this.viewportMinimum;)d+=b;this.equidistantInterval=!1;this.intervalStartPosition=d;this.interval=b}else this.options.interval||(b=Math.ceil(this.interval),this.range>this.interval&&(this.interval=b,c=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(d*this.interval)+0.2)*this.interval))),this.equidistantInterval=!0,this.intervalStartPosition=c;if(!this.valueFormatString&&(this.valueFormatString="#,##0.##",1>this.viewportMinimum)){d=Math.floor(Math.abs(Math.log(this.viewportMinimum)/
Math.LN10))+2;if(isNaN(d)||!isFinite(d))d=2;if(2<d)for(k=0;k<d-2;k++)this.valueFormatString+="#"}};z.generateValueFormatString=function(a,d){var b="#,##0.",c=d;1>a&&(c+=Math.floor(Math.abs(Math.log(a)/Math.LN10)),isNaN(c)||!isFinite(c))&&(c=d);for(var e=0;e<c;e++)b+="#";return b};z.getNiceExponent=function(a,d){var b=Math.floor(Math.log(a)/Math.LN10),c=a/Math.pow(10,b),c=0>b?1>=c?1:5>=c?5:10:Math.max(Math.floor(c),1);return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.getNiceNumber=
function(a,d){var b=Math.floor(Math.log(a)/Math.LN10),c=a/Math.pow(10,b),c=d?1.5>c?1:3>c?2:7>c?5:10:1>=c?1:2>=c?2:5>=c?5:10;return-20>b?Number(c*Math.pow(10,b)):Number((c*Math.pow(10,b)).toFixed(20))};z.prototype.getLabelStartPoint=function(){var a=S[this.intervalType+"Duration"]*this.interval,a=new Date(Math.floor(this.viewportMinimum/a)*a);if("millisecond"!==this.intervalType)if("second"===this.intervalType)0<a.getMilliseconds()&&(a.setSeconds(a.getSeconds()+1),a.setMilliseconds(0));else if("minute"===
this.intervalType){if(0<a.getSeconds()||0<a.getMilliseconds())a.setMinutes(a.getMinutes()+1),a.setSeconds(0),a.setMilliseconds(0)}else if("hour"===this.intervalType){if(0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setHours(a.getHours()+1),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("day"===this.intervalType){if(0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setDate(a.getDate()+1),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("week"===
this.intervalType){if(0<a.getDay()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setDate(a.getDate()+(7-a.getDay())),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else if("month"===this.intervalType){if(1<a.getDate()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())a.setMonth(a.getMonth()+1),a.setDate(1),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)}else"year"===this.intervalType&&(0<a.getMonth()||1<
a.getDate()||0<a.getHours()||0<a.getMinutes()||0<a.getSeconds()||0<a.getMilliseconds())&&(a.setFullYear(a.getFullYear()+1),a.setMonth(0),a.setDate(1),a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0));return a};qa(Q,V);qa(L,V);L.prototype.createUserOptions=function(a){if("undefined"!==typeof a||this.options._isPlaceholder){var d=0;this.parent.options._isPlaceholder&&this.parent.createUserOptions();this.options._isPlaceholder||(Fa(this.parent[this.optionsName]),d=this.parent.options[this.optionsName].indexOf(this.options));
this.options="undefined"===typeof a?{}:a;this.parent.options[this.optionsName][d]=this.options}};L.prototype.render=function(a){if(0!==this.spacing||0!==this.options.lineThickness&&("undefined"!==typeof this.options.lineThickness||0!==this.parent.lineThickness)){var d=this.ctx,b=this.ctx.globalAlpha;this.ctx=a||this.ctx;this.ctx.save();this.ctx.beginPath();this.ctx.rect(this.chart.plotArea.x1,this.chart.plotArea.y1,this.chart.plotArea.width,this.chart.plotArea.height);this.ctx.clip();var c=this.scaleBreaks.parent.getPixelCoordinatesOnAxis(this.startValue),
e=this.scaleBreaks.parent.getPixelCoordinatesOnAxis(this.endValue);this.ctx.strokeStyle=this.lineColor;this.ctx.fillStyle=this.color;this.ctx.beginPath();this.ctx.globalAlpha=1;N(this.id);var g,k,l,h,m,p;a=Math.max(this.spacing,3);var q=Math.max(0,this.lineThickness);this.ctx.lineWidth=q;this.ctx.setLineDash&&this.ctx.setLineDash(R(this.lineDashType,q));if("bottom"===this.scaleBreaks.parent._position||"top"===this.scaleBreaks.parent._position)if(c=1===q%2?(c.x<<0)+0.5:c.x<<0,k=1===q%2?(e.x<<0)+0.5:
e.x<<0,"top"===this.scaleBreaks.parent._position?(e=this.chart.plotArea.y1,l=this.chart.plotArea.y2+q/2+0.5<<0):(e=this.chart.plotArea.y2,l=this.chart.plotArea.y1-q/2+0.5<<0,a*=-1),this.bounds={x1:c-q/2,y1:e,x2:k+q/2,y2:l},this.ctx.moveTo(c,e),"straight"===this.type||"top"===this.scaleBreaks.parent._position&&0>=a||"bottom"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(c,l),this.ctx.lineTo(k,l),this.ctx.lineTo(k,e);else if("wavy"===this.type){h=c;m=e;g=0.5;p=(l-m)/a/3;for(var n=0;n<p;n++)this.ctx.bezierCurveTo(h+
g*a,m+a,h+g*a,m+2*a,h,m+3*a),m+=3*a,g*=-1;this.ctx.bezierCurveTo(h+g*a,m+a,h+g*a,m+2*a,h,m+3*a);h=k;g*=-1;this.ctx.lineTo(h,m);for(n=0;n<p;n++)this.ctx.bezierCurveTo(h+g*a,m-a,h+g*a,m-2*a,h,m-3*a),m-=3*a,g*=-1}else{if("zigzag"===this.type){g=-1;m=e+a;h=c+a;p=(l-m)/a/2;for(n=0;n<p;n++)this.ctx.lineTo(h,m),h+=2*g*a,m+=2*a,g*=-1;this.ctx.lineTo(h,m);h+=k-c;for(n=0;n<p+1;n++)this.ctx.lineTo(h,m),h+=2*g*a,m-=2*a,g*=-1;this.ctx.lineTo(h+g*a,m+a)}}else if("left"===this.scaleBreaks.parent._position||"right"===
this.scaleBreaks.parent._position)if(e=1===q%2?(e.y<<0)+0.5:e.y<<0,l=1===q%2?(c.y<<0)+0.5:c.y<<0,"left"===this.scaleBreaks.parent._position?(c=this.chart.plotArea.x1,k=this.chart.plotArea.x2+q/2+0.5<<0):(c=this.chart.plotArea.x2,k=this.chart.plotArea.x1-q/2+0.5<<0,a*=-1),this.bounds={x1:c,y1:e-q/2,x2:k,y2:l+q/2},this.ctx.moveTo(c,e),"straight"===this.type||"left"===this.scaleBreaks.parent._position&&0>=a||"right"===this.scaleBreaks.parent._position&&0<=a)this.ctx.lineTo(k,e),this.ctx.lineTo(k,l),
this.ctx.lineTo(c,l);else if("wavy"===this.type){h=c;m=e;g=0.5;p=(k-h)/a/3;for(n=0;n<p;n++)this.ctx.bezierCurveTo(h+a,m+g*a,h+2*a,m+g*a,h+3*a,m),h+=3*a,g*=-1;this.ctx.bezierCurveTo(h+a,m+g*a,h+2*a,m+g*a,h+3*a,m);m=l;g*=-1;this.ctx.lineTo(h,m);for(n=0;n<p;n++)this.ctx.bezierCurveTo(h-a,m+g*a,h-2*a,m+g*a,h-3*a,m),h-=3*a,g*=-1}else if("zigzag"===this.type){g=1;m=e-a;h=c+a;p=(k-h)/a/2;for(n=0;n<p;n++)this.ctx.lineTo(h,m),m+=2*g*a,h+=2*a,g*=-1;this.ctx.lineTo(h,m);m+=l-e;for(n=0;n<p+1;n++)this.ctx.lineTo(h,
m),m+=2*g*a,h-=2*a,g*=-1;this.ctx.lineTo(h+a,m+g*a)}0<q&&this.ctx.stroke();this.ctx.closePath();this.ctx.globalAlpha=this.fillOpacity;this.ctx.globalCompositeOperation="destination-over";this.ctx.fill();this.ctx.restore();this.ctx.globalAlpha=b;this.ctx=d}};qa(X,V);X.prototype.createUserOptions=function(a){if("undefined"!==typeof a||this.options._isPlaceholder){var d=0;this.parent.options._isPlaceholder&&this.parent.createUserOptions();this.options._isPlaceholder||(Fa(this.parent.stripLines),d=this.parent.options.stripLines.indexOf(this.options));
this.options="undefined"===typeof a?{}:a;this.parent.options.stripLines[d]=this.options}};X.prototype.render=function(){this.ctx.save();var a=this.parent.getPixelCoordinatesOnAxis(this.value),d=Math.abs("pixel"===this._thicknessType?this.thickness:this.parent.conversionParameters.pixelPerUnit*this.thickness);if(0<d){var b=null===this.opacity?1:this.opacity;this.ctx.strokeStyle=this.color;this.ctx.beginPath();var c=this.ctx.globalAlpha;this.ctx.globalAlpha=b;N(this.id);var e,g,k,l;this.ctx.lineWidth=
d;this.ctx.setLineDash&&this.ctx.setLineDash(R(this.lineDashType,d));if("bottom"===this.parent._position||"top"===this.parent._position)e=g=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0,k=this.chart.plotArea.y1,l=this.chart.plotArea.y2,this.bounds={x1:e-d/2,y1:k,x2:g+d/2,y2:l};else if("left"===this.parent._position||"right"===this.parent._position)k=l=1===this.ctx.lineWidth%2?(a.y<<0)+0.5:a.y<<0,e=this.chart.plotArea.x1,g=this.chart.plotArea.x2,this.bounds={x1:e,y1:k-d/2,x2:g,y2:l+d/2};this.ctx.moveTo(e,
k);this.ctx.lineTo(g,l);this.ctx.stroke();this.ctx.globalAlpha=c}this.ctx.restore()};qa(fa,V);fa.prototype.render=function(a,d){var b,c,e,g,k=null,l=k=null,h="";if(!this.valueFormatString)if("dateTime"===this.parent.valueType)this.valueFormatString=this.parent.valueFormatString;else{var m=0,m="xySwapped"===this.chart.plotInfo.axisPlacement?50<this.parent.range?0:500<this.chart.width&&25>this.parent.range?2:Math.floor(Math.abs(Math.log(this.parent.range)/Math.LN10))+(5>this.parent.range?2:10>this.parent.range?
1:0):50<this.parent.range?0:Math.floor(Math.abs(Math.log(this.parent.range)/Math.LN10))+(5>this.parent.range?2:10>this.parent.range?1:0);this.valueFormatString=z.generateValueFormatString(this.parent.range,m)}var l=null===this.opacity?1:this.opacity,m=Math.abs("pixel"===this._thicknessType?this.thickness:this.parent.conversionParameters.pixelPerUnit*this.thickness),p=this.chart.overlaidCanvasCtx,q=p.globalAlpha;p.globalAlpha=l;p.beginPath();p.strokeStyle=this.color;p.lineWidth=m;p.save();this.labelFontSize=
u(this.options.labelFontSize)?this.parent.labelFontSize:this.labelFontSize;if("left"===this.parent._position||"right"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?this.parent.bounds.x2-this.parent.bounds.x1:this.labelMaxWidth,this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?3*this.chart.height:2*this.labelFontSize;else if("top"===this.parent._position||"bottom"===this.parent._position)this.labelMaxWidth=u(this.options.labelMaxWidth)?3*this.chart.width:this.labelMaxWidth,
this.labelMaxHeight=u(this.options.labelWrap)||this.labelWrap?this.parent.bounds.height:2*this.labelFontSize;0<m&&p.setLineDash&&p.setLineDash(R(this.lineDashType,m));l=new ka(p,{x:0,y:0,padding:{top:2,right:3,bottom:2,left:4},backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,maxWidth:this.labelMaxWidth,maxHeight:this.labelMaxHeight,angle:this.labelAngle,text:h,horizontalAlign:"left",fontSize:this.labelFontSize,
fontFamily:this.labelFontFamily,fontWeight:this.labelFontWeight,fontColor:this.labelFontColor,fontStyle:this.labelFontStyle,textBaseline:"middle"});if(this.snapToDataPoint){var n=0,k=[];if("xySwapped"===this.chart.plotInfo.axisPlacement){var f=null;if("bottom"===this.parent._position||"top"===this.parent._position)n=this.parent.dataSeries[0].axisX.convertPixelToValue({y:d});else if("left"===this.parent._position||"right"===this.parent._position)n=this.parent.convertPixelToValue({y:d});for(var r=0;r<
this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(f.dataSeries=this.parent.dataSeries[r],null!==f.dataPoint.y&&k.push(f));f=null;if(0===k.length)return;k.sort(function(a,b){return a.distance-b.distance});f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y));r=0;if("rangeBar"===k[0].dataSeries.type||"error"===k[0].dataSeries.type)for(var f=Math.abs(a-this.parent.convertValueToPixel(k[r].dataPoint.y[0])),v=0,n=0;n<k.length;n++)if(k[n].dataPoint.y&&
k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y)),v<f&&(f=v,r=n);else if("stackedBar"===k[0].dataSeries.type)for(var f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y)),D=v=0,n=r=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),
v<f&&(f=v,r=n);else D+=k[n].dataPoint.y,v=Math.abs(a-this.parent.convertValueToPixel(D)),v<f&&(f=v,r=n);else if("stackedBar100"===k[0].dataSeries.type)for(var f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y)),t=D=v=0,n=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else D+=k[n].dataPoint.y,t=k[n].dataPoint.x.getTime?k[n].dataPoint.x.getTime():k[n].dataPoint.x,
t=100*(D/k[n].dataSeries.plotUnit.dataPointYSums[t]),v=Math.abs(a-this.parent.convertValueToPixel(t)),v<f&&(f=v,r=n);else for(f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y)),n=r=v=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else v=Math.abs(a-this.parent.convertValueToPixel(k[n].dataPoint.y)),v<f&&(f=v,r=n);h=k[r];if("bottom"===this.parent._position||
"top"===this.parent._position){b=0;if("rangeBar"===this.parent.dataSeries[r].type||"error"===this.parent.dataSeries[r].type){f=Math.abs(a-this.parent.convertValueToPixel(h.dataPoint.y[0]));for(n=v=0;n<h.dataPoint.y.length;n++)v=Math.abs(a-this.parent.convertValueToPixel(h.dataPoint.y[n])),v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.y[b])<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.y[b])<<0;l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,
axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y[b]}):u(this.options.label)?ba(h.dataPoint.y[b],this.valueFormatString,this.chart._cultureInfo):this.label}else if("stackedBar"===this.parent.dataSeries[r].type){f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y));D=v=0;for(n=r;0<=n;n--)D+=k[n].dataPoint.y,v=Math.abs(a-this.parent.convertValueToPixel(D)),v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(D)<<0)+0.5:this.parent.convertValueToPixel(D)<<0;
l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y}):u(this.options.label)?ba(h.dataPoint.y,this.valueFormatString,this.chart._cultureInfo):this.label}else if("stackedBar100"===this.parent.dataSeries[r].type){f=Math.abs(a-this.parent.convertValueToPixel(k[0].dataPoint.y));t=D=v=0;for(n=r;0<=n;n--)D+=k[n].dataPoint.y,t=k[n].dataPoint.x.getTime?k[n].dataPoint.x.getTime():k[n].dataPoint.x,t=100*(D/k[n].dataSeries.plotUnit.dataPointYSums[t]),
v=Math.abs(a-this.parent.convertValueToPixel(t)),v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(t)<<0)+0.5:this.parent.convertValueToPixel(t)<<0;l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:t}):u(this.options.label)?ba(t,this.valueFormatString,this.chart._cultureInfo):this.label}else k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.y)<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.y)<<
0,l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y}):u(this.options.label)?ba(h.dataPoint.y,this.valueFormatString,this.chart._cultureInfo):this.label;b=c=k;e=this.chart.plotArea.y1;g=this.chart.plotArea.y2;this.bounds={x1:b-m/2,y1:e,x2:c+m/2,y2:g};l.x=b-l.measureText().width/2;l.x+l.width>this.chart.bounds.x2?l.x=this.chart.bounds.x2-l.width:l.x<this.chart.bounds.x1&&(l.x=this.chart.bounds.x1);l.y=this.parent.lineCoordinates.y2+
l.fontSize/2+2}else if("left"===this.parent._position||"right"===this.parent._position){e=g=k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.x)<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.x)<<0;b=this.chart.plotArea.x1;c=this.chart.plotArea.x2;this.bounds={x1:b,y1:e-m/2,x2:c,y2:g+m/2};t=!1;if(this.parent.labels)for(k=Math.ceil(this.parent.interval),n=0;n<this.parent.viewportMaximum;n+=k)if(this.parent.labels[n])t=!0;else{t=!1;break}if(t){if("axisX"===this.parent.type)for(n=
this.parent.convertPixelToValue({y:d}),f=null,r=0;r<this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.x}):u(this.options.label)?f.dataPoint.label:this.label)}else"dateTime"===this.parent.valueType?l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.x}):
u(this.options.label)?Ca(h.dataPoint.x,this.valueFormatString,this.chart._cultureInfo):this.label:"number"===this.parent.valueType&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.x}):u(this.options.label)?ba(h.dataPoint.x,this.valueFormatString,this.chart._cultureInfo):this.label);l.y=g+l.fontSize/2-l.measureText().height/2+2;l.y-l.fontSize/2<this.chart.bounds.y1?l.y=this.chart.bounds.y1+l.fontSize/2+2:l.y+l.measureText().height-
l.fontSize/2>this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}}else if("bottom"===this.parent._position||"top"===this.parent._position){n=this.parent.convertPixelToValue({x:a});for(r=0;r<this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(f.dataSeries=this.parent.dataSeries[r],
null!==f.dataPoint.y&&k.push(f));if(0===k.length)return;k.sort(function(a,b){return a.distance-b.distance});h=k[0];b=c=k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.x)<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.x)<<0;e=this.chart.plotArea.y1;g=this.chart.plotArea.y2;this.bounds={x1:b-m/2,y1:e,x2:c+m/2,y2:g};t=!1;if(this.parent.labels)for(k=Math.ceil(this.parent.interval),n=0;n<this.parent.viewportMaximum;n+=k)if(this.parent.labels[n])t=!0;else{t=!1;break}if(t){if("axisX"===
this.parent.type)for(n=this.parent.convertPixelToValue({x:a}),f=null,r=0;r<this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.x}):u(this.options.label)?f.dataPoint.label:this.label)}else"dateTime"===this.parent.valueType?l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,
value:h.dataPoint.x}):u(this.options.label)?Ca(h.dataPoint.x,this.valueFormatString,this.chart._cultureInfo):this.label:"number"===this.parent.valueType&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.x}):u(this.options.label)?ba(h.dataPoint.x,this.valueFormatString,this.chart._cultureInfo):this.label);l.x=b-l.measureText().width/2;l.x+l.width>this.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.x<this.chart.bounds.x1&&
(l.x=this.chart.bounds.x1);"bottom"===this.parent._position?l.y=this.parent.lineCoordinates.y2+l.fontSize/2+2:"top"===this.parent._position&&(l.y=this.parent.lineCoordinates.y1-l.height+l.fontSize/2+2)}else if("left"===this.parent._position||"right"===this.parent._position){!u(this.parent.dataSeries)&&0<this.parent.dataSeries.length&&(n=this.parent.dataSeries[0].axisX.convertPixelToValue({x:a}));for(r=0;r<this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&
(f.dataSeries=this.parent.dataSeries[r],null!==f.dataPoint.y&&k.push(f));if(0===k.length)return;k.sort(function(a,b){return a.distance-b.distance});r=0;if("rangeColumn"===k[0].dataSeries.type||"rangeArea"===k[0].dataSeries.type||"error"===k[0].dataSeries.type||"rangeSplineArea"===k[0].dataSeries.type||"candlestick"===k[0].dataSeries.type||"ohlc"===k[0].dataSeries.type||"boxAndWhisker"===k[0].dataSeries.type)for(f=Math.abs(d-this.parent.convertValueToPixel(k[0].dataPoint.y[0])),n=v=0;n<k.length;n++)if(k[n].dataPoint.y&&
k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y)),v<f&&(f=v,r=n);else if("stackedColumn"===k[0].dataSeries.type||"stackedArea"===k[0].dataSeries.type)for(f=Math.abs(d-this.parent.convertValueToPixel(k[0].dataPoint.y)),n=D=v=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),
v<f&&(f=v,r=n);else D+=k[n].dataPoint.y,v=Math.abs(d-this.parent.convertValueToPixel(D)),v<f&&(f=v,r=n);else if("stackedColumn100"===k[0].dataSeries.type||"stackedArea100"===k[0].dataSeries.type)for(f=Math.abs(d-this.parent.convertValueToPixel(k[0].dataPoint.y)),n=t=D=v=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else D+=k[n].dataPoint.y,t=k[n].dataPoint.x.getTime?
k[n].dataPoint.x.getTime():k[n].dataPoint.x,t=100*(D/k[n].dataSeries.plotUnit.dataPointYSums[t]),v=Math.abs(d-this.parent.convertValueToPixel(t)),v<f&&(f=v,r=n);else for(f=Math.abs(d-this.parent.convertValueToPixel(k[0].dataPoint.y)),n=v=0;n<k.length;n++)if(k[n].dataPoint.y&&k[n].dataPoint.y.length)for(h=0;h<k[n].dataPoint.y.length;h++)v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y[h])),v<f&&(f=v,r=n);else v=Math.abs(d-this.parent.convertValueToPixel(k[n].dataPoint.y)),v<f&&(f=v,r=
n);h=k[r];b=0;if("rangeColumn"===this.parent.dataSeries[r].type||"rangeArea"===this.parent.dataSeries[r].type||"error"===this.parent.dataSeries[r].type||"rangeSplineArea"===this.parent.dataSeries[r].type||"candlestick"===this.parent.dataSeries[r].type||"ohlc"===this.parent.dataSeries[r].type||"boxAndWhisker"===this.parent.dataSeries[r].type){f=Math.abs(d-this.parent.convertValueToPixel(h.dataPoint.y[0]));for(n=v=0;n<h.dataPoint.y.length;n++)v=Math.abs(d-this.parent.convertValueToPixel(h.dataPoint.y[n])),
v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.y[b])<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.y[b])<<0;l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y[b]}):u(this.options.label)?ba(h.dataPoint.y[b],this.valueFormatString,this.chart._cultureInfo):this.label}else if("stackedColumn"===this.parent.dataSeries[r].type||"stackedArea"===this.parent.dataSeries[r].type){f=Math.abs(d-
this.parent.convertValueToPixel(k[0].dataPoint.y));D=v=0;for(n=r;0<=n;n--)D+=k[n].dataPoint.y,v=Math.abs(d-this.parent.convertValueToPixel(D)),v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(D)<<0)+0.5:this.parent.convertValueToPixel(D)<<0;l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y}):u(this.options.label)?ba(h.dataPoint.y,this.valueFormatString,this.chart._cultureInfo):this.label}else if("stackedColumn100"===
this.parent.dataSeries[r].type||"stackedArea100"===this.parent.dataSeries[r].type){f=Math.abs(d-this.parent.convertValueToPixel(k[0].dataPoint.y));D=v=0;for(n=r;0<=n;n--)D+=k[n].dataPoint.y,t=k[n].dataPoint.x.getTime?k[n].dataPoint.x.getTime():k[n].dataPoint.x,t=100*(D/k[n].dataSeries.plotUnit.dataPointYSums[t]),v=Math.abs(d-this.parent.convertValueToPixel(t)),v<f&&(f=v,b=n);k=1===p.lineWidth%2?(this.parent.convertValueToPixel(t)<<0)+0.5:this.parent.convertValueToPixel(t)<<0;l.text=this.labelFormatter?
this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:t}):u(this.options.label)?ba(t,this.valueFormatString,this.chart._cultureInfo):this.label}else"waterfall"===this.parent.dataSeries[r].type?(k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataSeries.dataPointEOs[h.index].cumulativeSum)<<0)+0.5:this.parent.convertValueToPixel(h.dataSeries.dataPointEOs[h.index].cumulativeSum)<<0,l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,
crosshair:this.options,value:h.dataSeries.dataPointEOs[h.index].cumulativeSum}):u(this.options.label)?ba(h.dataSeries.dataPointEOs[h.index].cumulativeSum,this.valueFormatString,this.chart._cultureInfo):this.label):(k=1===p.lineWidth%2?(this.parent.convertValueToPixel(h.dataPoint.y)<<0)+0.5:this.parent.convertValueToPixel(h.dataPoint.y)<<0,l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:h.dataPoint.y}):u(this.options.label)?ba(h.dataPoint.y,
this.valueFormatString,this.chart._cultureInfo):this.label);e=g=k;b=this.chart.plotArea.x1;c=this.chart.plotArea.x2;this.bounds={x1:b,y1:e-m/2,x2:c,y2:g+m/2};l.y=g+l.fontSize/2-l.measureText().height/2+2;l.y-l.fontSize/2<this.chart.bounds.y1?l.y=this.chart.bounds.y1+l.fontSize/2+2:l.y+l.measureText().height-l.fontSize/2>this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===
this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}k=null;("bottom"===this.parent._position||"top"===this.parent._position)&&(b>=this.parent.convertValueToPixel(this.parent.viewportMinimum)&&c<=this.parent.convertValueToPixel(this.parent.viewportMaximum))&&(0<m&&(p.moveTo(b,e),p.lineTo(c,g),p.stroke()),p.restore(),!u(l.text)&&("number"===typeof l.text.valueOf()||0<l.text.length)&&l.render(!0));("left"===this.parent._position||"right"===this.parent._position)&&(g>=this.parent.convertValueToPixel(this.parent.viewportMaximum)&&
e<=this.parent.convertValueToPixel(this.parent.viewportMinimum))&&(0<m&&(p.moveTo(b,e),p.lineTo(c,g),p.stroke()),p.restore(),!u(l.text)&&("number"===typeof l.text.valueOf()||0<l.text.length)&&l.render(!0))}else{if("bottom"===this.parent._position||"top"===this.parent._position)b=c=k=1===p.lineWidth%2?(a<<0)+0.5:a<<0,e=this.chart.plotArea.y1,g=this.chart.plotArea.y2,this.bounds={x1:b-m/2,y1:e,x2:c+m/2,y2:g};else if("left"===this.parent._position||"right"===this.parent._position)e=g=k=1===p.lineWidth%
2?(d<<0)+0.5:d<<0,b=this.chart.plotArea.x1,c=this.chart.plotArea.x2,this.bounds={x1:b,y1:e-m/2,x2:c,y2:g+m/2};if("xySwapped"===this.chart.plotInfo.axisPlacement)if("left"===this.parent._position||"right"===this.parent._position){t=!1;if(this.parent.labels)for(k=Math.ceil(this.parent.interval),n=0;n<this.parent.viewportMaximum;n+=k)if(this.parent.labels[n])t=!0;else{t=!1;break}if(t){if("axisX"===this.parent.type)for(n=this.parent.convertPixelToValue({y:d}),f=null,r=0;r<this.parent.dataSeries.length;r++)(f=
this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):u(this.options.label)?f.dataPoint.label:this.label)}else"dateTime"===this.parent.valueType?l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(d)}):u(this.options.label)?Ca(this.parent.convertPixelToValue(d),
this.valueFormatString,this.chart._cultureInfo):this.label:"number"===this.parent.valueType&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(d)}):u(this.options.label)?ba(this.parent.convertPixelToValue(d),this.valueFormatString,this.chart._cultureInfo):this.label);l.y=d+l.fontSize/2-l.measureText().height/2+2;l.y-l.fontSize/2<this.chart.bounds.y1?l.y=this.chart.bounds.y1+l.fontSize/2+2:l.y+l.measureText().height-
l.fontSize/2>this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2);"left"===this.parent._position?l.x=this.parent.lineCoordinates.x1-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2)}else{if("bottom"===this.parent._position||"top"===this.parent._position)l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):u(this.options.label)?
ba(this.parent.convertPixelToValue(a),this.valueFormatString,this.chart._cultureInfo):this.label,l.x=b-l.measureText().width/2,l.x+l.width>this.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width),l.x<this.chart.bounds.x1&&(l.x=this.chart.bounds.x1),"bottom"===this.parent._position&&(l.y=this.parent.lineCoordinates.y2+l.fontSize/2+2),"top"===this.parent._position&&(l.y=this.parent.lineCoordinates.y1-l.height+l.fontSize/2+2)}else if("bottom"===this.parent._position||"top"===this.parent._position){t=
!1;h="";if(this.parent.labels)for(k=Math.ceil(this.parent.interval),n=0;n<this.parent.viewportMaximum;n+=k)if(this.parent.labels[n])t=!0;else{t=!1;break}if(t){if("axisX"===this.parent.type)for(n=this.parent.convertPixelToValue({x:a}),f=null,r=0;r<this.parent.dataSeries.length;r++)(f=this.parent.dataSeries[r].getDataPointAtX(n,!0))&&0<=f.index&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):
u(this.options.label)?f.dataPoint.label:this.label)}else"dateTime"===this.parent.valueType?l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(a)}):u(this.options.label)?Ca(this.parent.convertPixelToValue(a),this.valueFormatString,this.chart._cultureInfo):this.label:"number"===this.parent.valueType&&(l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,
value:0<this.parent.dataSeries.length?this.parent.convertPixelToValue(a):""}):u(this.options.label)?ba(this.parent.convertPixelToValue(a),this.valueFormatString,this.chart._cultureInfo):this.label);l.x=b-l.measureText().width/2;l.x+l.width>this.chart.bounds.x2&&(l.x=this.chart.bounds.x2-l.width);l.x<this.chart.bounds.x1&&(l.x=this.chart.bounds.x1);"bottom"===this.parent._position?l.y=this.parent.lineCoordinates.y2+l.fontSize/2+2:"top"===this.parent._position&&(l.y=this.parent.lineCoordinates.y1-l.height+
l.fontSize/2+2)}else if("left"===this.parent._position||"right"===this.parent._position)l.text=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.parent.options,crosshair:this.options,value:this.parent.convertPixelToValue(d)}):u(this.options.label)?ba(this.parent.convertPixelToValue(d),this.valueFormatString,this.chart._cultureInfo):this.label,l.y=d+l.fontSize/2-l.measureText().height/2+2,l.y-l.fontSize/2<this.chart.bounds.y1?l.y=this.chart.bounds.y1+l.fontSize/2+2:l.y+l.measureText().height-
l.fontSize/2>this.chart.bounds.y2&&(l.y=this.chart.bounds.y2-l.measureText().height+l.fontSize/2),"left"===this.parent._position?l.x=this.parent.lineCoordinates.x2-l.measureText().width:"right"===this.parent._position&&(l.x=this.parent.lineCoordinates.x2);0<m&&(p.moveTo(b,e),p.lineTo(c,g),p.stroke());p.restore();!u(l.text)&&("number"===typeof l.text.valueOf()||0<l.text.length)&&l.render(!0)}p.globalAlpha=q};qa($,V);$.prototype._initialize=function(){if(this.enabled){this.container=document.createElement("div");
this.container.setAttribute("class","canvasjs-chart-tooltip");this.container.style.position="absolute";this.container.style.height="auto";this.container.style.boxShadow="1px 1px 2px 2px rgba(0,0,0,0.1)";this.container.style.zIndex="1000";this.container.style.pointerEvents="none";this.container.style.display="none";var a;a='<div style=" width: auto;height: auto;min-width: 50px;';a+="line-height: auto;";a+="margin: 0px 0px 0px 0px;";a+="padding: 5px;";a+="font-family: Calibri, Arial, Georgia, serif;";
a+="font-weight: normal;";a+="font-style: "+(r?"italic;":"normal;");a+="font-size: 14px;";a+="color: #000000;";a+="text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);";a+="text-align: left;";a+="border: 2px solid gray;";a+=r?"background: rgba(255,255,255,.9);":"background: rgb(255,255,255);";a+="text-indent: 0px;";a+="white-space: nowrap;";a+="border-radius: 5px;";a+="-moz-user-select:none;";a+="-khtml-user-select: none;";a+="-webkit-user-select: none;";a+="-ms-user-select: none;";a+="user-select: none;";
r||(a+="filter: alpha(opacity = 90);",a+="filter: progid:DXImageTransform.Microsoft.Shadow(Strength=3, Direction=135, Color='#666666');");a+='} "> Sample Tooltip</div>';this.container.innerHTML=a;this.contentDiv=this.container.firstChild;this.container.style.borderRadius=this.contentDiv.style.borderRadius;this.chart._canvasJSContainer.appendChild(this.container)}};$.prototype.mouseMoveHandler=function(a,d){this._lastUpdated&&4>(new Date).getTime()-this._lastUpdated||(this._lastUpdated=(new Date).getTime(),
this.chart.resetOverlayedCanvas(),this._updateToolTip(a,d))};$.prototype._updateToolTip=function(a,d,b){b="undefined"===typeof b?!0:b;this.container||this._initialize();this.enabled||this.hide();if(!this.chart.disableToolTip){if("undefined"===typeof a||"undefined"===typeof d){if(isNaN(this._prevX)||isNaN(this._prevY))return;a=this._prevX;d=this._prevY}else this._prevX=a,this._prevY=d;var c=null,e=null,g=[],k=0;if(this.shared&&this.enabled&&"none"!==this.chart.plotInfo.axisPlacement){if("xySwapped"===
this.chart.plotInfo.axisPlacement){var l=[];if(this.chart.axisX)for(var h=0;h<this.chart.axisX.length;h++){for(var k=this.chart.axisX[h].convertPixelToValue({y:d}),m=null,c=0;c<this.chart.axisX[h].dataSeries.length;c++)(m=this.chart.axisX[h].dataSeries[c].getDataPointAtX(k,b))&&0<=m.index&&(m.dataSeries=this.chart.axisX[h].dataSeries[c],null!==m.dataPoint.y&&l.push(m));m=null}if(this.chart.axisX2)for(h=0;h<this.chart.axisX2.length;h++){k=this.chart.axisX2[h].convertPixelToValue({y:d});m=null;for(c=
0;c<this.chart.axisX2[h].dataSeries.length;c++)(m=this.chart.axisX2[h].dataSeries[c].getDataPointAtX(k,b))&&0<=m.index&&(m.dataSeries=this.chart.axisX2[h].dataSeries[c],null!==m.dataPoint.y&&l.push(m));m=null}}else{l=[];if(this.chart.axisX)for(h=0;h<this.chart.axisX.length;h++)for(k=this.chart.axisX[h].convertPixelToValue({x:a}),m=null,c=0;c<this.chart.axisX[h].dataSeries.length;c++)(m=this.chart.axisX[h].dataSeries[c].getDataPointAtX(k,b))&&0<=m.index&&(m.dataSeries=this.chart.axisX[h].dataSeries[c],
null!==m.dataPoint.y&&l.push(m));if(this.chart.axisX2)for(h=0;h<this.chart.axisX2.length;h++)for(k=this.chart.axisX2[h].convertPixelToValue({x:a}),m=null,c=0;c<this.chart.axisX2[h].dataSeries.length;c++)(m=this.chart.axisX2[h].dataSeries[c].getDataPointAtX(k,b))&&0<=m.index&&(m.dataSeries=this.chart.axisX2[h].dataSeries[c],null!==m.dataPoint.y&&l.push(m))}if(0===l.length)return;l.sort(function(a,b){return a.distance-b.distance});b=l[0];for(c=0;c<l.length;c++)l[c].dataPoint.x.valueOf()===b.dataPoint.x.valueOf()&&
g.push(l[c]);l=null}else{if(m=this.chart.getDataPointAtXY(a,d,b))this.currentDataPointIndex=m.dataPointIndex,this.currentSeriesIndex=m.dataSeries.index;else if(r)if(m=ab(a,d,this.chart._eventManager.ghostCtx),0<m&&"undefined"!==typeof this.chart._eventManager.objectMap[m]){m=this.chart._eventManager.objectMap[m];if("legendItem"===m.objectType)return;this.currentSeriesIndex=m.dataSeriesIndex;this.currentDataPointIndex=0<=m.dataPointIndex?m.dataPointIndex:-1}else this.currentDataPointIndex=-1;else this.currentDataPointIndex=
-1;if(0<=this.currentSeriesIndex){e=this.chart.data[this.currentSeriesIndex];m={};if(0<=this.currentDataPointIndex)c=e.dataPoints[this.currentDataPointIndex],m.dataSeries=e,m.dataPoint=c,m.index=this.currentDataPointIndex,m.distance=Math.abs(c.x-k),"waterfall"===e.type&&(m.cumulativeSumYStartValue=e.dataPointEOs[this.currentDataPointIndex].cumulativeSumYStartValue,m.cumulativeSum=e.dataPointEOs[this.currentDataPointIndex].cumulativeSum);else{if(!this.enabled||"line"!==e.type&&"stepLine"!==e.type&&
"spline"!==e.type&&"area"!==e.type&&"stepArea"!==e.type&&"splineArea"!==e.type&&"stackedArea"!==e.type&&"stackedArea100"!==e.type&&"rangeArea"!==e.type&&"rangeSplineArea"!==e.type&&"candlestick"!==e.type&&"ohlc"!==e.type&&"boxAndWhisker"!==e.type)return;k=e.axisX.convertPixelToValue({x:a});m=e.getDataPointAtX(k,b);u(m)||(m.dataSeries=e,this.currentDataPointIndex=m.index,c=m.dataPoint)}if(!u(m)&&!u(m.dataPoint)&&!u(m.dataPoint.y))if(m.dataSeries.axisY)if(0<m.dataPoint.y.length){for(c=b=0;c<m.dataPoint.y.length;c++)m.dataPoint.y[c]<
m.dataSeries.axisY.viewportMinimum?b--:m.dataPoint.y[c]>m.dataSeries.axisY.viewportMaximum&&b++;b<m.dataPoint.y.length&&b>-m.dataPoint.y.length&&g.push(m)}else"column"===e.type||"bar"===e.type?0>m.dataPoint.y?0>m.dataSeries.axisY.viewportMinimum&&m.dataSeries.axisY.viewportMaximum>=m.dataPoint.y&&g.push(m):m.dataSeries.axisY.viewportMinimum<=m.dataPoint.y&&0<=m.dataSeries.axisY.viewportMaximum&&g.push(m):"bubble"===e.type?(b=this.chart._eventManager.objectMap[e.dataPointIds[m.index]].size/2,m.dataPoint.y>=
m.dataSeries.axisY.viewportMinimum-b&&m.dataPoint.y<=m.dataSeries.axisY.viewportMaximum+b&&g.push(m)):"waterfall"===e.type?(b=0,m.cumulativeSumYStartValue<m.dataSeries.axisY.viewportMinimum?b--:m.cumulativeSumYStartValue>m.dataSeries.axisY.viewportMaximum&&b++,m.cumulativeSum<m.dataSeries.axisY.viewportMinimum?b--:m.cumulativeSum>m.dataSeries.axisY.viewportMaximum&&b++,2>b&&-2<b&&g.push(m)):(0<=m.dataSeries.type.indexOf("100")||"stackedColumn"===e.type||"stackedBar"===e.type||m.dataPoint.y>=m.dataSeries.axisY.viewportMinimum&&
m.dataPoint.y<=m.dataSeries.axisY.viewportMaximum)&&g.push(m);else g.push(m)}}if(0<g.length){if(this.highlightObjects(g),this.enabled)if(b="",b=this.getToolTipInnerHTML({entries:g}),null!==b){this.contentDiv.innerHTML=b;b=!1;"none"===this.container.style.display&&(b=!0,this.container.style.display="block");try{this.contentDiv.style.background=this.backgroundColor?this.backgroundColor:r?"rgba(255,255,255,.9)":"rgb(255,255,255)",this.borderColor="waterfall"===g[0].dataSeries.type?this.contentDiv.style.borderRightColor=
this.contentDiv.style.borderLeftColor=this.contentDiv.style.borderColor=this.options.borderColor?this.options.borderColor:g[0].dataPoint.color?g[0].dataPoint.color:0<g[0].dataPoint.y?g[0].dataSeries.risingColor:g[0].dataSeries.fallingColor:"error"===g[0].dataSeries.type?this.contentDiv.style.borderRightColor=this.contentDiv.style.borderLeftColor=this.contentDiv.style.borderColor=this.options.borderColor?this.options.borderColor:g[0].dataSeries.color?g[0].dataSeries.color:g[0].dataSeries._colorSet[e.index%
g[0].dataSeries._colorSet.length]:this.contentDiv.style.borderRightColor=this.contentDiv.style.borderLeftColor=this.contentDiv.style.borderColor=this.options.borderColor?this.options.borderColor:g[0].dataPoint.color?g[0].dataPoint.color:g[0].dataSeries.color?g[0].dataSeries.color:g[0].dataSeries._colorSet[g[0].index%g[0].dataSeries._colorSet.length],this.contentDiv.style.borderWidth=this.borderThickness||0===this.borderThickness?this.borderThickness+"px":"2px",this.contentDiv.style.borderRadius=this.cornerRadius||
0===this.cornerRadius?this.cornerRadius+"px":"5px",this.container.style.borderRadius=this.contentDiv.style.borderRadius,this.contentDiv.style.fontSize=this.fontSize||0===this.fontSize?this.fontSize+"px":"14px",this.contentDiv.style.color=this.fontColor?this.fontColor:"#000000",this.contentDiv.style.fontFamily=this.fontFamily?this.fontFamily:"Calibri, Arial, Georgia, serif;",this.contentDiv.style.fontWeight=this.fontWeight?this.fontWeight:"normal",this.contentDiv.style.fontStyle=this.fontStyle?this.fontStyle:
r?"italic":"normal"}catch(p){}"pie"===g[0].dataSeries.type||"doughnut"===g[0].dataSeries.type||"funnel"===g[0].dataSeries.type||"pyramid"===g[0].dataSeries.type||"bar"===g[0].dataSeries.type||"rangeBar"===g[0].dataSeries.type||"stackedBar"===g[0].dataSeries.type||"stackedBar100"===g[0].dataSeries.type?a=a-10-this.container.clientWidth:(a=g[0].dataSeries.axisX.convertValueToPixel(g[0].dataPoint.x)-this.container.clientWidth<<0,a-=10);0>a&&(a+=this.container.clientWidth+20);a+this.container.clientWidth>
Math.max(this.chart.container.clientWidth,this.chart.width)&&(a=Math.max(0,Math.max(this.chart.container.clientWidth,this.chart.width)-this.container.clientWidth));d=1!==g.length||this.shared||"line"!==g[0].dataSeries.type&&"stepLine"!==g[0].dataSeries.type&&"spline"!==g[0].dataSeries.type&&"area"!==g[0].dataSeries.type&&"stepArea"!==g[0].dataSeries.type&&"splineArea"!==g[0].dataSeries.type?"bar"===g[0].dataSeries.type||"rangeBar"===g[0].dataSeries.type||"stackedBar"===g[0].dataSeries.type||"stackedBar100"===
g[0].dataSeries.type?g[0].dataSeries.axisX.convertValueToPixel(g[0].dataPoint.x):d:g[0].dataSeries.axisY.convertValueToPixel(g[0].dataPoint.y);d=-d+10;0<d+this.container.clientHeight+5&&(d-=d+this.container.clientHeight+5-0);this.fixMozTransitionDelay(a,d);!this.animationEnabled||b?this.disableAnimation():(this.enableAnimation(),this.container.style.MozTransition=this.mozContainerTransition);this.container.style.left=a+"px";this.container.style.bottom=d+"px"}else this.hide(!1)}else this.hide()}};
$.prototype.highlightObjects=function(a){var d=this.chart.overlaidCanvasCtx;this.chart.resetOverlayedCanvas();d.clearRect(0,0,this.chart.width,this.chart.height);d.save();var b=this.chart.plotArea,c=0;d.beginPath();d.rect(b.x1,b.y1,b.x2-b.x1,b.y2-b.y1);d.clip();for(b=0;b<a.length;b++){var e=a[b];if((e=this.chart._eventManager.objectMap[e.dataSeries.dataPointIds[e.index]])&&e.objectType&&"dataPoint"===e.objectType){var c=this.chart.data[e.dataSeriesIndex],g=c.dataPoints[e.dataPointIndex],k=e.dataPointIndex;
!1===g.highlightEnabled||!0!==c.highlightEnabled&&!0!==g.highlightEnabled||("line"===c.type||"stepLine"===c.type||"spline"===c.type||"scatter"===c.type||"area"===c.type||"stepArea"===c.type||"splineArea"===c.type||"stackedArea"===c.type||"stackedArea100"===c.type||"rangeArea"===c.type||"rangeSplineArea"===c.type?(g=c.getMarkerProperties(k,e.x1,e.y1,this.chart.overlaidCanvasCtx),g.size=Math.max(1.5*g.size<<0,10),g.borderColor=g.borderColor||"#FFFFFF",g.borderThickness=g.borderThickness||Math.ceil(0.1*
g.size),ia.drawMarkers([g]),"undefined"!==typeof e.y2&&(g=c.getMarkerProperties(k,e.x1,e.y2,this.chart.overlaidCanvasCtx),g.size=Math.max(1.5*g.size<<0,10),g.borderColor=g.borderColor||"#FFFFFF",g.borderThickness=g.borderThickness||Math.ceil(0.1*g.size),ia.drawMarkers([g]))):"bubble"===c.type?(g=c.getMarkerProperties(k,e.x1,e.y1,this.chart.overlaidCanvasCtx),g.size=e.size,g.color="white",g.borderColor="white",d.globalAlpha=0.3,ia.drawMarkers([g]),d.globalAlpha=1):"column"===c.type||"stackedColumn"===
c.type||"stackedColumn100"===c.type||"bar"===c.type||"rangeBar"===c.type||"stackedBar"===c.type||"stackedBar100"===c.type||"rangeColumn"===c.type||"waterfall"===c.type?ea(d,e.x1,e.y1,e.x2,e.y2,"white",0,null,!1,!1,!1,!1,0.3):"pie"===c.type||"doughnut"===c.type?ja(d,e.center,e.radius,"white",c.type,e.startAngle,e.endAngle,0.3,e.percentInnerRadius):"funnel"===c.type||"pyramid"===c.type?ra(d,e.funnelSection,0.3,"white"):"candlestick"===c.type?(d.globalAlpha=1,d.strokeStyle=e.color,d.lineWidth=2*e.borderThickness,
c=0===d.lineWidth%2?0:0.5,d.beginPath(),d.moveTo(e.x3-c,Math.min(e.y2,e.y3)),d.lineTo(e.x3-c,Math.min(e.y1,e.y4)),d.stroke(),d.beginPath(),d.moveTo(e.x3-c,Math.max(e.y1,e.y4)),d.lineTo(e.x3-c,Math.max(e.y2,e.y3)),d.stroke(),ea(d,e.x1,Math.min(e.y1,e.y4),e.x2,Math.max(e.y1,e.y4),"transparent",2*e.borderThickness,e.color,!1,!1,!1,!1),d.globalAlpha=1):"ohlc"===c.type?(d.globalAlpha=1,d.strokeStyle=e.color,d.lineWidth=2*e.borderThickness,c=0===d.lineWidth%2?0:0.5,d.beginPath(),d.moveTo(e.x3-c,e.y2),d.lineTo(e.x3-
c,e.y3),d.stroke(),d.beginPath(),d.moveTo(e.x3,e.y1),d.lineTo(e.x1,e.y1),d.stroke(),d.beginPath(),d.moveTo(e.x3,e.y4),d.lineTo(e.x2,e.y4),d.stroke(),d.globalAlpha=1):"boxAndWhisker"===c.type?(d.save(),d.globalAlpha=1,d.strokeStyle=e.stemColor,d.lineWidth=2*e.stemThickness,0<e.stemThickness&&(d.beginPath(),d.moveTo(e.x3,e.y2+e.borderThickness/2),d.lineTo(e.x3,e.y1+e.whiskerThickness/2),d.stroke(),d.beginPath(),d.moveTo(e.x3,e.y4-e.whiskerThickness/2),d.lineTo(e.x3,e.y3-e.borderThickness/2),d.stroke()),
d.beginPath(),ea(d,e.x1-e.borderThickness/2,Math.max(e.y2+e.borderThickness/2,e.y3+e.borderThickness/2),e.x2+e.borderThickness/2,Math.min(e.y2-e.borderThickness/2,e.y3-e.borderThickness/2),"transparent",e.borderThickness,e.color,!1,!1,!1,!1),d.globalAlpha=1,d.strokeStyle=e.whiskerColor,d.lineWidth=2*e.whiskerThickness,0<e.whiskerThickness&&(d.beginPath(),d.moveTo(Math.floor(e.x3-e.whiskerLength/2),e.y4),d.lineTo(Math.ceil(e.x3+e.whiskerLength/2),e.y4),d.stroke(),d.beginPath(),d.moveTo(Math.floor(e.x3-
e.whiskerLength/2),e.y1),d.lineTo(Math.ceil(e.x3+e.whiskerLength/2),e.y1),d.stroke()),d.globalAlpha=1,d.strokeStyle=e.lineColor,d.lineWidth=2*e.lineThickness,0<e.lineThickness&&(d.beginPath(),d.moveTo(e.x1,e.y5),d.lineTo(e.x2,e.y5),d.stroke()),d.restore(),d.globalAlpha=1):"error"===c.type&&E(d,e.x1,e.y1,e.x2,e.y2,"white",e.whiskerProperties,e.stemProperties,e.isXYSwapped,0.3))}}d.restore();d.globalAlpha=1;d.beginPath()};$.prototype.getToolTipInnerHTML=function(a){a=a.entries;for(var d=null,b=null,
c=null,e=0,g="",k=!0,h=0;h<a.length;h++)if(a[h].dataSeries.toolTipContent||a[h].dataPoint.toolTipContent){k=!1;break}if(k&&(this.content&&"function"===typeof this.content||this.contentFormatter))a={chart:this.chart,toolTip:this.options,entries:a},d=this.contentFormatter?this.contentFormatter(a):this.content(a);else if(this.shared&&"none"!==this.chart.plotInfo.axisPlacement){for(var p=null,m="",h=0;h<a.length;h++)b=a[h].dataSeries,c=a[h].dataPoint,e=a[h].index,g="",0===h&&(k&&!this.content)&&(this.chart.axisX&&
0<this.chart.axisX.length?m+="undefined"!==typeof this.chart.axisX[0].labels[c.x]?this.chart.axisX[0].labels[c.x]:"{x}":this.chart.axisX2&&0<this.chart.axisX2.length&&(m+="undefined"!==typeof this.chart.axisX2[0].labels[c.x]?this.chart.axisX2[0].labels[c.x]:"{x}"),m+="</br>",m=this.chart.replaceKeywordsWithValue(m,c,b,e)),null===c.toolTipContent||"undefined"===typeof c.toolTipContent&&null===b.options.toolTipContent||("line"===b.type||"stepLine"===b.type||"spline"===b.type||"area"===b.type||"stepArea"===
b.type||"splineArea"===b.type||"column"===b.type||"bar"===b.type||"scatter"===b.type||"stackedColumn"===b.type||"stackedColumn100"===b.type||"stackedBar"===b.type||"stackedBar100"===b.type||"stackedArea"===b.type||"stackedArea100"===b.type||"waterfall"===b.type?(this.chart.axisX&&1<this.chart.axisX.length&&(g+=p!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?
this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>{name}:</span>&nbsp;&nbsp;{y}",p=b.axisXIndex):"bubble"===b.type?(this.chart.axisX&&1<this.chart.axisX.length&&(g+=p!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>{name}:</span>&nbsp;&nbsp;{y}, &nbsp;&nbsp;{z}"):
"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?(this.chart.axisX&&1<this.chart.axisX.length&&(g+=p!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>{name}:</span>&nbsp;&nbsp;{y[0]},&nbsp;{y[1]}"):"candlestick"===
b.type||"ohlc"===b.type?(this.chart.axisX&&1<this.chart.axisX.length&&(g+=p!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>{name}:</span><br/>Open: &nbsp;&nbsp;{y[0]}<br/>High: &nbsp;&nbsp;&nbsp;{y[1]}<br/>Low:&nbsp;&nbsp;&nbsp;{y[2]}<br/>Close: &nbsp;&nbsp;{y[3]}"):"boxAndWhisker"===
b.type&&(this.chart.axisX&&1<this.chart.axisX.length&&(g+=p!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>{name}:</span><br/>Minimum: &nbsp;&nbsp;{y[0]}<br/>Q1: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[1]}<br/>Q2: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[4]}<br/>Q3: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[2]}<br/>Maximum: &nbsp;{y[3]}"),
null===d&&(d=""),!0===this.reversed?(d=this.chart.replaceKeywordsWithValue(g,c,b,e)+d,h<a.length-1&&(d="</br>"+d)):(d+=this.chart.replaceKeywordsWithValue(g,c,b,e),h<a.length-1&&(d+="</br>")));null!==d&&(d=m+d)}else{b=a[0].dataSeries;c=a[0].dataPoint;e=a[0].index;if(null===c.toolTipContent||"undefined"===typeof c.toolTipContent&&null===b.options.toolTipContent)return null;"line"===b.type||"stepLine"===b.type||"spline"===b.type||"area"===b.type||"stepArea"===b.type||"splineArea"===b.type||"column"===
b.type||"bar"===b.type||"scatter"===b.type||"stackedColumn"===b.type||"stackedColumn100"===b.type||"stackedBar"===b.type||"stackedBar100"===b.type||"stackedArea"===b.type||"stackedArea100"===b.type||"waterfall"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(c.label?"{label}":"{x}")+":</span>&nbsp;&nbsp;{y}":"bubble"===b.type?g=c.toolTipContent?
c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(c.label?"{label}":"{x}")+":</span>&nbsp;&nbsp;{y}, &nbsp;&nbsp;{z}":"pie"===b.type||"doughnut"===b.type||"funnel"===b.type||"pyramid"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":
"'color:{color};'")+"\"'>"+(c.name?"{name}:</span>&nbsp;&nbsp;":c.label?"{label}:</span>&nbsp;&nbsp;":"</span>")+"{y}":"rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"error"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(c.label?"{label}":"{x}")+" :</span>&nbsp;&nbsp;{y[0]}, &nbsp;{y[1]}":
"candlestick"===b.type||"ohlc"===b.type?g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(c.label?"{label}":"{x}")+"</span><br/>Open: &nbsp;&nbsp;{y[0]}<br/>High: &nbsp;&nbsp;&nbsp;{y[1]}<br/>Low: &nbsp;&nbsp;&nbsp;&nbsp;{y[2]}<br/>Close: &nbsp;&nbsp;{y[3]}":"boxAndWhisker"===b.type&&(g=c.toolTipContent?c.toolTipContent:b.toolTipContent?b.toolTipContent:
this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(c.label?"{label}":"{x}")+"</span><br/>Minimum: &nbsp;&nbsp;{y[0]}<br/>Q1: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[1]}<br/>Q2: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[4]}<br/>Q3: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{y[2]}<br/>Maximum: &nbsp;{y[3]}");
null===d&&(d="");d+=this.chart.replaceKeywordsWithValue(g,c,b,e)}return d};$.prototype.enableAnimation=function(){if(!this.container.style.WebkitTransition){var a=this.getContainerTransition(this.containerTransitionDuration);this.container.style.WebkitTransition=a;this.container.style.MsTransition=a;this.container.style.transition=a;this.container.style.MozTransition=this.mozContainerTransition}};$.prototype.disableAnimation=function(){this.container.style.WebkitTransition&&(this.container.style.WebkitTransition=
"",this.container.style.MozTransition="",this.container.style.MsTransition="",this.container.style.transition="")};$.prototype.hide=function(a){this.container&&(this.container.style.display="none",this.currentSeriesIndex=-1,this._prevY=this._prevX=NaN,("undefined"===typeof a||a)&&this.chart.resetOverlayedCanvas())};$.prototype.show=function(a,d,b){this._updateToolTip(a,d,"undefined"===typeof b?!1:b)};$.prototype.fixMozTransitionDelay=function(a,d){if(20<this.chart._eventManager.lastObjectId)this.mozContainerTransition=
this.getContainerTransition(0);else{var b=parseFloat(this.container.style.left),b=isNaN(b)?0:b,c=parseFloat(this.container.style.bottom),c=isNaN(c)?0:c;10<Math.sqrt(Math.pow(b-a,2)+Math.pow(c-d,2))?this.mozContainerTransition=this.getContainerTransition(0.1):this.mozContainerTransition=this.getContainerTransition(0)}};$.prototype.getContainerTransition=function(a){return"left "+a+"s ease-out 0s, bottom "+a+"s ease-out 0s"};ha.prototype.reset=function(){this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=
[];this.previousDataPointEventObject=null;this.eventObjects=[];r&&(this.ghostCtx.clearRect(0,0,this.chart.width,this.chart.height),this.ghostCtx.beginPath())};ha.prototype.getNewObjectTrackingId=function(){return++this.lastObjectId};ha.prototype.mouseEventHandler=function(a){if("mousemove"===a.type||"click"===a.type){var d=[],b=Ra(a),c=null;if((c=this.chart.getObjectAtXY(b.x,b.y,!1))&&"undefined"!==typeof this.objectMap[c])if(c=this.objectMap[c],"dataPoint"===c.objectType){var e=this.chart.data[c.dataSeriesIndex],
g=e.dataPoints[c.dataPointIndex],h=c.dataPointIndex;c.eventParameter={x:b.x,y:b.y,dataPoint:g,dataSeries:e.options,dataPointIndex:h,dataSeriesIndex:e.index,chart:this.chart};c.eventContext={context:g,userContext:g,mouseover:"mouseover",mousemove:"mousemove",mouseout:"mouseout",click:"click"};d.push(c);c=this.objectMap[e.id];c.eventParameter={x:b.x,y:b.y,dataPoint:g,dataSeries:e.options,dataPointIndex:h,dataSeriesIndex:e.index,chart:this.chart};c.eventContext={context:e,userContext:e.options,mouseover:"mouseover",
mousemove:"mousemove",mouseout:"mouseout",click:"click"};d.push(this.objectMap[e.id])}else"legendItem"===c.objectType&&(e=this.chart.data[c.dataSeriesIndex],g=null!==c.dataPointIndex?e.dataPoints[c.dataPointIndex]:null,c.eventParameter={x:b.x,y:b.y,dataSeries:e.options,dataPoint:g,dataPointIndex:c.dataPointIndex,dataSeriesIndex:c.dataSeriesIndex,chart:this.chart},c.eventContext={context:this.chart.legend,userContext:this.chart.legend.options,mouseover:"itemmouseover",mousemove:"itemmousemove",mouseout:"itemmouseout",
click:"itemclick"},d.push(c));e=[];for(b=0;b<this.mouseoveredObjectMaps.length;b++){g=!0;for(c=0;c<d.length;c++)if(d[c].id===this.mouseoveredObjectMaps[b].id){g=!1;break}g?this.fireEvent(this.mouseoveredObjectMaps[b],"mouseout",a):e.push(this.mouseoveredObjectMaps[b])}this.mouseoveredObjectMaps=e;for(b=0;b<d.length;b++){e=!1;for(c=0;c<this.mouseoveredObjectMaps.length;c++)if(d[b].id===this.mouseoveredObjectMaps[c].id){e=!0;break}e||(this.fireEvent(d[b],"mouseover",a),this.mouseoveredObjectMaps.push(d[b]));
"click"===a.type?this.fireEvent(d[b],"click",a):"mousemove"===a.type&&this.fireEvent(d[b],"mousemove",a)}}};ha.prototype.fireEvent=function(a,d,b){if(a&&d){var c=a.eventParameter,e=a.eventContext,g=a.eventContext.userContext;g&&(e&&g[e[d]])&&g[e[d]].call(g,c);"mouseout"!==d?g.cursor&&g.cursor!==b.target.style.cursor&&(b.target.style.cursor=g.cursor):(b.target.style.cursor=this.chart._defaultCursor,delete a.eventParameter,delete a.eventContext);"click"===d&&("dataPoint"===a.objectType&&this.chart.pieDoughnutClickHandler)&&
this.chart.pieDoughnutClickHandler.call(this.chart.data[a.dataSeriesIndex],c);"click"===d&&("dataPoint"===a.objectType&&this.chart.funnelPyramidClickHandler)&&this.chart.funnelPyramidClickHandler.call(this.chart.data[a.dataSeriesIndex],c)}};ga.prototype.animate=function(a,d,b,c,e){var g=this;this.chart.isAnimating=!0;e=e||M.easing.linear;b&&this.animations.push({startTime:(new Date).getTime()+(a?a:0),duration:d,animationCallback:b,onComplete:c});for(a=[];0<this.animations.length;)if(d=this.animations.shift(),
b=(new Date).getTime(),c=0,d.startTime<=b&&(c=e(Math.min(b-d.startTime,d.duration),0,1,d.duration),c=Math.min(c,1),isNaN(c)||!isFinite(c))&&(c=1),1>c&&a.push(d),d.animationCallback(c),1<=c&&d.onComplete)d.onComplete();this.animations=a;0<this.animations.length?this.animationRequestId=this.chart.requestAnimFrame.call(window,function(){g.animate.call(g)}):this.chart.isAnimating=!1};ga.prototype.cancelAllAnimations=function(){this.animations=[];this.animationRequestId&&this.chart.cancelRequestAnimFrame.call(window,
this.animationRequestId);this.animationRequestId=null;this.chart.isAnimating=!1};var M={yScaleAnimation:function(a,d){if(0!==a){var b=d.dest,c=d.source.canvas,e=d.animationBase;b.drawImage(c,0,0,c.width,c.height,0,e-e*a,b.canvas.width/W,a*b.canvas.height/W)}},xScaleAnimation:function(a,d){if(0!==a){var b=d.dest,c=d.source.canvas,e=d.animationBase;b.drawImage(c,0,0,c.width,c.height,e-e*a,0,a*b.canvas.width/W,b.canvas.height/W)}},xClipAnimation:function(a,d){if(0!==a){var b=d.dest,c=d.source.canvas;
b.save();0<a&&b.drawImage(c,0,0,c.width*a,c.height,0,0,c.width*a/W,c.height/W);b.restore()}},fadeInAnimation:function(a,d){if(0!==a){var b=d.dest,c=d.source.canvas;b.save();b.globalAlpha=a;b.drawImage(c,0,0,c.width,c.height,0,0,b.canvas.width/W,b.canvas.height/W);b.restore()}},easing:{linear:function(a,d,b,c){return b*a/c+d},easeOutQuad:function(a,d,b,c){return-b*(a/=c)*(a-2)+d},easeOutQuart:function(a,d,b,c){return-b*((a=a/c-1)*a*a*a-1)+d},easeInQuad:function(a,d,b,c){return b*(a/=c)*a+d},easeInQuart:function(a,
d,b,c){return b*(a/=c)*a*a*a+d}}},ia={drawMarker:function(a,d,b,c,e,g,h,l){if(b){var p=1;b.fillStyle=g?g:"#000000";b.strokeStyle=h?h:"#000000";b.lineWidth=l?l:0;b.setLineDash&&b.setLineDash(R("solid",l));"circle"===c?(b.moveTo(a,d),b.beginPath(),b.arc(a,d,e/2,0,2*Math.PI,!1),g&&b.fill(),l&&(h?b.stroke():(p=b.globalAlpha,b.globalAlpha=0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=p))):"square"===c?(b.beginPath(),b.rect(a-e/2,d-e/2,e,e),g&&b.fill(),l&&(h?b.stroke():(p=b.globalAlpha,b.globalAlpha=
0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=p))):"triangle"===c?(b.beginPath(),b.moveTo(a-e/2,d+e/2),b.lineTo(a+e/2,d+e/2),b.lineTo(a,d-e/2),b.closePath(),g&&b.fill(),l&&(h?b.stroke():(p=b.globalAlpha,b.globalAlpha=0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=p)),b.beginPath()):"cross"===c&&(b.strokeStyle=g,b.lineWidth=e/4,b.beginPath(),b.moveTo(a-e/2,d-e/2),b.lineTo(a+e/2,d+e/2),b.stroke(),b.moveTo(a+e/2,d-e/2),b.lineTo(a-e/2,d+e/2),b.stroke())}},drawMarkers:function(a){for(var d=
0;d<a.length;d++){var b=a[d];ia.drawMarker(b.x,b.y,b.ctx,b.type,b.size,b.color,b.borderColor,b.borderThickness)}}};return p}();Na.Chart.version="v2.3.2 GA"})();
/*
excanvas is used to support IE678 which do not implement HTML5 Canvas Element. You can safely remove the following excanvas code if you don't need to support older browsers.
Copyright 2006 Google Inc. https://code.google.com/p/explorercanvas/
Licensed under the Apache License, Version 2.0
*/
document.createElement("canvas").getContext||function(){function V(){return this.context_||(this.context_=new C(this))}function W(a,b,c){var g=M.call(arguments,2);return function(){return a.apply(b,g.concat(M.call(arguments)))}}function N(a){return String(a).replace(/&/g,"&amp;").replace(/"/g,"&quot;")}function O(a){a.namespaces.g_vml_||a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML");a.namespaces.g_o_||a.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML");
a.styleSheets.ex_canvas_||(a=a.createStyleSheet(),a.owningElement.id="ex_canvas_",a.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}")}function X(a){var b=a.srcElement;switch(a.propertyName){case "width":b.getContext().clearRect();b.style.width=b.attributes.width.nodeValue+"px";b.firstChild.style.width=b.clientWidth+"px";break;case "height":b.getContext().clearRect(),b.style.height=b.attributes.height.nodeValue+"px",b.firstChild.style.height=b.clientHeight+
"px"}}function Y(a){a=a.srcElement;a.firstChild&&(a.firstChild.style.width=a.clientWidth+"px",a.firstChild.style.height=a.clientHeight+"px")}function D(){return[[1,0,0],[0,1,0],[0,0,1]]}function t(a,b){for(var c=D(),g=0;3>g;g++)for(var e=0;3>e;e++){for(var f=0,d=0;3>d;d++)f+=a[g][d]*b[d][e];c[g][e]=f}return c}function P(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=
a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.globalAlpha=a.globalAlpha;b.font=a.font;b.textAlign=a.textAlign;b.textBaseline=a.textBaseline;b.arcScaleX_=a.arcScaleX_;b.arcScaleY_=a.arcScaleY_;b.lineScale_=a.lineScale_}function Q(a){var b=a.indexOf("(",3),c=a.indexOf(")",b+1),b=a.substring(b+1,c).split(",");if(4!=b.length||"a"!=a.charAt(3))b[3]=1;return b}function E(a,b,c){return Math.min(c,Math.max(b,a))}function F(a,b,c){0>c&&c++;1<c&&c--;return 1>6*c?a+6*(b-a)*c:
1>2*c?b:2>3*c?a+6*(b-a)*(2/3-c):a}function G(a){if(a in H)return H[a];var b,c=1;a=String(a);if("#"==a.charAt(0))b=a;else if(/^rgb/.test(a)){c=Q(a);b="#";for(var g,e=0;3>e;e++)g=-1!=c[e].indexOf("%")?Math.floor(255*(parseFloat(c[e])/100)):+c[e],b+=v[E(g,0,255)];c=+c[3]}else if(/^hsl/.test(a)){e=c=Q(a);b=parseFloat(e[0])/360%360;0>b&&b++;g=E(parseFloat(e[1])/100,0,1);e=E(parseFloat(e[2])/100,0,1);if(0==g)g=e=b=e;else{var f=0.5>e?e*(1+g):e+g-e*g,d=2*e-f;g=F(d,f,b+1/3);e=F(d,f,b);b=F(d,f,b-1/3)}b="#"+
v[Math.floor(255*g)]+v[Math.floor(255*e)]+v[Math.floor(255*b)];c=c[3]}else b=Z[a]||a;return H[a]={color:b,alpha:c}}function C(a){this.m_=D();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.fillStyle=this.strokeStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=1*q;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=a;var b="width:"+a.clientWidth+"px;height:"+a.clientHeight+"px;overflow:hidden;position:absolute",
c=a.ownerDocument.createElement("div");c.style.cssText=b;a.appendChild(c);b=c.cloneNode(!1);b.style.backgroundColor="red";b.style.filter="alpha(opacity=0)";a.appendChild(b);this.element_=c;this.lineScale_=this.arcScaleY_=this.arcScaleX_=1}function R(a,b,c,g){a.currentPath_.push({type:"bezierCurveTo",cp1x:b.x,cp1y:b.y,cp2x:c.x,cp2y:c.y,x:g.x,y:g.y});a.currentX_=g.x;a.currentY_=g.y}function S(a,b){var c=G(a.strokeStyle),g=c.color,c=c.alpha*a.globalAlpha,e=a.lineScale_*a.lineWidth;1>e&&(c*=e);b.push("<g_vml_:stroke",
' opacity="',c,'"',' joinstyle="',a.lineJoin,'"',' miterlimit="',a.miterLimit,'"',' endcap="',$[a.lineCap]||"square",'"',' weight="',e,'px"',' color="',g,'" />')}function T(a,b,c,g){var e=a.fillStyle,f=a.arcScaleX_,d=a.arcScaleY_,k=g.x-c.x,n=g.y-c.y;if(e instanceof w){var h=0,l=g=0,u=0,m=1;if("gradient"==e.type_){h=e.x1_/f;c=e.y1_/d;var p=s(a,e.x0_/f,e.y0_/d),h=s(a,h,c),h=180*Math.atan2(h.x-p.x,h.y-p.y)/Math.PI;0>h&&(h+=360);1E-6>h&&(h=0)}else p=s(a,e.x0_,e.y0_),g=(p.x-c.x)/k,l=(p.y-c.y)/n,k/=f*q,
n/=d*q,m=x.max(k,n),u=2*e.r0_/m,m=2*e.r1_/m-u;f=e.colors_;f.sort(function(a,b){return a.offset-b.offset});d=f.length;p=f[0].color;c=f[d-1].color;k=f[0].alpha*a.globalAlpha;a=f[d-1].alpha*a.globalAlpha;for(var n=[],r=0;r<d;r++){var t=f[r];n.push(t.offset*m+u+" "+t.color)}b.push('<g_vml_:fill type="',e.type_,'"',' method="none" focus="100%"',' color="',p,'"',' color2="',c,'"',' colors="',n.join(","),'"',' opacity="',a,'"',' g_o_:opacity2="',k,'"',' angle="',h,'"',' focusposition="',g,",",l,'" />')}else e instanceof
I?k&&n&&b.push("<g_vml_:fill",' position="',-c.x/k*f*f,",",-c.y/n*d*d,'"',' type="tile"',' src="',e.src_,'" />'):(e=G(a.fillStyle),b.push('<g_vml_:fill color="',e.color,'" opacity="',e.alpha*a.globalAlpha,'" />'))}function s(a,b,c){a=a.m_;return{x:q*(b*a[0][0]+c*a[1][0]+a[2][0])-r,y:q*(b*a[0][1]+c*a[1][1]+a[2][1])-r}}function z(a,b,c){isFinite(b[0][0])&&(isFinite(b[0][1])&&isFinite(b[1][0])&&isFinite(b[1][1])&&isFinite(b[2][0])&&isFinite(b[2][1]))&&(a.m_=b,c&&(a.lineScale_=aa(ba(b[0][0]*b[1][1]-b[0][1]*
b[1][0]))))}function w(a){this.type_=a;this.r1_=this.y1_=this.x1_=this.r0_=this.y0_=this.x0_=0;this.colors_=[]}function I(a,b){if(!a||1!=a.nodeType||"IMG"!=a.tagName)throw new A("TYPE_MISMATCH_ERR");if("complete"!=a.readyState)throw new A("INVALID_STATE_ERR");switch(b){case "repeat":case null:case "":this.repetition_="repeat";break;case "repeat-x":case "repeat-y":case "no-repeat":this.repetition_=b;break;default:throw new A("SYNTAX_ERR");}this.src_=a.src;this.width_=a.width;this.height_=a.height}
function A(a){this.code=this[a];this.message=a+": DOM Exception "+this.code}var x=Math,k=x.round,J=x.sin,K=x.cos,ba=x.abs,aa=x.sqrt,q=10,r=q/2;navigator.userAgent.match(/MSIE ([\d.]+)?/);var M=Array.prototype.slice;O(document);var U={init:function(a){a=a||document;a.createElement("canvas");a.attachEvent("onreadystatechange",W(this.init_,this,a))},init_:function(a){a=a.getElementsByTagName("canvas");for(var b=0;b<a.length;b++)this.initElement(a[b])},initElement:function(a){if(!a.getContext){a.getContext=
V;O(a.ownerDocument);a.innerHTML="";a.attachEvent("onpropertychange",X);a.attachEvent("onresize",Y);var b=a.attributes;b.width&&b.width.specified?a.style.width=b.width.nodeValue+"px":a.width=a.clientWidth;b.height&&b.height.specified?a.style.height=b.height.nodeValue+"px":a.height=a.clientHeight}return a}};U.init();for(var v=[],d=0;16>d;d++)for(var B=0;16>B;B++)v[16*d+B]=d.toString(16)+B.toString(16);var Z={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",
bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",
darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",
ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",
mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",
peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"},
H={},L={},$={butt:"flat",round:"round"},d=C.prototype;d.clearRect=function(){this.textMeasureEl_&&(this.textMeasureEl_.removeNode(!0),this.textMeasureEl_=null);this.element_.innerHTML=""};d.beginPath=function(){this.currentPath_=[]};d.moveTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"moveTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.lineTo=function(a,b){var c=s(this,a,b);this.currentPath_.push({type:"lineTo",x:c.x,y:c.y});this.currentX_=c.x;this.currentY_=c.y};d.bezierCurveTo=
function(a,b,c,g,e,f){e=s(this,e,f);a=s(this,a,b);c=s(this,c,g);R(this,a,c,e)};d.quadraticCurveTo=function(a,b,c,g){a=s(this,a,b);c=s(this,c,g);g={x:this.currentX_+2/3*(a.x-this.currentX_),y:this.currentY_+2/3*(a.y-this.currentY_)};R(this,g,{x:g.x+(c.x-this.currentX_)/3,y:g.y+(c.y-this.currentY_)/3},c)};d.arc=function(a,b,c,g,e,f){c*=q;var d=f?"at":"wa",k=a+K(g)*c-r,n=b+J(g)*c-r;g=a+K(e)*c-r;e=b+J(e)*c-r;k!=g||f||(k+=0.125);a=s(this,a,b);k=s(this,k,n);g=s(this,g,e);this.currentPath_.push({type:d,
x:a.x,y:a.y,radius:c,xStart:k.x,yStart:k.y,xEnd:g.x,yEnd:g.y})};d.rect=function(a,b,c,g){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath()};d.strokeRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+g);this.lineTo(a,b+g);this.closePath();this.stroke();this.currentPath_=e};d.fillRect=function(a,b,c,g){var e=this.currentPath_;this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+
c,b+g);this.lineTo(a,b+g);this.closePath();this.fill();this.currentPath_=e};d.createLinearGradient=function(a,b,c,g){var e=new w("gradient");e.x0_=a;e.y0_=b;e.x1_=c;e.y1_=g;return e};d.createRadialGradient=function(a,b,c,g,e,f){var d=new w("gradientradial");d.x0_=a;d.y0_=b;d.r0_=c;d.x1_=g;d.y1_=e;d.r1_=f;return d};d.drawImage=function(a,b){var c,g,e,d,r,y,n,h;e=a.runtimeStyle.width;d=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var l=a.width,u=a.height;a.runtimeStyle.width=
e;a.runtimeStyle.height=d;if(3==arguments.length)c=arguments[1],g=arguments[2],r=y=0,n=e=l,h=d=u;else if(5==arguments.length)c=arguments[1],g=arguments[2],e=arguments[3],d=arguments[4],r=y=0,n=l,h=u;else if(9==arguments.length)r=arguments[1],y=arguments[2],n=arguments[3],h=arguments[4],c=arguments[5],g=arguments[6],e=arguments[7],d=arguments[8];else throw Error("Invalid number of arguments");var m=s(this,c,g),p=[];p.push(" <g_vml_:group",' coordsize="',10*q,",",10*q,'"',' coordorigin="0,0"',' style="width:',
10,"px;height:",10,"px;position:absolute;");if(1!=this.m_[0][0]||this.m_[0][1]||1!=this.m_[1][1]||this.m_[1][0]){var t=[];t.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",k(m.x/q),",","Dy=",k(m.y/q),"");var v=s(this,c+e,g),w=s(this,c,g+d);c=s(this,c+e,g+d);m.x=x.max(m.x,v.x,w.x,c.x);m.y=x.max(m.y,v.y,w.y,c.y);p.push("padding:0 ",k(m.x/q),"px ",k(m.y/q),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",t.join(""),", sizingmethod='clip');")}else p.push("top:",
k(m.y/q),"px;left:",k(m.x/q),"px;");p.push(' ">','<g_vml_:image src="',a.src,'"',' style="width:',q*e,"px;"," height:",q*d,'px"',' cropleft="',r/l,'"',' croptop="',y/u,'"',' cropright="',(l-r-n)/l,'"',' cropbottom="',(u-y-h)/u,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",p.join(""))};d.stroke=function(a){var b=[];b.push("<g_vml_:shape",' filled="',!!a,'"',' style="position:absolute;width:',10,"px;height:",10,'px;"',' coordorigin="0,0"',' coordsize="',10*q,",",10*q,'"',
' stroked="',!a,'"',' path="');for(var c={x:null,y:null},d={x:null,y:null},e=0;e<this.currentPath_.length;e++){var f=this.currentPath_[e];switch(f.type){case "moveTo":b.push(" m ",k(f.x),",",k(f.y));break;case "lineTo":b.push(" l ",k(f.x),",",k(f.y));break;case "close":b.push(" x ");f=null;break;case "bezierCurveTo":b.push(" c ",k(f.cp1x),",",k(f.cp1y),",",k(f.cp2x),",",k(f.cp2y),",",k(f.x),",",k(f.y));break;case "at":case "wa":b.push(" ",f.type," ",k(f.x-this.arcScaleX_*f.radius),",",k(f.y-this.arcScaleY_*
f.radius)," ",k(f.x+this.arcScaleX_*f.radius),",",k(f.y+this.arcScaleY_*f.radius)," ",k(f.xStart),",",k(f.yStart)," ",k(f.xEnd),",",k(f.yEnd))}if(f){if(null==c.x||f.x<c.x)c.x=f.x;if(null==d.x||f.x>d.x)d.x=f.x;if(null==c.y||f.y<c.y)c.y=f.y;if(null==d.y||f.y>d.y)d.y=f.y}}b.push(' ">');a?T(this,b,c,d):S(this,b);b.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",b.join(""))};d.fill=function(){this.stroke(!0)};d.closePath=function(){this.currentPath_.push({type:"close"})};d.save=function(){var a=
{};P(this,a);this.aStack_.push(a);this.mStack_.push(this.m_);this.m_=t(D(),this.m_)};d.restore=function(){this.aStack_.length&&(P(this.aStack_.pop(),this),this.m_=this.mStack_.pop())};d.translate=function(a,b){z(this,t([[1,0,0],[0,1,0],[a,b,1]],this.m_),!1)};d.rotate=function(a){var b=K(a);a=J(a);z(this,t([[b,a,0],[-a,b,0],[0,0,1]],this.m_),!1)};d.scale=function(a,b){this.arcScaleX_*=a;this.arcScaleY_*=b;z(this,t([[a,0,0],[0,b,0],[0,0,1]],this.m_),!0)};d.transform=function(a,b,c,d,e,f){z(this,t([[a,
b,0],[c,d,0],[e,f,1]],this.m_),!0)};d.setTransform=function(a,b,c,d,e,f){z(this,[[a,b,0],[c,d,0],[e,f,1]],!0)};d.drawText_=function(a,b,c,d,e){var f=this.m_;d=0;var r=1E3,t=0,n=[],h;h=this.font;if(L[h])h=L[h];else{var l=document.createElement("div").style;try{l.font=h}catch(u){}h=L[h]={style:l.fontStyle||"normal",variant:l.fontVariant||"normal",weight:l.fontWeight||"normal",size:l.fontSize||10,family:l.fontFamily||"sans-serif"}}var l=h,m=this.element_;h={};for(var p in l)h[p]=l[p];p=parseFloat(m.currentStyle.fontSize);
m=parseFloat(l.size);"number"==typeof l.size?h.size=l.size:-1!=l.size.indexOf("px")?h.size=m:-1!=l.size.indexOf("em")?h.size=p*m:-1!=l.size.indexOf("%")?h.size=p/100*m:-1!=l.size.indexOf("pt")?h.size=m/0.75:h.size=p;h.size*=0.981;p=h.style+" "+h.variant+" "+h.weight+" "+h.size+"px "+h.family;m=this.element_.currentStyle;l=this.textAlign.toLowerCase();switch(l){case "left":case "center":case "right":break;case "end":l="ltr"==m.direction?"right":"left";break;case "start":l="rtl"==m.direction?"right":
"left";break;default:l="left"}switch(this.textBaseline){case "hanging":case "top":t=h.size/1.75;break;case "middle":break;default:case null:case "alphabetic":case "ideographic":case "bottom":t=-h.size/2.25}switch(l){case "right":d=1E3;r=0.05;break;case "center":d=r=500}b=s(this,b+0,c+t);n.push('<g_vml_:line from="',-d,' 0" to="',r,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!e,'" stroked="',!!e,'" style="position:absolute;width:1px;height:1px;">');e?S(this,n):T(this,n,{x:-d,y:0},
{x:r,y:h.size});e=f[0][0].toFixed(3)+","+f[1][0].toFixed(3)+","+f[0][1].toFixed(3)+","+f[1][1].toFixed(3)+",0,0";b=k(b.x/q)+","+k(b.y/q);n.push('<g_vml_:skew on="t" matrix="',e,'" ',' offset="',b,'" origin="',d,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',N(a),'" style="v-text-align:',l,";font:",N(p),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",n.join(""))};d.fillText=function(a,b,c,d){this.drawText_(a,b,c,d,!1)};d.strokeText=function(a,
b,c,d){this.drawText_(a,b,c,d,!0)};d.measureText=function(a){this.textMeasureEl_||(this.element_.insertAdjacentHTML("beforeEnd",'<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>'),this.textMeasureEl_=this.element_.lastChild);var b=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(b.createTextNode(a));return{width:this.textMeasureEl_.offsetWidth}};d.clip=function(){};
d.arcTo=function(){};d.createPattern=function(a,b){return new I(a,b)};w.prototype.addColorStop=function(a,b){b=G(b);this.colors_.push({offset:a,color:b.color,alpha:b.alpha})};d=A.prototype=Error();d.INDEX_SIZE_ERR=1;d.DOMSTRING_SIZE_ERR=2;d.HIERARCHY_REQUEST_ERR=3;d.WRONG_DOCUMENT_ERR=4;d.INVALID_CHARACTER_ERR=5;d.NO_DATA_ALLOWED_ERR=6;d.NO_MODIFICATION_ALLOWED_ERR=7;d.NOT_FOUND_ERR=8;d.NOT_SUPPORTED_ERR=9;d.INUSE_ATTRIBUTE_ERR=10;d.INVALID_STATE_ERR=11;d.SYNTAX_ERR=12;d.INVALID_MODIFICATION_ERR=
13;d.NAMESPACE_ERR=14;d.INVALID_ACCESS_ERR=15;d.VALIDATION_ERR=16;d.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=U;CanvasRenderingContext2D=C;CanvasGradient=w;CanvasPattern=I;DOMException=A}();
/*eslint-enable*/
/*jshint ignore:end*/

10
data/getSweep.json Normal file

File diff suppressed because one or more lines are too long

115
data/help.html Normal file
View File

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<title>[RBNSpyBox]</title>
<link rel="stylesheet" type="text/css" href="styles.css" media="all" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="support.js"></script>
<script type="text/javascript">
// At this point the page is running
$(document).ready(function () {
// Update the details
updateNameVersion();
});
</script>
</head>
<body>
<div class='norm'>
<h1>
<span id="programName">[programName]</span>&nbsp;<span id="programVersion">[programVersion]</span>
</h1>
<button id="homeButton" onclick="window.location.href='index.html';">Home</button>
<button id="friendsButton" onclick="window.location.href='friends.html';">Friends</button>
<button id="settingsButton" onclick="window.location.href='settings.html';">Settings</button>
<button id="backupRestoreButton" onclick="window.location.href='backupRestore.html';">Backup/Restore</button>
<button id="helpButton" onclick="window.location.href='help.html';">Help</button>
<button id="aboutButton" onclick="window.location.href='about.html';">About</button>
<br />
<br />
<!-- About Div -->
<div id="aboutDiv">
<hr />
<h1>Home</h1>
<p style="max-width:700px;">
The Home page displays the current status of the RBNSpyBox and the status of all your "Friends".
This page will automatically refresh to display the latest "Friend" activity.
</p>
<h1>Friends</h1>
<p style="max-width:700px;">
The Friends page is used to maintain your list of "Friends", you can add upto a maximum of 50.
</p>
<p style="max-width:700px;">
RBNSpyBox comes pre-loaded with a number of example "Friends" and you will see these listed
on the Friends page.
</p>
<p style="max-width:700px;">
To update a "Friend" simply change the values in the boxes and hit the associated "Update" button.
</p>
<p style="max-width:700px;">
To remove a "Friend" simply hit the "Remove" button.
</p>
<p style="max-width:700px;">
At the bottom of the page you will find an area titled "Add New Friend".
</p>
<p style="max-width:700px;">
Here you can specify a callsign, name, comment and sound to be used when that particular "Friend"
is spotted. Callsigns can contain wildcards. Typically, taking the UK callsign G0PJO as an
example you will want to spot a "Friend" while working abroad, while within one of
the UK regions such as Wales, and possibly when out operating portable. To do this you can
specify the callsign as *G*0PJO* which will catch all occurrences. All fields are limited
to 20 characters. The associated sound can be disabled by unchecking the check-box.
</p>
<h1>Settings</h1>
<p style="max-width:700px;">
The Settings page is used to configure the RBNSpyBox. The main settings relate to Wi-Fi connectivity
but also includes settings for the Reverse Beacon Network interface and some miscellaneous settings.
</p>
<p style="max-width:700px;">
To commit all changes hit the "Update" button.
</p>
<h1>Backup/Restore</h1>
<p style="max-width:700px;">
The Backup/Restore page is used to backup and restore your list of "Friends".
</p>
<p style="max-width:700px;">
The "Friends" are backup they are persisted as a simple XML file. This file should be
securely saved away. To restore the friends simply drag and drop the file to the box
comtaining the text "Here...".
</p>
<hr/>
</div>
</div>
<div id=footer>Copyright &copy; <span id="programCopyright"></span></div>
</body>
</html>

1788
data/index.html Normal file

File diff suppressed because it is too large Load Diff

1264
data/index2.html Normal file

File diff suppressed because it is too large Load Diff

12
data/jquery-3.2.1.min.js vendored Normal file

File diff suppressed because one or more lines are too long

BIN
data/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

200
data/settings.html Normal file
View File

@ -0,0 +1,200 @@
<!DOCTYPE html>
<html>
<head>
<title>[RBNSpyBox]</title>
<link rel="stylesheet" type="text/css" href="styles.css" media="all" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="text/javascript" src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="support.js"></script>
<script type="text/javascript">
function updateForm() {
// Get the form data
$.ajax({
url: "getSettingsData"
}).done(function (data) {
// We have the data, process it.
var settingsData = data.documentElement;
$("#hostnameInput").val(settingsData.getAttribute("Hostname"));
$("#ssidInput").val(settingsData.getAttribute("WiFiSSID"));
$("#passwordInput").val(settingsData.getAttribute("WiFiPassword"));
$("#callsignInput").val(settingsData.getAttribute("Callsign"));
$("#ledEnabledInput").attr('checked',"true" === settingsData.getAttribute("LEDEnabled"));
$("#soundEnabledInput").attr('checked',"true" === settingsData.getAttribute("SoundEnabled"));
$("#volumeInput").val(settingsData.getAttribute("Volume"));
$("#friendCycleCountInput").val(settingsData.getAttribute("FriendCycleCount"));
}).fail(function (jqXHR, textStatus) {
alert("Contact with host lost!" + textStatus);
});
}
/*
// This method is called when the Listen button is clicked. Here
// we simply ask the server to play the sound.
*/
function doListenWithVolume() {
// Play the sound
$.ajax({
url: "doListenWithVolume?volume=" + $("#volumeInput").val()
}).done(function (data) { });
}
// At this point the page is running
$(document).ready(function () {
// Do we need to hide the refresh button?
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
if (true === isSafari)
$('#refreshRow').hide();
updateNameVersion();
updateForm();
updateSSIDs();
});
</script>
</head>
<body>
<div class='norm'>
<h1>
<span id="programName">[programName]</span>&nbsp;<span id="programVersion">[programVersion]</span>
</h1>
<button id="homeButton" onclick="window.location.href='index.html';">Home</button>
<button id="friendsButton" onclick="window.location.href='friends.html';">Friends</button>
<button id="settingsButton" onclick="window.location.href='settings.html';">Settings</button>
<button id="backupRestoreButton" onclick="window.location.href='backupRestore.html';">Backup/Restore</button>
<button id="helpButton" onclick="window.location.href='help.html';">Help</button>
<button id="aboutButton" onclick="window.location.href='about.html';">About</button>
<br />
<br />
<!-- Settings Div -->
<div id="settingsDiv">
<hr />
<h1>Settings</h1>
<form action="/doSettingsUpdate" method="get" style="margin-bottom:20px;">
<fieldset>
<legend>Wi-Fi</legend>
<table>
<tr>
<td>Hostname:</td>
<td>
<input type="text" name="hostnameInput" id="hostnameInput" placeholder="Hostname" minlength="1" maxlength="15" />
</td>
</tr>
<tr>
<td>Wi-Fi SSID:</td>
<td>
<input type="text" name="ssidInput" id="ssidInput" placeholder="32 characters maximum" minlength="1" maxlength="32" list="availableSSIDS" required />
<datalist id="availableSSIDS"></datalist>
</td>
</tr>
<tr id="refreshRow">
<td></td>
<td>
<button type="button" id="refreshButton" onclick="updateSSIDs()">Refresh Available SSIDs</button>
</td>
</tr>
<tr>
<td>Wi-Fi Password:</td>
<td>
<input type="password" name="passwordInput" id="passwordInput" placeholder="64 characters maximum" maxlength="64" />
</td>
</tr>
</table>
</fieldset>
<br />
<fieldset>
<legend>Reverse Beacon Network</legend>
<table>
<tr>
<td>Callsign:</td>
<td>
<input type="text" name="callsignInput" id="callsignInput" placeholder="Callsign" minlength="1" maxlength="32" required />
</td>
</tr>
</table>
</fieldset>
<br />
<fieldset>
<legend>Miscellaneous</legend>
<table>
<tr>
<td>Friend Cycle Count:</td>
<td>
<input type="number" name="friendCycleCountInput" id="friendCycleCountInput" min="1" max="50" step="1" />
</td>
</tr>
<tr>
<td>LED Enabled:</td>
<td>
<input type="checkbox" name="ledEnabledInput" id="ledEnabledInput" />
</td>
</tr>
<tr>
<td>Sound Enabled:</td>
<td>
<input type="checkbox" name="soundEnabledInput" id="soundEnabledInput" />
</td>
</tr>
<tr>
<td>Volume:</td>
<td>
<input type="range" name="volumeInput" id="volumeInput" min="0" max="30" step="1" />
<button type="button" onclick="doListenWithVolume();">Listen!</button>
</td>
</tr>
</table>
</fieldset>
<br />
<table>
<tr>
<td>Update</td>
<td>
<button>Update</button>
</td>
</tr>
</table>
</form>
</div>
</div>
<div id=footer>Copyright &copy; <span id="programCopyright"></span></div>
</body>
</html>

333
data/styles.css Normal file
View File

@ -0,0 +1,333 @@
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
margin: 0px;
padding: 0px;
}
#footer {
position: fixed;
bottom: 0;
height: 18px;
width: 100%;
text-align: center;
color: black;
clear: both;
margin-right: 30px;
margin-top: 30px;
}
#volumeSlider {
max-width: 100px;
}
.norm {
width: 95%;
height: 3vh;
font-family: Arial, Helvetica, sans-serif;
margin-top: 5px;
padding-left: 45px;
padding-top: 5px;
border-left-width: 1px;
border-left-style: solid;
border-left-color: #BED6E2;
}
.Grid {
display: flex;
}
.Grid-cell {
flex: 1;
min-width:500px;
min-height: 600px;
width: 90vw;
height: 80vh;
}
.Grid-cell-side {
flex: 0 0 220px;
height: 600;
border: solid 1px grey;
background-color: lightgrey;
font-size: 16px;
}
.Grid-cell-connection {
flex: 1;
border: solid 1px grey;
background-color: lightgrey;
font-size: 12px;
}
h1 {
color: black;
font-weight: bolder;
background-repeat: no-repeat;
margin: 10px;
font-size: 22px;
}
td, th {
color: black;
font-weight: lighter;
background-repeat: no-repeat;
margin: 0px;
font-size: 18px;
border-right: solid 1px white;
padding-right: 13px;
padding-left: 13px;
}
input[type="color"],
input[type="date"],
input[type="datetime"],
input[type="datetime-local"],
input[type="email"],
input[type="month"],
input[type="number"],
input[type="password"],
input[type="search"],
input[type="tel"],
input[type="text"],
input[type="time"],
input[type="url"],
input[type="week"],
select:focus,
textarea {
font-size: 16px;
}
input[type="number"] {
-webkit-appearance: none;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
select:focus,
select,
textarea,
input {
font-size: 16px;
}
}
.value {
border: 0;
text-align: right;
width: 50px;
display: inline-block;
}
.value-display {
border: 0;
text-align: right;
width: 75px;
display: inline-block;
background-color: lightgrey;
}
.sweep-display {
border: 0;
text-align: right;
width: 65px;
display: inline-block;
background-color: lightgrey;
}
.message-display {
border: 0;
text-align: left;
width: 200px;
height: 200px;
display: inline-block;
background-color: lightgrey;
}
.value-input {
border: solid 1px grey;
text-align: right;
width: 73px;
display: inline-block;
}
.checkbox-input {
border: solid 1px grey;
text-align: right;
width: 12px;
display: inline-block;
}
.select-input {
border: solid 1px grey;
text-align: right;
text-align-last: right;
width: 77px;
display: inline-block;
}
.Sweep-select {
border: solid 1px grey;
text-align: left;
text-align-last: left;
width: 150px;
display: inline-block;
color: black;
font-weight: bolder;
background-repeat: no-repeat;
margin: 10px;
}
.select-option {
text-align: right;
text-align-last: right;
}
.sweep-option {
text-align: left;
text-align-last: left;
}
.inc-button {
width: 60px;
height: 30px;
font-size: 14px;
float: right;
margin-right:60px;
margin-top:3px;
}
.on-button {
width: 60px;
height: 33px;
font-size: 14px;
float: right;
margin-right:0px;
margin-top:3px;
}
.setting {
width: 210px;
}
.setting-label {
width: 80px;
display: inline-block;
}
.setting-widelabel {
width: 138px;
display: inline-block;
}
.sigTitle {
font-size: 36px;
font-weight: bold;
}
.sigFreq {
font-weight: bolder;
font-size: 32px;
margin: 0;
padding: 0;
letter-spacing: -10px;
width: 500px;
}
.sigFreqLabel {
width: 180px;
display: inline-block;
font-size: 32px;
margin: 0;
padding: 0;
letter-spacing: normal;
}
.sigFreqText {
display: inline-block;
font-size: 32px;
margin: 0;
padding: 0;
letter-spacing: normal;
}
input.sigFreqInput {
border: solid 1px grey;
text-align: right;
width: 18px;
display: inline-block;
font-size: 32px;
margin: 0;
padding: 0;
}
input.sigFrequencyInput {
border: solid 1px grey;
text-align: right;
width: 180px;
display: inline-block;
font-size: 32px;
margin: 0;
padding: 0;
}
input.sigFreqInput::-webkit-outer-spin-button,
input.sigFreqInput::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input.sigFreqInput {
-moz-appearance: textfield;
}
.sigLevelLabel {
width: 180px;
display: inline-block;
font-size: 32px;
letter-spacing: normal;
}
input.sigLevelInput {
border: solid 1px grey;
text-align: right;
width: 180px;
display: inline-block;
font-size: 32px;
}
TH, .bold {
background-color: lightblue;
color: black;
}
.slider {
-webkit-appearance: none;
width: 340px;
height: 15px;
border-radius: 5px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}
.slider::-moz-range-thumb {
width: 25px;
height: 25px;
border-radius: 50%;
background: #4CAF50;
cursor: pointer;
}

101
data/support.js Normal file
View File

@ -0,0 +1,101 @@
function updateNameVersion() {
// Get the program name and version
$.ajax({
url: "getNameVersion"
}).done(function (data) {
// We have the data, process it.
var indexNameVersionETC = data.documentElement;
$("#programName").html(indexNameVersionETC.getAttribute("Name"));
$("#programVersion").html(indexNameVersionETC.getAttribute("Version"));
$("#programCopyright").html(indexNameVersionETC.getAttribute("Copyright"));
document.title = indexNameVersionETC.getAttribute("Name");
}).fail(function (jqXHR, textStatus) {
alert("Contact with host lost!" + textStatus);
});
}
// Home.html specific
// This method is called to add a number of table headers to a table object.
// It is passed the div into which to draw and it will create the new table
// and return a reference to the associated <tbody>.
function buildTable(divToUse, titles) {
// Clean out the div
divToUse.empty();
// Create the main table objects
var table = $("<table/>");
var thead = $("<thead/>");
var tbody = $("<tbody/>");
var tr = $("<tr/>");
// Append them together
tr.appendTo(thead);
thead.appendTo(table);
tbody.appendTo(table);
// Process and add all the table headers
for (var i = 0; i < titles.length; i++) {
var th = $("<th/>");
var span = $("<span/>");
span.html(titles[i]);
span.appendTo(th);
th.appendTo(tr);
}
// Append them to the table
table.appendTo(divToUse);
// Return the table body
return tbody;
}
// Settings.html specific
function updateSSIDs() {
// Get the program name and version
$.ajax({
url: "getSSIDs"
}).done(function (data) {
// We have the data, process it.
$("#availableSSIDS").empty();
var ssids = data.documentElement.getElementsByTagName("SSID");
if (0 === ssids.length) {
alert("No SSIDS visible!");
}
else {
for (var i = 0; i < ssids.length; i++) {
var ssid = ssids[i];
$("<option/>").val(ssid.getAttribute("Name")).html(ssid.getAttribute("Name")).appendTo("#availableSSIDS");
}
}
}).fail(function (jqXHR, textStatus) {
alert("Contact with host lost!" + textStatus);
});
}

304
marker.cpp Normal file
View File

@ -0,0 +1,304 @@
/*
* "Marker.cpp" Contains the code for the "Marker" class functions
*/
#include "marker.h" // Class definition
/*
* There are two constructors; the first simply creates an uninitialized
* object, and the second one fills in the address of the display object,
* the address of the marker's sprite and sets the marker's number. The
* "Init" function actually does the work.
*/
Marker::Marker () {} // Create an uninitialized object
Marker::Marker ( TFT_eSprite* spr, uint8_t marker )
{
Init ( spr, marker );
}
/*
* The "Init" function does all the work of the real constructor but can also
* be called to initialized a previously uninitialized object.
*/
void Marker::Init ( TFT_eSprite* spr, uint8_t marker )
{
/*
* Create our "sprite" and set it up. The actual sprites are created in the main
* program. It would make more sense to creat them in here, but I haven't figured
* out how to do that without causing crashes or other goofy behaviors.
*/
_sprite = spr; // Pointer to our sprite object
_sprite->setAttribute ( PSRAM_ENABLE, false ); // Don't use PSRAM on the WROVERs
_sprite->createSprite ( MARKER_SPRITE_WIDTH, MARKER_SPRITE_HEIGHT ); // Set the size
/*
* Set some default conditions for the time being:
*/
_index = marker - 1; // Save marker number
_enabled = false; // Turn it off for now
_status = 0; // White, disabled & invisible
_x = 0; // No real position yet
_y = 0;
_frequency = 0; // Don't know the frequency
_mode = MKR_PEAK; // Assume peak frequency mode
/*
* Set the pivot point for the marker - this is the point used when pushing
*/
_sprite->setPivot ( X_MARKER_OFFSET, Y_MARKER_OFFSET );
}
/*
* "Paint" Remembers the "x"and "y" coordinates and sends the marker to the display.
* Note x and y are relative to the target sprite
*/
void Marker::Paint ( TFT_eSprite *target, uint16_t x, uint16_t y )
{
if ( _enabled )
{
_x = x + 1; // Remember location
_y = y;
target->setPivot ( _x, _y ); // Set pivot point in target.
// Push rotated checks the bounds of
// the target sprite
_sprite->pushRotated ( target, 0, BLACK ); // Send the sprite to the target sprite
}
else // Not enabled
return; // Do nothing
}
/*
* Set or retreive the mode for the marker.
*/
void Marker::Mode ( uint8_t mode )
{
_mode = mode; // Simple enough!
}
uint8_t Marker::Mode ()
{
return _mode; // Also simple!
}
/*
* The next three functions set or clear the "_enabled" indicator. There are two
* versions of "Enable"; the first simply marks the marker as enabled and the
* second enables the marker and sets the mode all at once.
*
* "Toggle" toggles the enabled/disabled status.
*
* These functions also manipulate the "MKR_ACTIVE" bit in the "_status" byte.
*/
void Marker::Enable () // Enable it
{
_enabled = true;
_status |= MKR_ACTIVE; // In the "_status" byte also
}
void Marker::Enable ( uint8_t mode ) // Enable & set mode all at once
{
Mode ( mode ); // Set the mode
Enable (); // and enable the marker
}
void Marker::Disable () // Disable it
{
_enabled = false;
_status &= ~MKR_ACTIVE; // In the "_status" byte also
}
void Marker::Toggle () // Toggle enabled/disabled status
{
_enabled = !_enabled;
_status ^= MKR_ACTIVE; // In the "_status" byte also
}
/*
* "isEnabled" returns "true" if the marker is enabled; "false" if not.
*/
bool Marker::isEnabled () // Request enabled/disabled status
{
return _enabled;
}
/*
* Set or get the marker's current frequency.
*/
void Marker::Frequency ( uint32_t freq ) // Set the marker's frequency
{
_frequency = freq;
}
uint32_t Marker::Frequency () // Get the frequency
{
return _frequency;
}
uint8_t Marker::Index () // Get marker's index
{
return _index;
}
/*
* This version of "Status" simply returns the current "_status" byte, which is updated
* whenever a new color for the marker is specified or the enabled/disabled changes.
*/
uint8_t Marker::Status () // Return the "_status" byte
{
return _status;
}
/*
* This version of "Status" is used to set the "_status" byte. Note, that we don't
* check for a legitimate color choice here as the expectation is that this capability
* is only used by the main program when the marker statuses saved in flash memory
* are recalled at startup.
*/
void Marker::Status ( uint8_t status) // Set the status byte
{
_color = _colors[status & MKR_COLOR];
Color ( _color );
if ( status & MKR_ACTIVE ) // Enable it?
Enable (); // Yes, do it
else // Otherwise
Disable (); // Turn it off
}
/*
* This version of "Color" (do I need an alternate version named "Colour" for the
* Brits?) simply returns the 16 bit color assigned to the marker.
*/
uint16_t Marker::Color () // Returns the marker's color
{
return _color;
}
/*
* This version of "Color" is used by the serial command handler and the touch screen
* menu system to specify the color for the marker.
*
* There are only six colors allowed. The number is based on the fact that the touch
* screen menu can only display that many choices at once. The legal colors are defined
* in the "_colors" array in the header file.
*
* The function checks to see if the specified color is in the list and if so, not only
* sets the "_color" variable, but also updates the "_status" byte.
*/
bool Marker::Color ( uint16_t color ) // Sets the marker's color
{
/*
* The "markerBitmap" shows where in the "sprite" the pixels should be of
* the specified color (1) and where the background color (0) should be used.
*
* Using the bitmap, we build the "sprite" using the specified color.
*/
const uint8_t markerBitmap[] =
{
0xFE, 0xEE, 0xCE, 0xEE, 0xEE, 0xEE, 0xC6, 0x7C, 0X38, 0x10, // Marker 1
0xFE, 0xC6, 0xBA, 0xFA, 0xC6, 0xBE, 0x82, 0x7C, 0x38, 0x10, // Marker 2
0xFE, 0xC6, 0xBA, 0xE6, 0xFA, 0xBA, 0xC6, 0x7C, 0x38, 0x10, // Marker 3
0xFE, 0xF6, 0xE6, 0xD6, 0xB6, 0xB6, 0x82, 0x74, 0x38, 0x10, // Marker 4
};
int line; // Which horizontal line we're painting
int column; // and which column
int colorIx; // Loop index
uint8_t bits; // One byte from the marker definition
bool returnCode = false; // Assume bad color specification
/*
* The first thing we do is to compare the requested color to the legitimate ones
* and if we don't find a match, return a "false" indication.
*/
for ( colorIx = 0; colorIx < MKR_COLOR_COUNT; colorIx++ )
if ( color == _colors[colorIx] ) // Found a match?
{
returnCode = true; // Set good return code
break; // No need to look further
}
if ( !returnCode ) // If no match found
return false; // Return "false"
_color = color; // It's a legal color, so save it
_status = _status & ~MKR_COLOR; // Clear the previous color
_status = _status | colorIx; // Set the new one
/*
* Next, we re-paint our sprite in the new color.
*/
for ( line = 0; line < MARKER_HEIGHT; line++ ) // Paint one line at a time
{
bits = markerBitmap[_index * MARKER_HEIGHT + line]; // Get bitmap of the next line
for ( column = 0; column < MARKER_WIDTH; column++ ) // horizontally left to right
{
if ( bits & 0x80 ) // Next pixel turned on?
_sprite->drawPixel ( column, line, SwapBytes ( _color )); // Yes, use specified color
else
_sprite->drawPixel ( column, line, BACKGROUND );// Use display background color
bits <<= 1; // Shift the bitmap byte one place left
}
}
return true; // Good return code
}
/*
* "SwapBytes" was added in Version 2.9. Bodmer did something in the TFT_eSPI library
* (version 2.2.5 and later) that makes it necessary to swap the bytes in the color
* word when using rotated sprites (as we do here).
*/
uint16_t Marker::SwapBytes ( uint16_t color)
{
uint16_t low = ( color & 0x00FF ) << 8;
uint16_t high = ( color & 0xFF00 ) >> 8;
uint16_t swap = low | high;
return swap;
}

125
marker.h Normal file
View File

@ -0,0 +1,125 @@
/*
* "Markers.h" defines the "Marker" class.
*/
#ifndef _MARKERS_H_ // Prevent double include
#define _MARKERS_H_
#include "simpleSA.h" // Definitions needed by the whole program
#define MARKER_COUNT 4 // MUST be set to '4' for now
#define MARKER_SPRITE_HEIGHT 10 // Reverse of marker width and height
#define MARKER_SPRITE_WIDTH 7
#define MARKER_WIDTH 7 // Markers are 7 pixels wide
#define MARKER_HEIGHT 10 // and 10 pixels high
#define X_MARKER_OFFSET 3 // Center offset from x position
#define Y_MARKER_OFFSET MARKER_SPRITE_HEIGHT // Top offset from Y position
/*
* These definitions are intended to allow the marker to be set to particular
* places on the spectrum display, but we haven't exactly figured out how to do that
* yet. For now, marker #1 is always at the highest peak, marker #2 at the 2nd highest
* peak, etc.
*/
#define MKR_PEAK 1 // Marker is at the maximum signal point
#define MKR_LEFT 2 // Marker is at first peak left of maximum signal
#define MKR_RIGHT 3 // Marker is at first peak right of maximum signal
#define MKR_START 4 // Marker is at sweep start point
#define MKR_STOP 5 // Marker is at sweep end point
#define MKR_CENTER 6 // Marker is at the center of the sweep range
/*
* These definitions are for the bits in the "_status" byte. The "_status" byte
* contains the enabled status and the color of the marker. It provides a compact
* way of saving the marker's configuration and restoring the status of the marker
* when the program is restarted as we do for all the other sweep parameters.
*/
#define MKR_COLOR 0x07 // The bottom three bits hold an index to the color
#define MKR_ACTIVE 0x08 // Enabled/disabled status
#define MKR_VISIBLE 0x10 // Not used yet; I'll explain later!
#define MKR_COLOR_COUNT 6 // Limited by touch screen menu entries
class Marker
{
public:
/*
* Function prototypes available to the outside world:
*
* There are three constructors; the first simply creates an uninitialized object
* The second fills in the details by calling one version of the "Init" function.
* The third one is designed to setup the marker using the "status" byte saved in
* the flash memory.
*/
explicit Marker (); // Do nothing constructor
explicit Marker ( TFT_eSprite* spr, uint8_t marker ); // Real constructor #1
void Init ( TFT_eSprite* spr, uint8_t marker ); // Psuudo constructor #1
void Paint ( TFT_eSprite* target, uint16_t x, uint16_t y ); // Put it on the target sprite
void Mode ( uint8_t mode ); // Set marker mode
uint8_t Mode (); // Request marker's mode
void Enable (); // Enable it
void Enable ( uint8_t mode ); // Enable & set mode all at once
void Disable (); // Disable it
void Toggle (); // Toggle enable/disable
bool isEnabled (); // Request enabled/disabled status
uint8_t Status (); // Return the "_status" byte
void Status ( uint8_t status); // Set the status byte
uint16_t Color (); // Returns the merker's color
bool Color ( uint16_t color ); // Sets the marker's color
void Frequency ( uint32_t freq ); // Set the marker's frequency
uint32_t Frequency (); // Get the frequency
uint8_t Index (); // Get marker's index
private:
TFT_eSprite* _sprite ; // Sprite for the marker
uint32_t _frequency; // Marker's frequency
uint16_t _x; // Horizontal coordinate on the screen
uint16_t _y; // Vertical coordinate on the screen
uint16_t _color; // Marker's color
uint8_t _index; // Which marker?
uint8_t _mode; // Marker's mode
byte _status; // Color, enabled and visible all in a byte
bool _enabled; // Marker is or isn't enabled
/*
* If we ever figure out how to use different references for the markers,
* these define the labels for the top of the screen:
*/
const char* _text[6] = { "dBm", "dBc", "uV", "mV", "uW", "mW"};
/*
* The colors available are limited to the following six. Why six? Because that's the
* maximum number of touch screen menu selections we can display at once.
*/
const uint16_t _colors[MKR_COLOR_COUNT] =
{
WHITE, RED, BLUE, GREEN, YELLOW, ORANGE
};
uint16_t SwapBytes ( uint16_t color );
};
#endif

136
menu.cpp Normal file
View File

@ -0,0 +1,136 @@
/*
* "Menu.cpp" implements the "Menuitem" class.
*/
#include "menu.h" // Class definition
Menuitem::Menuitem () {} // Placeholder constructor
/*
* In the original macro implementation, one could enter "text" as a string in the format:
* "\2line1\0line2" for 2 line button labels. In the original implementation, the string
* was parsed everytime the button was created.
*
* Here, we'll parse it into "_text1" and "_text2" when the object is created saving some
* work each time the object is used. If the "text" does not begin with the '\2', it's a
* one-line label and we set "_text2" to NULL.
*
* This constructor is used for "MT_FUNC" type objects where the third argument is a
* pointer to the function that process the menu item.
*/
Menuitem::Menuitem ( uint8_t type, const char* text, fptr callback )
{
_type = type; // Menu item type
_callback = callback; // Save pointer to callback function
ParseLabel ( text ); // Parse two line labels
}
/*
* This constructor is used for "MT_MENU" type objects. It's identical to the previous
* constructor except the third argument is the address of the sub-menu to be displayed.
*/
Menuitem::Menuitem ( uint8_t type, const char* text, Menuitem* submenu )
{
_type = type; // Menu item type
_submenu = submenu; // Save pointer to the sub-menu
ParseLabel ( text ); // Parse two line labels
}
/*
* This constructor is for the "MT_BACK" type objects where we need only the type and
* a label, but no menu or function pointer.
*
* It is also used for the "MT_END" type objects where we don't need a label or a pointer.
* The "text" argument defaults to "NULL" if it is not specified. Note that if a button
* label is supplied we do allow a two-line label, but we really don't expect that to
* be used.
*/
Menuitem::Menuitem ( uint8_t type, const char* text )
{
_type = type; // Menu item type
ParseLabel ( text ); // Parse two line labels
}
/*
* The "Call" member simply calls the callback function with the specified argument,
* which is it's position in the menu list. If the object isn't an "MT_FUNC" type, it
* does nothing, ensuring we don't try to call some bogus address.
*/
void Menuitem::Call ( int num )
{
if ( _type == MT_FUNC ) // "Call" only works for "FUNC" type
_callback ( num );
}
/*
* These two functions return the text for line1 and (if a multiline label), the text
* for line2 of the label. If it's a single line label, "Text2" returns NULL.
*/
const char* Menuitem::Text1 ()
{
return _text1;
}
const char* Menuitem::Text2 ()
{
return _text2;
}
/*
* Return the menu item type ( MT_MENU, MT_FUNC, MT_CLOSE, MT_BACK, or MT_END )
*/
uint8_t Menuitem::Type ()
{
return _type;
}
/*
* Returns "true" if the button label has 2 lines; false if it's a single-line label.
*/
bool Menuitem::isMultiline ()
{
return ( _text2 != NULL ); // If "_text2" isn't "NULL", the answer is "true"
}
Menuitem* Menuitem::GetSubmenu () // For "MENU" type object, returns the sub-menu pointer
{
if ( _type == MT_MENU ) // Only allowed for "MENU" types
return _submenu;
}
/*
* This private function handles parsing the two-line buttopn labels into "_text1" and
* "_text2". If it's a single line label, "_text2" is set to "NULL".
*/
void Menuitem::ParseLabel ( const char* text ) // Parse two line labels
{
if ( text == NULL ) // If NULL pointer
return; // Nothing to see here!
if ( text[0] == '\2' ) // It's a two-line label
{
_text1 = &text[1]; // Address of 1st line
_text2 = &text[1] + strlen ( &text[1] ) + 1; // Address of line 2
}
else // Single line label
{
_text1 = text; // Only one line
_text2 = NULL; // and no second line
}
}

67
menu.h Normal file
View File

@ -0,0 +1,67 @@
/*
* "Menu.h" defines the classe "Menuitem", which replaces the original macros used to
* instantiate the menus in the original code.
*/
#ifndef _MENU_H_ // Prevent double include
#define _MENU_H_
#include <Arduino.h> // Basic Arduino stuff
typedef void (*fptr)( int ); // Functions all take an integer argument
enum { MT_FUNC, MT_MENU, MT_CLOSE, MT_BACK, MT_END }; // Symbols for "type" of menu object
class Menuitem
{
public:
/*
* Four constructors; the first just creates a placeholder instance of the object.
*
* The second fills in the "type", button label and callback function address for
* "MT_FUNC" type objects.
*
* The third constructor is used for creating "MT_MENU" type objects. Its third
* argument is a pointer to a sub-menu to be displayed.
*
* The fourth constructor is used for the "MT_BACK" and "MT_END" type objects where
* we need only the type and maybe a label, but no menu or function pointer. Note
* that the "text" argument defaults to a NULL pointer for the "MT_END" type
* object.
*/
explicit Menuitem ();
explicit Menuitem ( uint8_t type, const char* text, fptr callback );
explicit Menuitem ( uint8_t type, const char* text, Menuitem* submenu );
explicit Menuitem ( uint8_t type, const char* text = NULL );
void Call ( int num ); // Executes the callback function
const char* Text1 (); // Returns the button label - line 1
const char* Text2 (); // Returns the button label - line 2 (or NULL)
uint8_t Type (); // Returns the menu item "type"
bool isMultiline (); // Returns "true" if a multiline label
Menuitem* GetSubmenu (); // For "MT_MENU" type object, returns the sub-menu pointer
private:
void ParseLabel ( const char* text ); // Parse two line labels
private: // All the data is private
uint8_t _type = -1; // Menu item type (invalid default)
const char* _text1 = NULL; // Label line 1
const char* _text2 = NULL; // Label line 2
fptr _callback = NULL; // Address of callback function
Menuitem* _submenu = NULL; // Address of a sub-menu
};
#endif

323
my_SA.h Normal file
View File

@ -0,0 +1,323 @@
/*
* "My_SA.h"
*
* Added in Version 2.2 by John Price (WA2FZW):
*
* This file contains all the user settable parameters for the TinySA spectrum
* analyzer software. If tou change anything in any of the header files, you've
* just become a test pilot!
*/
#ifndef _MY_SA_H_
#define _MY_SA_H_ // Prevent double inclusion
//#define GLENN_BUILD
/*
* The following definitions are all related to the WiFi interface. If you don't want
* to use the WiFi interface, set the "USE_WIFI" definition to 'false'.
*
* If you are going to use the WiFi interface, you'll need to set the "WIFI_SSID"
* and "WIFI_PASSWORD" symbols to the appropriate values for your network.
*
* Don't comment out the SSID and password definitions. Doing so will cause compiler
* errors.
*/
// #define USE_ACCESS_POINT // Comment out if want to connect to SSID, leave in to use access point
// #define AP_PASSWORD "none"
#define WIFI_SSID ":)" // SSID of your WiFi if not using access point
#define WIFI_PASSWORD "S0ftR0ckRXTX" // Password for your WiFi
#define MAX_WIFI_POINTS 75 // was 150 Number of sample points to send to clients in each wifi chunk
// Some clients cannot handle too few (too frequent chart refresh)
// ** IMPORTANT ** Must be less than or equal to DISPLAY_POINTS (290)
#define WIFI_UPDATE_TARGET_TIME 500000 // No of microseconds to target chart updates.
#define WEBSOCKET_INTERVAL 2000 // Microseconds between check for websocket events if no client connected
/*
* You can set your own colors for the various traces, but you can only choose
* from the following colors:
*
* WHITE YELLOW ORANGE GREEN RED BLUE PINK LT_BLUE MAGENTA
*/
#define DB_COLOR YELLOW
#define GAIN_COLOR GREEN
#define AVG_COLOR MAGENTA
#define STORAGE_COLOR LT_BLUE
#define SIG_BACKGROUND_COLOR TFT_DARKGREY
#define SLIDER_BOX_HEIGHT 8
#define SLIDER_BOX_COLOR TFT_LIGHTGREY
#define SLIDER_FILL_COLOR TFT_ORANGE
#define SLIDER_KNOB_RADIUS 15 // Sprite is twice this high
#define SLIDER_WIDTH 200 // Sprite width is this plus twice knob radius
#define SLIDER_KNOB_COLOR TFT_WHITE
#define SLIDER_X 10
#define SLIDER_Y 195
//#define SHOW_FREQ_UP_DOWN_BUTTONS // Comment out if you don't like them!
// #define SLIDER_MIN_POWER -43.0 // in dBm
// #define SLIDER_MAX_POWER -13.0
#define ATTENUATOR_RANGE 30 // in dB
// the B3555 SAW filter has max power rating of 10dBm.
// Drive of 4 = 11dBm but 1 dB pad in my build. If you have a larger attenuator pad you can try higher powers
#define RX_SI4432_MAX_DRIVE 4 // corresponds to 11dBm
/*
* These definitions control the values that get set when you select "AUTO SETTINGS" from
* the main touch screen menu; feel free to change them as you wish, but the legal values
* for some of them won't be obvious, so do your research first!
*/
#define AUTO_SWEEP_START 0 // Default sewwp start is 0Hz
#define AUTO_SWEEP_STOP 100000000 // Default stop is 100MHz
#define AUTO_PWR_GRID 10 // 10 dB per horizontal division
#define AUTO_LNA 0x60 // Receiver Si4432 AGC on
#define AUTO_REF_LEVEL -10 // Top line of the grid is at -10dB
#define AUTO_REF_OUTPUT 1 // Transmitter GPIO2 is 15MHz
#define AUTO_ATTEN 0 // No attenuation
#define AUTO_RBW 0 // Automatic RBW
/*
* Some definitions for IF-Sweep to test the internal SAW filters
*/
#define IF_SWEEP_START 432000000
#define IF_SWEEP_STOP 435000000
// Limits for keypad entry of IF sweep frequency start/stop
#define IF_STOP_MAX 500000000 // 500MHz
#define IF_START_MIN 400000000 // 400MHz
/*
* Some definitions for RX-Sweep to test the internal FIR filters
*/
#define RX_SWEEP_START 432000000
#define RX_SWEEP_STOP 436000000
// Limits for keypad entry of IF sweep frequency start/stop
#define RX_STOP_MAX 440000000 // 500MHz
#define RX_START_MIN 430000000 // 400MHz
#define RF_SWITCH 11
#define RF_SWITCH_HIGH_RANGE 1
/*
* Spur reduction shifts the IF down from its normal setting every other scan
* Set MAX_IF_SHIFT so that the IF cannot go outside the flat top of the SAW filter passband
*/
#define MAX_IF_SHIFT 300000 // Maximum shift of IF (Hz) in spur reduction mode
#define PAST_PEAK_LIMIT 3 // number of consecutive lower values to detect a peak
#define MARKER_NOISE_LIMIT 20 // Don't display marker if its RSSI value is < (min for sweep + this)
#define MARKER_MIN_FREQUENCY 750000 // Ignore values at lower frequencies as probably IF bleed through.
/*
* There are three possible implementations for the PE4302 attenuator:
*
* The parallel version using a PCF8574 I2C GPIO expander interface to the processor
* The parallel version using six separate GPIO pins to interface with the processor
* The serial version.
*
* Change the following definition to select the version you are using. The options are:
*
* PE4302_PCF
* PE4302_GPIO
* PE4302_SERIAL
*/
#define PE4302_TYPE PE4302_SERIAL
/*
* If you're using the "PE4302_PCF" mode, you need to define the I2C address for
* the PCF8574:
*/
// #define PCF8574_ADDRESS 0x38 // Change for your device if necessary
/*
* If you're using the "PE4302_SERIAL" mode, you need to define the chip select (LE)
* pin number:
*/
#define PE4302_LE 21 // Chip Select for the serial attenuator
/*
* If you're using the "PE4302_GPIO" mode, you need to define the GPIO pins used for
* the six data inputs to the PE4302:
*/
// #define DATA_0 nn // Replace the "nn" with real pin numbers
// #define DATA_1 nn
// #define DATA_2 nn
// #define DATA_3 nn
// #define DATA_4 nn
// #define DATA_5 nn
/*
* If you're using the PE4302 attenuator, the 'ATTEN' command will set the attenuation.
* The maximum setting for the PE4302 is 31dB. If you have some other attenuator arrangement
* you can change this value appropriately so that the software can take into account
* a higher attenuation; but note only the PE4302 will actually be congigured by the code.
*/
#define PE4302_MAX 31
/*
* The Si4432 is an SPI device and we have it set up to use the ESP32's VSPI bus.
*
* Here, we define the chip select GPIO pins for the two Si4432 modules and the GPIO
* pins for the clock and data lines.
*/
#define SI_RX_CS 4 // Receiver chip select pin
#define SI_TX_CS 5 // Transmitter chip select pin
#define V_SCLK 18 // VSPI clock pin
#define V_SDI 36 // VSPI SDI (aka MOSI) pin
#define V_SDO 17 // VSPI SDO (aka MISO) pin
/*
* An option is add more SI4432 devices to make a tracking generator
* Options are for a single one producing the IF where the LO is tapped off from the TinySA LO
* or two where the track gen LO is generated with another SI4432 to give improved
* isolation and therefore better dynamic range.
* Do NOT comment out these definitions if you plan to install/test with TG physically
* installed.
*
*/
// #define SI_TG_IF_CS 2 // pin to use for tracking gen IF SI4432
// #define SI_TG_LO_CS 0 // pin to use for tracking gen LO SI4432
/*
* Comment both lines below out if you do not have the tracking generator
* If only one SI4432 comment out the TG_LO_INSTALLED line
* Leave both uncommented if you have the two SI4432 TG option
*/
// #define TG_IF_INSTALLED
// #define TG_LO_INSTALLED
/*
* These two definitions are used to tune the Si4432 module frequencies. Once you have
* performed the frequency calibration as explained in the documentation, you should
* change the values defined here to the values you determined appropriate in the
* calibration procedure or they may be lost when new software versions are released.
*
* The values here were Erik's original values
*/
#define TX_CAPACITANCE 0x65 // Transmitter (LO) crystal load capacitance
#define RX_CAPACITANCE 0x59 // Receiver crystal load capacitance
#define TG_LO_CAPACITANCE 0x64 // Tracking generator LO crystal load capacitance
#define TG_IF_CAPACITANCE 0x62 // Tracking generator IF crystal load capacitance
#define RX_PASSBAND_OFFSET 1300 // RX bandpass filters are offset from nominal IF by this
/*
* "CAL_POWER" is the calibrated power level determined when doing the calibration
* procedure. As is the case with the crystal capacitances, you should change the
* value here to prevent the calibration from being lost when new versions of the
* code are released.
*/
#define CAL_POWER -30 // Si4432 GPIO2 normal reference level
/*
* The default transmitter Si4432 power output is based on the type of mixer being used.
* For the ADE-1 mixer should be +7dBm and +17dBm for the ADE-25H mixer.
*
* The definition of "MIXER" can be set to one of the following symbols, or you can
* define a power setting numerically (refer to A440).
*
* MIX_ADE_1 // For the ADE-1 mixer module
* MIX_ADE_25H // For the ADE-25H mixer module
* MIX_MINIMUM // Minimum power for testing
*/
#define MIXER MIX_ADE_25H // High power
/*
* These define the sweep range limits. The unit is capable of sweeping up to 430MHz,
* but if you're using Glenn's PCBx, there is a 200MHz LPF on the input, so anyone
* using that (or some other input LPF) might want to change the maximum frequency
* accordingly.
*/
#define STOP_MAX 550000000 // 250MHz
#define START_MIN 0 // 0MHz
/*
* The "FOCUS_FACTOR" is used in conjunction with the "FOCUS" command (from either
* the serial interface or touch screen). The requested focus frequency is divided
* by the "FOCUS_FACTOR" to set the frequency span.
*
* So for example, if you request a focus frequency of 50MHz, and the "FOCUS_FACTOR"
* is set to 1000, 50MHz / 1000 = 25KHz which would be the total span. In other words,
* the sweep frequency range would be from 49.975MHz to 50.025MHz. But since the
* readings on the grid are only good to 2 decimal places, the display may not indicate
* the exact span.
*/
#define FOCUS_FACTOR 1000UL
/*
* "TS_MINIMUM_Z" is the minimum touch pressure that will be considered to indicate a
* valid touch screen touch. It was originally hard-coded in the "ui.cpp" module as
* '600', but that proved to be too much for some displays, so now you can set it to
* see what works for your particular display.
*/
#define TS_MINIMUM_Z 600 // The original setting
/*
* The "BACKLIGHT_LEVEL" setting can be used to control the brightness of the backlight
* of the TFT display. Presently, there is no place in the menu system where this can be
* adjusted, so if using the adjustable option, you have to set the symbol appropriately
*
* Glenn's PCB provides the option to simply connect the backlight to 3V3 through an
* appropriate resistor.
* Comment out if not using the PWM backlight (uses GPIO25 which can then be used for
* a tracking generator, but not both)
*/
// #define BACKLIGHT_LEVEL 50 // Level setting
/*
* We haven't implemented the use of a rotary encoder to control the menu selections
* yet, but if we ever do, the GPIO pins that it uses need to be defined. The ones
* defined here are based on Glenn's PCB design.
*/
#ifdef USE_ROTARY // Not defined anywhere!
#define ENC_PB 32 // Encoder pushbutton switch
#define ENC_BB 33 // Back button pushbutton switch (or TS_INT)
#define ENC_B 34 // Encoder pin "B" (input only pin)
#define ENC_A 35 // Encoder pin "A" (input only pin)
#endif
#endif // #ifndef _MY_SA_H_

41
ns2009.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef _NS2009_H_
#define _NS2009_H_
#define DEFAULT_NS2009_ADDR 0x48 //10010000
#define THRESHOLD_Z 150 // this is a sensitivity threshold value on how hard the touch screen must be touched to react
#define NS2009_READ_X 0xC0
#define NS2009_READ_Y 0xD0
#define NS2009_READ_Z 0xE0
#define CALIBRATE_MIN_X 200
#define CALIBRATE_MIN_Y 400
#define CALIBRATE_MAX_X 3850
#define CALIBRATE_MAX_Y 3920
#define SCREEN_SIZE_X 240
#define SCREEN_SIZE_Y 320
#define MIN(A,B) (A<B?A:B)
#define MAX(A,B) (A>B?A:B)
class NS2009
{
int Address, MinX=CALIBRATE_MIN_X, MinY=CALIBRATE_MIN_Y, MaxX=CALIBRATE_MAX_X, MaxY=CALIBRATE_MAX_Y;
bool FlipX, FlipY;
unsigned int ReadRegister (unsigned char Command);
public:
int RawX, X, RawY, Y, RawZ;
bool Touched;
NS2009 (void);
NS2009 (unsigned char _Address);
NS2009 (bool _FlipX, bool _FlipY);
NS2009 (unsigned char _Address, bool _FlipX, bool _FlipY);
void Calibrate ();
void Calibrate (int _MinX, int _MaxX, int _MinY, int _MaxY);
bool CheckTouched ();
void Scan ();
void ScanBlocking ();
};
#endif

232
pE4302.cpp Normal file
View File

@ -0,0 +1,232 @@
/*
* "PE4302.cpp"
*
* Added to the "TinySA" program in Version 1.1 by John Price (WA2FZW):
* Updated in V2.5 by M0WID
*
* Functions to handle the PE4302 attenuator and support for using the PCF8574
* I2C GPIO expander to drive a parallel version of the PE4302 module.
*/
#include "pE4302.h"
/*
* Create a "PCF8574" object with the default I2C address
* for the basic PCF8574 (if using a PCF8574A, the default address would be
* 0x38).
*
* Please note, we also assume that the PCF8574 is on the standard I2C bus
* pins for the processor being used (21 & 22 for the ESP32; different for
* Arduinos).
*
* The address is updated by the PCF8574 constructor.
*/
PCF8574 _pcf ( 0x28 ); // Create the PCF8574 object
/*
* The PE4302 chip can be operated in either a serial or parallel mode.
* Most pre-built modules are set for parallel but by changing some jumpers
* can be used in serial mode.
*/
/*
* Serial interface constructor:
*
* The arguments are a pointer to the SPI class used and pin definition
* for Latch Enable "LE" pin. When "LE" is HIGH the data in the serial
* buffer is latched. "LE" must be LOW when the SPI bus is used for other
* objects (which it is).
*
* The SPI object can be shared with other objects, so is declared in the main sketch
*
* SPIClass* vspi = new SPIClass ( VSPI );
* or SPIClass* hspi = new SPIClass ( HSPI );
*
* The SPI object is initialized once in the main sketch setup, along with the
* relevant pins, e.g.:
*
* pinMode ( V_SCLK, OUTPUT ); // SPI Clock pin
* pinMode ( V_SDO, INPUT ); // SDO (MISO) pin
* pinMode ( V_SDI, OUTPUT ); // SDI (MOSI) pin
*
* digitalWrite ( V_SCLK, LOW ); // Make SPI clock LOW
* digitalWrite ( V_SDI, LOW ); // Along with MOSI
*
* vspi->begin ( V_SCLK, V_SDO, V_SDI ); // Start the VSPI: SCLK, MISO, MOSI
*/
PE4302::PE4302 ( SPIClass* spi, int le ) // Constructor for serial interface
{
_interface = S; // SPI (serial)interface
_le_Pin = le; // Enable (LE) pin number
pinMode ( _le_Pin, OUTPUT ); // Chip select pin is an output
digitalWrite ( _le_Pin, LOW ); // Deselect the module
_spi = spi; // Save SPI object pointer
}
/*
* Parallel GPIO interface constructor:
* arguments are the GPIO pin assignments that correspond to the "C16" to
* "C0.5" pins of the chip.
*/
PE4302::PE4302 ( int c16, int c8, int c4,
int c2, int c1, int c0 )
{
_interface = P; // Parallel interface
_parallel_Pins[0] = c0; // The actual GPIO pin numbers
_parallel_Pins[1] = c1; // can be random as opposed
_parallel_Pins[2] = c2; // to David's requirement that
_parallel_Pins[3] = c4; // they had to be consecutive.
_parallel_Pins[4] = c8; // The array order is LSB to MSB.
_parallel_Pins[5] = c16;
}
/*
* PCF8574 (I2C IO expander interface) constructor:
* The default address set at the beginning of the module assumes that the
* chip in use is a PCF8574. The PCF8574A may also be used, however its
* default address is 0x38, so you might need to provide a different address
* here.
*/
PE4302::PE4302 ( int address ) // Constructor for PCF8574 interface
{
_interface = PCF; // PCF8574 interface
_pcf_I2C = address; // I2C bus address of the PCF8574
_pcf = PCF8574 ( address ); // Update the object with the address
}
/*
* "PE4302_init" - Initialize the attenuator module.
*/
void PE4302::Init ()
{
if ( _interface == S ) // If using the serial mode
{
// nothing to do!
}
else if ( _interface == P ) // If using the parallel mode
{
for ( int i = 0; i < 6; i++ )
pinMode ( _parallel_Pins[i], OUTPUT ); // All 6 pins are outputs
}
else if ( _interface == PCF ) // If using the PCF8574
{
_pcf.begin ( _pcf_I2C, 0 ); // Initialize the chip
}
}
/*
* "SetAtten" - Set the attenuation.
*
* The parameter is the required attenuation in dB.
* The PE4302 allows attenuation to be set in 0.5dB increments, but not here!
* If the requested attenuation is out of limits the function will return "false".
*/
bool PE4302::SetAtten ( int8_t atten )
{
bool retCode = true; // Assume good number
if ( atten > PE4302_MAX ) // Maximum is defined in "My_SA.h"
{
atten = PE4302_MAX;
retCode = false; // Indicate bad number
}
if ( atten < 0 ) // Can't be less than zero
{
atten = 0;
retCode = false; // Indicate bad number
}
_atten = atten << 1; // Double the number and save it
if ( _interface == S ) // If using the serial mode
{
// Serial.printf ( "SetAtten %i - clk:%i data:%i LE:%i \n",
// _atten, _clock_Pin, _data_Pin, _le_Pin);
uint32_t oldDivider = _spi->getClockDivider(); // run at 1MHz
_spi->setFrequency(1000000); // run at 1MHz
digitalWrite ( _le_Pin, LOW ); // Make the sure the attenuator is not using the shifted in data
_spi->transfer ( _atten ); // Send the attenution value bit pattern
digitalWrite ( _le_Pin, HIGH ); // Latch Enable pin HIGH
digitalWrite ( _le_Pin, LOW ); // Then immediately LOW
_spi->setClockDivider(oldDivider); // back to whatever speed it was running before
}
else if ( _interface == P ) // If using the parallel mode
{
for ( int i = 0; i < 6; i++ )
digitalWrite ( _parallel_Pins[i], _atten & ( 1 << i ));
}
else if ( _interface == PCF ) // If using the PCF8574
{
_pcf.write8 ( _atten );
}
return retCode; // Send back good/bad indication
}
/*
* Return current attenuation value.
* Bit pattern has to be divided by two to return dB
*/
int PE4302::GetAtten () // Send back stored attenuation
{
return _atten >> 1;
}
/*
* These functions implement the stripped down PCF8574 library:
*/
PCF8574::PCF8574 ( const uint8_t deviceAddress )
{
_address = deviceAddress;
_dataOut = 0;
}
/*
* The "begin" function is modified from the original to allow us to change the
* I2C address as well as set an inital output value:
*/
void PCF8574::begin ( const uint8_t deviceAddress, uint8_t val )
{
_address = deviceAddress; // Save address
Wire.begin (); // Initialize the I2C interface
PCF8574::write8 ( val ); // Output the initial value
}
void PCF8574::write8 ( const uint8_t value )
{
_dataOut = value; // Save value to be output
Wire.beginTransmission ( _address ); // Start the transmission process
Wire.write ( _dataOut ); // Send the data
Wire.endTransmission (); // Wasn't in original library
}

88
pE4302.h Normal file
View File

@ -0,0 +1,88 @@
/*
* "PE4302.h"
*
* Added to the "TinySA" program in Version 1.1 by John Price (WA2FZW):
*
* This is the header file for the PE4302 class.
*/
#ifndef PE4302_H_
#define PE4302_H_ // Prevent double inclusion
#include <Arduino.h> // General Arduino definitions
#include <Wire.h> // I2C library
#include <SPI.h> // Serial Peripheral Interface library
#include "simpleSA.h"
/*
* In order to save GPIO pins on the ESP32, Glenn (VK3PE) and I decided to use a
* PCF8574 GPIO expander chip between the processor and the parallel version of
* of the PE4302 attenuator. We know some PE4302 modules are available with a
* serial interface, but the ones we happen to have only support the parallel
* interface. This lets us control it over the I2C bus.
*
* It's not clear to me that the native serial interface to the serial module
* is an I2C interface, but we will also support that via a bit-banging techique.
* We will also support the parallel interface module without the PCF8574.
*/
enum { PCF, P, S }; // Symbols for operational modes
class PE4302
{
public:
/*
* Function prototypes for the PE4302 available to the outside world:
*
* Constructors:
*/
explicit PE4302 ( SPIClass* spi, int le ); // For serial interface
explicit PE4302 ( int address ); // For PCF8574 interface
explicit PE4302 ( int c16, int c8, int c4, // For parallel interface
int c2, int c1, int c0 );
void Init (); // Initialize the module
bool SetAtten ( int8_t atten ); // Set the attenuation
int GetAtten (); // Get the current attenuation
private:
uint8_t _pcf_I2C; // I2C address for the PCF8574
uint8_t _interface; // Interface type ( PCF, P or S )
uint8_t _le_Pin; // Enable (LE) pin for serial or parallel interdace
uint8_t _parallel_Pins[6]; // Pin numbers ( c0 - c16 ) for parallel interface
int16_t _atten; // Current attenuator setting ( 0 - 31 dB ) x 2
SPIClass* _spi; // Pointer to the SPI object used for serial mode
};
/*
* This is a stripped down version of the PCF8574 library. We only need the
* constructor, the "begin" function (slightly modified from the original
* library), the "write8" functions and a couple of the variables.
*/
class PCF8574
{
public:
explicit PCF8574 ( const uint8_t deviceAddress );
void begin ( const uint8_t deviceAddress, uint8_t val );
void write8 ( const uint8_t value );
private:
uint8_t _address; // PCF8574 I2C address
uint8_t _dataOut; // Data to be sent
};
#endif

397
preferencesd.cpp Normal file
View File

@ -0,0 +1,397 @@
/*
* "preferences.cpp"
*
* This file has the functions that write and read the "config" and "setting"
* structures to and from the flash memory on the ESP32; similar to how EEPROM
* works on the Arduinos.
*
* The starting point for this version is the "tinySA_touch02" software developed
* by Dave (M0WID). That software is based on the original version by Erik Kaashoek.
*
* Modified by John Price (WA2FZW):
*
* Version 1.0 - Just add comments and try to figure out how it all works!
*/
#include "Arduino.h" // Basic Arduino definitions
#include <Preferences.h> // Preferences library header
#include "preferencesd.h" // Our function prototypes
#include "simpleSA.h" // General program-wide definitions and stuff
extern Preferences preferences; // The Preferences object - Created in the main file
extern void DisplayError ( uint8_t severity, const char *line1, const char *line2, const char *line3, const char *line4 );
/*
* "ReadConfig" - Reads the "config" (see "tinySA.h") structure from flash memory
*/
void ReadConfig ()
{
config_t tempConfig; // Temporary store to check data is valid
size_t bytes; // Amount of data
bytes = preferences.getBytes ( "config", &tempConfig, sizeof (tempConfig ));
/*
* If the "magic" entry is zero or what we read is the wrong size, the data is invalid.
*
* If nothing has yet been saved then nothing is retrieved and default values are used
*
* It might be better if "magic" was a specific number or maybe even a short string.
*
* If the size isn't correct, it could be that the size of the "config" structure was
* changed in a newer release of the code.
*
* If what we read is invalid, we store the default values in the flash memory.
*/
if (( tempConfig.magic == 0 ) || ( bytes != sizeof ( tempConfig )))
{
Serial.printf ( "Bytes got = %i - aiming for %i. No config saved - Storing default values\n",
bytes, sizeof ( tempConfig ));
preferences.remove ( "config" ); // Clear any old data just in case size has changed
WriteConfig ();
}
else // Valid data was retrieved
{
// Serial.println ( "config retrieved" );
config = tempConfig; // Copy retrieved values to the real structure
}
}
/*
* "WriteConfig" - Writes the "config" structure to the flash memory.
*/
void WriteConfig ()
{
size_t bytes;
bytes = preferences.putBytes ( "config", &config, sizeof ( config ));
if ( bytes == 0 ) // Writing failed
Serial.println ( "Save of config failed" );
else
Serial.println ( "config saved" );
}
/*
* "ReadSettings" - Reads the "setting" structure from the flash memory
*/
void ReadSettings()
{
settings_t tempSettings; // Temporary store to check data is valid
size_t bytes; // Amount of data read
bytes = preferences.getBytes ( "Settings", &tempSettings, sizeof ( tempSettings ));
/*
* If the "PowerGrid" entry is zero or what we read is the wrong size, the data is invalid.
*
*
* It might be better if we included a "magic" element that is a specific number or maybe
* even a short string.
*
* If the size isn't correct, it could be that the size of the "setting" structure was
* changed in a newer release of the code.
*
* If what we read is invalid, we store the default values in the flash memory.
*/
if (( tempSettings.PowerGrid == 0 ) || ( bytes != sizeof ( tempSettings )))
{
Serial.printf ( "Bytes got = %i - aiming for %i. No Settings saved - Storing default values\n",
bytes, sizeof (tempSettings ));
preferences.remove ( "Settings" ); // Clear any old data just in case size has changed
WriteSettings (); // Write default values
}
else // Data retrieved looks valid
{
// Serial.println ( "Settings retrieved" );
setting = tempSettings; // Copy retrieved values to the real structure
}
}
/*
* "WriteSettings" - Writes the contents of the "setting" structure to the flash memory
*/
void WriteSettings ()
{
size_t bytes;
bytes = preferences.putBytes ( "Settings", &setting, sizeof ( setting ));
if ( bytes == 0 ) // WA2FZW - Should we compare to expected size?
Serial.println ( "Save of Settings failed" );
else
Serial.println ( "Settings saved" );
}
/*
* "Save" and "Recall" were added in Version 2.1 by WA2FZW.
*
* I reactivated the "Save" and "Recall" menu items on the touch screen so
* the user can save up to five scan configurations! Those commands will also
* be added to the serial command handler.
*/
void Save ( uint8_t loc )
{
char saveName[10];
sprintf ( saveName, "Save%d", loc );
if (( loc < 0 ) || ( loc > 4 ))
{
Serial.printf ( "Illegal location specification: %u\n", loc );
return;
}
// Serial.print ( "saveName = " ); Serial.println ( saveName );
size_t bytes;
bytes = preferences.putBytes ( saveName, &setting, sizeof ( setting ));
if ( bytes == 0 ) // WA2FZW - Should we compare to expected size?
Serial.printf ( "Failed to save'%s'\n", saveName );
else
Serial.printf ( "Settings saved to '%s'\n", saveName );
}
void Recall ( uint8_t loc )
{
char saveName[10]; // Place to construct the name of the data to recall
size_t bytes; // Number of bytes read from the flash
settings_t tempSettings; // Temporary store to check data is valid
if (( loc < 0 ) || ( loc > 4 ))
{
Serial.printf ( "Illegal location specification: %u\n", loc );
return;
}
sprintf ( saveName, "Save%d", loc ); // Construct the name of the data to be recalled
// Serial.print ( "saveName = " ); Serial.println ( saveName );
bytes = preferences.getBytes ( saveName, &tempSettings, sizeof ( tempSettings ));
if ( bytes != sizeof ( tempSettings )) // Incorrect amount of data read
Serial.printf ( "No data stored for '%s'\n", saveName );
else // Successful read!
{
Serial.printf ( "Settings recalled from '%s'\n", saveName );
setting = tempSettings; // Copy retrieved values to the real structure
}
}
/*
* "ReadSettings" - Reads the "setting" structure from the flash memory
*/
void ReadSigGenSettings()
{
sigGenSettings_t tempSettings; // Temporary store to check data is valid
size_t bytes; // Amount of data read
bytes = preferences.getBytes ( "SigGenLo", &tempSettings, sizeof ( tempSettings ));
/*
* If the "PowerGrid" entry is zero or what we read is the wrong size, the data is invalid.
*
*
* It might be better if we included a "magic" element that is a specific number or maybe
* even a short string.
*
* If the size isn't correct, it could be that the size of the "setting" structure was
* changed in a newer release of the code.
*
* If what we read is invalid, we store the default values in the flash memory.
*/
if (( tempSettings.Dummy != 123 ) || ( bytes != sizeof ( tempSettings )))
{
Serial.printf ( "Bytes got = %i - aiming for %i. No Sig Gen Settings saved - Storing default values\n",
bytes, sizeof (tempSettings ));
preferences.remove ( "SigGenLo" ); // Clear any old data just in case size has changed
WriteSigGenSettings (); // Write default values
}
else // Data retrieved looks valid
{
// Serial.println ( "SigGenLo Settings retrieved" );
sigGenSetting = tempSettings; // Copy retrieved values to the real structure
}
}
/*
* "WriteSettings" - Writes the contents of the "setting" structure to the flash memory
*/
void WriteSigGenSettings ()
{
size_t bytes;
bytes = preferences.putBytes ( "SigGenLo", &sigGenSetting, sizeof ( sigGenSetting ));
if ( bytes == 0 )
Serial.println ( "Save of sigGenLo failed" );
else
Serial.println ( "sigGenLo saved" );
}
/*
* "ReadSettings" - Reads the "setting" structure from the flash memory
*/
void ReadTrackGenSettings()
{
trackGenSettings_t tempSettings; // Temporary store to check data is valid
size_t bytes; // Amount of data read
bytes = preferences.getBytes ( "TrackGen", &tempSettings, sizeof ( tempSettings ));
/*
* If the "dummy" entry is zero or what we read is the wrong size, the data is invalid.
*
*
* It might be better if we included a "magic" element that is a specific number or maybe
* even a short string.
*
* If the size isn't correct, it could be that the size of the "setting" structure was
* changed in a newer release of the code.
*
* If what we read is invalid, we store the default values in the flash memory.
*/
if (( tempSettings.Dummy != 99 ) || ( bytes != sizeof ( tempSettings )))
{
Serial.printf ( "Bytes got = %i - aiming for %i. No Track Gen Settings saved - Storing default values\n",
bytes, sizeof (tempSettings ));
preferences.remove ( "TrackGen" ); // Clear any old data just in case size has changed
WriteTrackGenSettings (); // Write default values
}
else // Data retrieved looks valid
{
// Serial.println ( "Track Settings retrieved" );
trackGenSetting = tempSettings; // Copy retrieved values to the real structure
}
}
/*
* "WriteTrackGenSettings" - Writes the contents of the "trackGenSetting" structure to the flash memory
*/
void WriteTrackGenSettings ()
{
size_t bytes;
bytes = preferences.putBytes ( "TrackGen", &trackGenSetting, sizeof ( trackGenSetting ));
if ( bytes == 0 )
Serial.println ( "Save of TrackGen failed" );
else
Serial.println ( "TrackGen saved" );
}
/*
* "ReadBpfCalibration" - Reads the bandpass filter calibrations from the flash memory
*/
void ReadBpfCalibration()
{
double tempCal[MAX_SI4432_FILTERS]; // Temporary store to check data is valid
size_t bytes; // Amount of data read
bytes = preferences.getBytes ( "bpfCal", &tempCal, bpfCount * sizeof (bpfCalibrations[0]));
/*
* If the "dummy" entry is zero or what we read is the wrong size, the data is invalid.
*
*
* It might be better if we included a "magic" element that is a specific number or maybe
* even a short string.
*
* If the size isn't correct, it could be that the size of the "setting" structure was
* changed in a newer release of the code.
*
* If what we read is invalid, we store the default values in the flash memory.
*/
if ( bytes != bpfCount * sizeof (bpfCalibrations[0]) )
{
Serial.printf ( "Bytes got = %i - aiming for %i. No bandpass filter calibrations saved\n",
bytes, bpfCount * sizeof (bpfCalibrations[0]) );
DisplayError ( ERR_WARN,
"No Calibrations found",
"Connect ref out, goto",
"RXSweep mode then",
"Choose bpfCalibrate" );
preferences.remove ( "bpfCal" ); // Clear any old data just in case size has changed
}
else // Data retrieved looks valid
{
Serial.printf ( "bpf calibrations read - %i bytes, %i filters \n", bytes, bpfCount);
for (int i = 0; i < bpfCount; i++)
{
bpfCalibrations[i] = tempCal[i];
}
}
}
/*
* Save the bandpass filter calibrations to flash
*/
void SaveBpfCalibration ( )
{
// double bpfCalibrations[MAX_SI4432_FILTERS]; // temporary storage for calibration values
size_t bytes;
bytes = preferences.putBytes ( "bpfCal", &bpfCalibrations, bpfCount * sizeof (bpfCalibrations[0]) );
if ( bytes == 0 )
Serial.println ( "Save of bandpass filter calibrations failed" );
else
Serial.println ( "Bandpass filter calibrations saved" );
}

42
preferencesd.h Normal file
View File

@ -0,0 +1,42 @@
/*
* "preferences.h"
*
* This file has the prototype functions for "preferences.cpp". Those functions
* write and read the "config" and "setting" structures to and from the flash
* memory on the ESP32; similar to how EEPROM works on the Arduinos.
*
* The starting point for this version is the "tinySA_touch02" software developed
* by Dave (M0WID). That software is based on the original version by Erik Kaashoek.
*
* Modified by John Price (WA2FZW):
*
* Version 1.0 - Just add comments and try to figure out how it all works!
*/
#include "simpleSA.h" // Definitions for the entire program
extern config_t config; // Default colors, touch screen calibration, etc.
extern settings_t setting; // Scan limits and other scan parameters
extern sigGenSettings_t sigGenSetting; // parameters for sig gen mode
extern trackGenSettings_t trackGenSetting; // parameters for tracking gen mode
extern uint16_t bpfCount; // no of bandpass filters available
extern double bpfCalibrations[MAX_SI4432_FILTERS]; // temporary storage for calibration values
extern void ReadConfig ();
extern void WriteConfig ();
extern void ReadSettings ();
extern void WriteSettings ();
extern void ReadSigGenSettings ();
extern void WriteSigGenSettings ();
extern void ReadTrackGenSettings ();
extern void WriteTrackGenSettings ();
extern void Save ( uint8_t loc );
extern void Recall ( uint8_t loc );
extern void ReadBpfCalibration();
extern void SaveBpfCalibration();

923
si4432.cpp Normal file
View File

@ -0,0 +1,923 @@
/*
* "Si4432.cpp"
*
* Added to the "TinySA" program in Version 1.7 by John Price (WA2FZW):
*
* Functions to handle the Si4432 transceivers as an objects. Some of the
* functions in here apply only to the receiver and some apply only to the
* transmitter. The "Init" functions remember what type of module (transmitter
* or receiver) is being initialized.
*
* For further information on the register settings, please refer to the
* "Silicon Labs Si4430/31/32 - B1" data sheet and "Silicon Labs Technical
* Note A440".
*/
#include <SPI.h> // Serial Peripheral Interface library
#include "si4432.h" // Our header file
#include <esp32-hal-spi.h>
/*
* The "bandpassFilters" array contains a selection of the standard bandpass settings.
* Values for status: enum{DO_NOT_USE, USE_IN_AUTO, AVAILABLE}
* Filters with strange behaviour are marked as DO_NOT_USE
* Filters with large dip in passband or not very good shape are AVAILABLE
* Filters with 0.5dB or 1dB dip in passband are USE_IN_AUTO
*/
bandpassFilter_t Si4432::_bandpassFilters[]
{
// bw*10, settle, dwn3, ndec, filset, status
{ 26, 6800, 0, 5, 1, USE_IN_AUTO }, // 0 "AUTO" selection possibility
{ 28, 6600, 0, 5, 2, USE_IN_AUTO }, // 1 "AUTO" selection possibility
{ 31, 6600, 0, 5, 3, AVAILABLE }, // 2 If user selects 3KHz -> 3.1KHz actual
{ 32, 6600, 0, 5, 4, AVAILABLE }, // 3 "AUTO" selection possibility
{ 37, 6600, 0, 5, 5, USE_IN_AUTO }, // 4 "AUTO" selection possibility
{ 42, 7000, 0, 5, 6, USE_IN_AUTO }, // 5 "AUTO" selection possibility
{ 45, 6600, 0, 5, 7, AVAILABLE }, // 6 "AUTO" selection possibility
{ 49, 5000, 0, 4, 1, USE_IN_AUTO }, // 7 "AUTO" selection possibility
{ 54, 4200, 0, 4, 2, USE_IN_AUTO }, // 8 "AUTO" selection possibility
{ 59, 3700, 0, 4, 3, AVAILABLE }, // 9 "AUTO" selection possibility
{ 61, 3500, 0, 4, 4, AVAILABLE }, // 10 "AUTO" selection possibility
{ 72, 3300, 0, 4, 5, USE_IN_AUTO }, // 11 "AUTO" selection possibility
{ 82, 3300, 0, 4, 6, USE_IN_AUTO }, // 12 "AUTO" selection possibility
{ 88, 4000, 0, 4, 7, AVAILABLE }, // 13 "AUTO" selection possibility
{ 95, 3300, 0, 3, 1, USE_IN_AUTO }, // 14 "AUTO" selection possibility
{ 106, 2500, 0, 3, 2, USE_IN_AUTO }, // 15 If user selects 10KHz -> 10.6KHz actual
{ 115, 2500, 0, 3, 3, AVAILABLE }, // 16 "AUTO" selection possibility
{ 121, 2500, 0, 3, 4, AVAILABLE }, // 17 "AUTO" selection possibility
{ 142, 2500, 0, 3, 5, USE_IN_AUTO }, // 18 "AUTO" selection possibility
{ 162, 2000, 0, 3, 6, USE_IN_AUTO }, // 19 "AUTO" selection possibility
{ 175, 2000, 0, 3, 7, USE_IN_AUTO }, // 20 "AUTO" selection possibility
{ 189, 1600, 0, 2, 1, AVAILABLE }, // 21 "AUTO" selection possibility
{ 210, 1600, 0, 2, 2, USE_IN_AUTO }, // 22 "AUTO" selection possibility
{ 227, 1500, 0, 2, 3, AVAILABLE }, // 23 "AUTO" selection possibility
{ 240, 1400, 0, 2, 4, AVAILABLE }, // 24 "AUTO" selection possibility
{ 282, 1000, 0, 2, 5, USE_IN_AUTO }, // 25 "AUTO" selection possibility
{ 322, 1000, 0, 2, 6, USE_IN_AUTO }, // 26 If user selects 30KHz -> 32.2KHz actual
{ 347, 1000, 0, 2, 7, USE_IN_AUTO }, // 27 "AUTO" selection possibility
{ 377, 1000, 0, 1, 1, AVAILABLE }, // 28 "AUTO" selection possibility
{ 417, 1000, 0, 1, 2, USE_IN_AUTO }, // 29 "AUTO" selection possibility
{ 452, 1000, 0, 1, 3, AVAILABLE }, // 30 "AUTO" selection possibility
{ 479, 1000, 0, 1, 4, AVAILABLE }, // 31 "AUTO" selection possibility
{ 562, 700, 0, 1, 5, USE_IN_AUTO }, // 32 "AUTO" selection possibility
{ 641, 700, 0, 1, 6, USE_IN_AUTO }, // 33 "AUTO" selection possibility
{ 692, 700, 0, 1, 7, USE_IN_AUTO }, // 34 "AUTO" selection possibility
{ 752, 700, 0, 0, 1, AVAILABLE }, // 35 "AUTO" selection possibility
{ 832, 600, 0, 0, 2, USE_IN_AUTO }, // 36 "AUTO" selection possibility
{ 900, 600, 0, 0, 3, AVAILABLE }, // 37 "AUTO" selection possibility
{ 953, 600, 0, 0, 4, AVAILABLE }, // 38 "AUTO" selection possibility
{ 1121, 500, 0, 0, 5, USE_IN_AUTO }, // 39 If user selects 100KHz -> 112.1KHz actual
{ 1279, 600, 0, 0, 6, USE_IN_AUTO }, // 40 "AUTO" selection possibility
{ 1379, 600, 0, 0, 7, USE_IN_AUTO }, // 41 "AUTO" selection possibility
{ 1428, 600, 1, 1, 4, AVAILABLE }, // 42 "AUTO" selection possibility
{ 1678, 600, 1, 1, 5, USE_IN_AUTO }, // 43 "AUTO" selection possibility
{ 1811, 500, 1, 1, 9, USE_IN_AUTO }, // 44 "AUTO" selection possibility
{ 1915, 500, 1, 0, 15, AVAILABLE }, // 45 "AUTO" selection possibility
{ 2251, 500, 1, 0, 1, AVAILABLE }, // 46 "AUTO" selection possibility
{ 2488, 450, 1, 0, 2, USE_IN_AUTO }, // 47 "AUTO" selection possibility
{ 2693, 450, 1, 0, 3, AVAILABLE }, // 48 "AUTO" selection possibility
{ 2849, 450, 1, 0, 4, AVAILABLE }, // 49 "AUTO" selection possibility
{ 3355, 400, 1, 0, 8, AVAILABLE }, // 50 If user selects 300KHz -> 335.5KHz actual
{ 3618, 300, 1, 0, 9, USE_IN_AUTO }, // 51 "AUTO" selection possibility
{ 4202, 300, 1, 0, 10, USE_IN_AUTO }, // 52 "AUTO" selection possibility
{ 4684, 300, 1, 0, 11, USE_IN_AUTO }, // 53 "AUTO" selection possibility
{ 5188, 300, 1, 0, 12, USE_IN_AUTO }, // 54 "AUTO" selection possibility
{ 5770, 300, 1, 0, 13, USE_IN_AUTO }, // 55 "AUTO" selection possibility
{ 6207, 300, 1, 0, 14, AVAILABLE } // 56 "AUTO" selection possibility
};
uint8_t Si4432::_bpfCount = ELEMENTS ( Si4432::_bandpassFilters ); // Number of entries in the array
/*
* The constructor takes two arguments which are the pointer to the SPI object
* and "Chip Select" pin for the module.
*
* The SPI object can be shared with other objects, so is declared in the main sketch as
*
* SPIClass* vspi = new SPIClass ( VSPI );
* or SPIClass* hspi = new SPIClass ( HSPI );
*
* The SPI object is initialized once in the main sketch setup, along with the relevant pins:
*
* pinMode ( V_SCLK, OUTPUT ); // SPI Clock pin
* pinMode ( V_SDO, INPUT ); // SDO (MISO) pin
* pinMode ( V_SDI, OUTPUT ); // SDI (MOSI) pin
*
* digitalWrite ( V_SCLK, LOW ); // Make SPI clock LOW
* digitalWrite ( V_SDI, LOW ); // Along with MOSI
*
* vspi->begin ( V_SCLK, V_SDO, V_SDI ); // Start the VSPI: SCLK, MISO, MOSI
* vspi->setFrequency(10000000); // Max speed according to datasheet
*/
Si4432::Si4432 ( SPIClass* spi, uint8_t cs, uint8_t id ) // Constructor (argument is chip select pin number)
{
_cs = cs; // Remember the chip select pin number
_bw = 3355; // Set bandwidth to 335.5KHz for now
_dt = 400; // Proper delay time for wide bandwidth
_spi = spi; // Pointer to SPI object
_type = id;
pinMode ( _cs, OUTPUT ); // Chip select pin is an output
digitalWrite ( _cs, HIGH ); // Deselect the module
}
/*
* There are two different initialization functions, as there are obviously some
* differences in how you set up the transmitter and receiver modules.
*
* ** Modified for version 3.0e by M0WID to be one Init as the SI4432 can be used
* for either RX aor TX depending on mode. All SI4432 are set as RX to start with
* with no GPIO2 reference output
*
* The "SubInit" function takes care of the register settings common to both.
*/
bool Si4432::Init ( uint8_t cap )
{
if ( !Reset () ) // Reset the module
return false; // If that failed
/*
* "REG_FBS" is the frequency band select register. We select upper sideband
* (0x40) and the band is set to (0x06). What that means is dependent on the
* setting of the "HBSEL" bit (0x10), which is set to low band here.
*
* As near as I can tell from A440, this says we will be tuning in a range of
* 300 to 310MHz.
*/
// WriteByte ( REG_FBS, 0x46 ); // Select high sideband & low freq range?
/*
* The following two instructions seem to set the carrier frequency to zero
* per A440. The setting of "REG_NFC1" to 0x62 was commented out in the
* original code.
*/
// WriteByte ( REG_NCF1, 0x62 ); // Nominal Carrier Frequency 1
// WriteByte ( REG_NCF1, 0x00 ); // WE USE 433.92 MHz
// WriteByte ( REG_NCF0, 0x00 ); // Nominal Carrier Frequency 0
/*
* Set the receiver modem IF bandwidth. In the original code, the bandwidth
* was set to 37.3KHz (0x81); I changed it to 335.5KHz (maximum for the
* application).
*/
// WriteByte ( REG_IFBW, 0x81 ); // RX Modem IF bandwidth (original value)
WriteByte ( REG_IFBW, 0x18 ); // IF bandwidth (for 335.5 KHz)
/*
* Set the AFC loop gearshift override to minimum, turn off AFC
*/
WriteByte ( REG_AFCGSO, 0x00 ); // AFC Loop Gearshift Override
WriteByte ( REG_AFCTC, 0x02 ); // AFC Timing Control
/*
* Set the "Clock Recovery Gearshift Value". The original code set it to 0x00,
* however, the recommended value from A440 is 0x05.
* We are not sending data so have no need to syncronise clocks between Tx and Rx
* so just leave at the default value
*/
WriteByte ( REG_CRGO, 0x03 ); // Recommended value from A440
// WriteByte ( REG_CROSR, 0x78 ); // Clock Recovery Oversampling Ratio
WriteByte ( REG_CRO2, 0x01 ); // Clock Recovery Offset 2
WriteByte ( REG_CRO1, 0x11 ); // Clock Recovery Offset 1
WriteByte ( REG_CRO0, 0x11 ); // Clock Recovery Offset 0
WriteByte ( REG_CRTLG1, 0x01 ); // Clock Recovery Timing Loop Gain 1
WriteByte ( REG_CRTLG0, 0x13 ); // Clock Recovery Timing Loop Gain 0
WriteByte ( REG_AFCLIM, 0xFF ); // AFC Limiter - Maximum
WriteByte ( REG_OOKC1, 0x28 ); // OOK Counter Value 1
WriteByte ( REG_OOKC2, 0x0C ); // OOK Counter Value 2
WriteByte ( REG_SPH, 0x28 ); // OOK Attack & Decay settings
WriteByte ( REG_DATAC, 0x61 ); // Disable packet handling
/*
* The original code had all these choices for what to put into the "REG_AGCOR1"
* register (0x69) to control the LNA and pre-amp. Pick only one.
*/
// WriteByte ( REG_AGCOR1, 0x00 ); // No AGC, min LNA
// WriteByte ( REG_AGCOR1, LNAGAIN ); // No AGC, max LNA of 20dB
// WriteByte ( REG_AGCOR1, AGCEN ); // AGC enabled, min LNA
WriteByte ( REG_AGCOR1, 0x60 ); // AGC, min LNA, Gain increase during signal reductions
// WriteByte ( REG_AGCOR1, 0x30 ); // AGC, max LNA
// WriteByte ( REG_AGCOR1, 0x70 ); // AGC, max LNA, Gain increase during signal reductions
WriteByte ( REG_GPIO0, 0x12 ); // GPIO-0 TX State (output)
WriteByte ( REG_GPIO1, 0x15 ); // GPIO-1 RX State (output)
WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 output to ground until needed
/*
*
* We turn on receive mode ("RXON"), the PLL ("PLLON") and ready mode
* ("XTON"). The Si4432 module does not have the 32.768 kHz crystal for
* the microcontroller, so we do not turn on the "X32KSEL" bit.
*
* The initial frequency is set to 433.92 MHz which is a typical IF
* Finally the GPIO-2 pin is set to ground.
*
* 03/24 - Logic verified against A440 register edscription document
*/
Tune ( cap ); // Set the crystal capacitance to fine tune frequency
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
SetFrequency ( 443920000 ); // 443.92MHz
delayMicroseconds ( 300 ); // Time to allow the SI4432 state machine to do its stuff
Serial.printf ( "End of Init - _cs = %i\n", _cs );
return true;
}
/*
* "Reset" - Initializes the Si4432.
*
* We write the "SW_RESET" (0x80) bit in the "REG_OFC1" register (0x07).
* Doing so resets all the registers to their default values. The process
* is complete when the "ICHIPRDY" bit (0x02) in the "REG_IS2" register
* (0x04) goes high.
*
* We will try reading the ready bit 100 times with a slight pause between
* tries. When it goes high, we're done.
*
* 03/24 - Logic verified against A440
*/
bool Si4432::Reset ()
{
uint32_t count = 0;
uint32_t startTime = millis ();
uint8_t regRead = 0;
const char* unit[4] = { "RX", "TX", "TGIF", "TGLO" }; // For debugging
WriteByte ( REG_OFC1, SW_RESET ); // Always perform a system reset
delay(10);
while ( millis() - startTime < 8000 ) // Try for 2 seconds
{
regRead = ReadByte ( REG_IS2 );
//****************************************************************************
// Bodge to get Glenn's board going! Do not include in normal release!!!!
// Glenn's LO SI4432 does not seen to send signals back to the SPI bus.
//
//****************************************************************************
#ifdef GLENN_BUILD
if (_cs != 5) {
if ( ( regRead & ICHIPRDY ) && ( regRead != 0xFF ) ) // Wait for chip ready bit
{
Serial.printf ( " %s Si4432 Good to go - regRead = %02X _cs = %i ", unit[_type], regRead, _cs );
Serial.printf ( " Type %X Version %X Status %X \n", ReadByte(0), ReadByte(1), ReadByte(2));
return true; // Good to go!
}
}
else {
if ( regRead & ICHIPRDY ) // Wait for chip ready bit
{
Serial.printf ( " LO Si4432 Good to go - regRead = %02X _cs = %i ", regRead, _cs );
Serial.printf ( " Type %X Version %X Status %X \n", ReadByte(0), ReadByte(1), ReadByte(2));
return true; // Good to go!
}
}
#else
if ( ( regRead & ICHIPRDY ) && ( regRead != 0xFF ) ) // Wait for chip ready bit
{
Serial.printf ( " %s Si4432 Good to go - regRead = %02X _cs = %i ", unit[_type], regRead, _cs );
Serial.printf ( " Type %X Version %X Status %X \n", ReadByte(0), ReadByte(1), ReadByte(2));
return true; // Good to go!
}
#endif
/*
* If we don't have "ICHIPRDY" yet, only display the error message once per second.
*/
if ((( millis () - startTime ) % 1000 ) == 0 )
{
Serial.print ( "Waiting for " );
Serial.print ( unit[_type] );
Serial.print ( " Si4432 - regRead " );
Serial.println ( regRead );
Serial.printf ( "Type %X Version %X Status %X \n", ReadByte(0), ReadByte(1), ReadByte(2));
}
delay ( 1 ); // Slight pause
}
Serial.printf ( "%s Si4432 Reset failed - _cs = %i\n", unit[_type], _cs );
return false;
}
/*
* "WriteByte" sends a byte of data into the selected Si4432 register. The
* specified register number is transmitted first with the MSB turned on
* which indicates it's a write operation. That is followed by the data byte
* to be written to the specified register.
*/
void Si4432::WriteByte ( byte reg, byte setting )
{
reg |= 0x80 ; // Indicate this is a "write" operation
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
//spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( reg ); // Send the register address
_spi->transfer ( setting ); // and the data byte
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction(); // Release the bus
// delayMicroseconds ( WRITE_DELAY );
}
/*
* "ReadByte" reads a byte of data from the selected Si4432. Here we send the
* register with the MSB set to zero indicating that we want to read the
* specified register.
*
* The "transfer" call to read the data has a dummy argument of '0'; the
* argument is needed but has absolutely no meaning.
*/
uint8_t Si4432::ReadByte ( uint8_t reg )
{
uint8_t regValue; // Contains the requested data
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
//spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( reg ); // Send the register address
regValue = _spi->transfer ( 0 ); // Read the data byte
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
return regValue; // Return the answer
}
/*
* "SetFrequency" sets the Si4432 frequency. Technical note A440 explains some of
* what's going on in here!
*/
void Si4432::SetFrequency ( uint32_t freq )
{
int hbsel; // high band select, but shifted to the correct location in the byte
int sbsel; // Sideband select bit
uint16_t carrier; // Carrier frequency
uint32_t reqFreq = freq; // Copy of requested frequency
uint8_t fbs; // Frequency band select value (N - 24)
uint8_t ofc1; // To read the "REG_OFC1" register
uint8_t registerBuf[4]; // Used to send frequency data in burst mode
if ( freq >= 480000000 ) // Frequency > 480MHz (high band)?
{
hbsel = HBSEL; // High band is 480MHz to 960MHz
freq = freq / 2; // And divide the frequency in half
}
else // Frequency requested is less than 480MHz
{
hbsel = 0x00; // Low band is 240KHz to 479.9MHz
}
sbsel = SBSEL; // Select high sideband (always)
/*
* add half the frequency resolution to required frequency so the actual value is rounded to nearest, not lowest
* Frequency resoluion is 156.25Hz in low band, 312.5Hz in high band but freq is already divided above
*/
freq = freq + 78;
/*
* "N" picks the 10MHz range for low band and the 20MHz range for the high band.
* It's explained in "Table 12" in the Silicon Labs datasheet.
*/
int N = freq / 10000000; // Divide freq by 10MHz
/*
* The low order 5 bits of the "Frequency Band Select" register (0x75) get
* loaded with "N - 24" and we add in the band select and sideband select
* bits.
*/
fbs = (( N - 24 ) | hbsel | sbsel );
/*
* Compute the actual carrier frequency.
*
* It takes a few things to actually set the frequency. See Tech Note A440
* for an explanation (it made my head hurt)!
*
* The carrier frequency is actually an offset from the lower end of the
* frequency band selected (N-24), so for example in the initialization
* sequence we set the carrier frequency to 443,920,000. From Table 12 in
* the datasheet, we see that the frequency is in the "Low Band" (less than
* 480MHz) and the actual band will be '20', and "N" will be '44'.
*
* The "carrier" value is the number of '156.25Hz" increments from the base
* frequency of the band. For a frequency of 443,920,000, the answer is
* 25,088.
*
* If we're operating on the "High Band", the "carrier" is the number of
* '312.5Hz' increments from the base frequency of the band.
*/
carrier = ( 4 * ( freq - N * 10000000 )) / 625;
//if (_cs == 2 )
// Serial.printf ( "\nRequested frequency = %u \n", reqFreq );
/*
* M0WID mod - Update the frequency in "burst" mode as opposed to separate
* writes for each register
*/
uint8_t ncf1 = ( carrier >> 8 ) & 0xFF;
registerBuf[0] = REG_FBS|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fbs; // FBS register value
registerBuf[2] = ncf1 ; // NCF1 value
registerBuf[3] = carrier & 0xFF; // NCF0 value
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 4 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
_ncf1 = ncf1;
_fbs = fbs;
uint32_t fb = ( fbs & F_BAND ) ;
_hbsel = hbsel>>5; // should be 1 or 0
// _freq will contain the actual frequency, not necessarily what was requested
_freq = (double)(10000000 * (_hbsel + 1 )) * ( (double)fb + (double)24 + (double)carrier / (double)64000) ;
// Serial.printf("set Freq :%i, actual:%i, fb:%i, fc:%i, hbsel:%i\n", reqFreq, _freq, fb, carrier, hbsel);
// Serial.print ( ", N = " );
// Serial.print ( N );
// fbs = ReadByte ( REG_FBS );
// Serial.print ( ", Freq_Band = " );
// Serial.println ( fbs & F_BAND );
// Serial.print ( "hbsel = " );
// Serial.print ( fbs & HBSEL, HEX );
// carrier = ReadByte ( REG_NCF1 ) << 8;
// carrier |= ReadByte ( REG_NCF0 );
// Serial.print ( ", Carrier = " );
// Serial.print ( carrier );
// ofc1 = ReadByte ( REG_OFC1 );
// Serial.print ( ", REG_OFC1 = " );
// Serial.println ( ofc1, HEX );
/*
* This delay is needed in the test programs. In the real TinySA software it is
* handled by the calling program.
*/
// delayMicroseconds ( _dt );
}
/*
* "SetOffsetFreq" adjusts the frequency from the nominal value set in SetFrequency.
* It is intended for use by the SI4432 AFC algorithm to enable more accurate
* frequency matching of different radios, but we can turn off the AFC and use it
* to adjust the frequency without having the Si4432 go through its TX-RX-TX state machine
* and enable reduced edlay times from setting a frequency to getting valid readings
* The offset can vary +- 80kHz in low bands (f<480MHz) and +-160kHz in high bands (480-960MHz)
* negative numbers are twos complement of the positive offset
*/
void Si4432::SetOffsetFreq ( int32_t freq )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint32_t posFreq;
if (freq < 0)
posFreq = -freq;
else
posFreq = freq;
uint16_t fo = (double)posFreq/(156.25 * (double)(_hbsel + 1));
if (freq < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" offset frequency %i fo=%3x \n", freq, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
}
/*
* "SetOffset" sets the frequency offset registers to correspond to the number of offset steps
*/
void Si4432::SetOffset ( int32_t offset )
{
uint8_t registerBuf[3]; // Used to send data in burst mode
uint16_t posOffset;
if (offset < 0)
posOffset = -offset;
else
posOffset = offset;
uint16_t fo = posOffset;
if (offset < 0)
// do twos complement - invert the bits (0-9) (use XOR) and add one
fo = (fo ^ 0x3FF) + 1;
//Serial.printf(" posoffset %3x, offset %i fo=%3x \n", posOffset, offset, fo);
// write to the Si4432
registerBuf[0] = REG_FOFF1|0x80; // First register in write mode (bit 7 set)
registerBuf[1] = fo & 0xFF; // first 8 bits
registerBuf[2] = (fo >> 8 ) & 0x03; // last 2 bits
//_spi->beginTransaction ( SPISettings ( BUS_SPEED, BUS_ORDER, BUS_MODE ));
// spiSimpleTransaction(_spi->bus());
digitalWrite ( _cs, LOW ); // Select the correct device
_spi->transfer ( registerBuf, 3 ); // Send the data
digitalWrite ( _cs, HIGH ); // Deselect the device
//_spi->endTransaction();
}
/*
* "SetRBW" Sets the "Resolution Bandwidth" based on a required value passed in
* and returns the actual value chosen as well as the required delay to allow the
* FIR filter in the SI4432 to settle (in microseconds) Delay time is longer for
* narrower bandwidths.
* The filter index is also returned.
* Filters flagged as DO_NOT_USE will not be chosen
*/
float Si4432::SetRBW ( float reqBandwidth10, unsigned long* delaytime_p, uint16_t* filter_p ) // "reqBandwidth" in kHz * 10
{
int filter = _bpfCount-1; // Elements in the "bandpassFilters" array
// Serial.printf ( "bpfCount %i\n", filter );
/*
* "filter" is the index into "bandpassFilters" array. If the requested
* bandwidth is less than the bandwith in the first entry, we use that entry (2.6KHz).
*/
if ( reqBandwidth10 <= _bandpassFilters[0].bandwidth10 )
{
filter = 0;
// Serial.print ("Minimum RBW");
}
/*
* If the requested bandwidth is greater or equal to the value in the first entry,
* find the setting that is nearest (and above) the requested setting.
*/
else
while ( ( ( _bandpassFilters[filter-1].bandwidth10 > reqBandwidth10 - 0.01 ) && ( filter > 0 ) )
|| (_bandpassFilters[filter].Status == DO_NOT_USE ) )
filter--;
// Serial.printf ( "Required = %f, filter = %i\n", reqBandwidth10, filter); Serial.println ( filter );
/*
* Ok, we found the appropriate setting (or ended up with the maximum one),
* formulate the byte to sent to the "REG_IFBW" register (0x1C) from the piece parts.
*/
byte BW = ( _bandpassFilters[filter].dwn3_bypass << 7 )
| ( _bandpassFilters[filter].ndec_exp << 4 )
| _bandpassFilters[filter].filset;
WriteByte ( REG_IFBW ,BW ); // Send the bandwidth setting to the Si4432
/*
* "Oversampling rate for clock recovery". Let me know if you understand the explanation
* in Tech Note A440!
*/
float rxosr = 500.0 * ( 1.0 + 2.0 * _bandpassFilters[filter].dwn3_bypass )
/ ( pow ( 2.0, ( _bandpassFilters[filter].ndec_exp - 3.0 ))
* _bandpassFilters[filter].bandwidth10 / 10.0 );
byte integer = (byte) rxosr ;
byte fractio = (byte) (( rxosr - integer ) * 8 );
byte memory = ( integer << 3 ) | ( 0x07 & fractio );
WriteByte ( REG_CROSR , memory ); // Clock recovery oversampling rate
/*
* Set the bandwidth and delay time in the "RBW" structure returned by the function
* and in our internal variables.
*/
_bw = _bandpassFilters[filter].bandwidth10 / 10.0;
_dt = _bandpassFilters[filter].settleTime;
*delaytime_p = _dt;
*filter_p = filter;
return _bw;
}
void Si4432::SetPreampGain ( int gain ) // Sets preamp gain
{
WriteByte ( REG_AGCOR1, gain ); // Just feed it to the Si4432
_gainReg = gain;
_autoGain = (bool)(gain & AGCEN);
// Serial.printf("Si4432 set gain:%i, auto:%i\n", _gainReg, _autoGain);
}
/*
* "GetPreampGain" was added by M0WID to read the LNA/PGA gain from the RX Si4432. Later
* modified to return the AGC setting state (on or off) and the "PGAGAIN | LNAGAIN"
* settings via the pointers in the argument list.
*/
int Si4432::GetPreampGain ()
{
if (_autoGain) {
return ReadByte ( REG_AGCOR1 ); // Just return the register
} else {
return _gainReg;
}
}
int Si4432::ReadPreampGain ()
{
return ReadByte ( REG_AGCOR1 ); // Just return the register
}
/*
* "PreAmpAGC" returns "true" if the AGC is set to auto:
*/
bool Si4432::PreAmpAGC () // Return true if agc set to auto
{
/*
* Register 0x69 (REG_AGCOR1) contains the current setting of the LNA and PGA
* amplifiers. If AGC is enabled the value can vary during a sweep
*/
byte Reg69 = ReadByte ( REG_AGCOR1 ); // Read the register
// Serial.printf ( "REG_AGCOR1 %X \n", Reg69 ); // Debugging
return ( Reg69 & AGCEN ); // And the answer is!
}
bool Si4432::GetPreAmpAGC () // Return true if agc set to auto
{
return ( _autoGain );
}
/*
* "GetRSSI" is perhaps the most important function in the whole TinySA program.
* It returns the "Received Signal Strength Indicator" value from the receiver
* module. The RSSI is essentially an "S" meter indication from the receiver that
* will be used to measure the received level. The daya dheet indicates and accuracy
* of +/- 0.5dB.
*/
uint8_t Si4432::GetRSSI ()
{
uint8_t rawRSSI;
rawRSSI = ReadByte ( REG_RSSI );
// float dBm = 0.5 * rawRSSI - 120.0 ;
// Serial.println ( dBm, 2 );
return rawRSSI ;
}
/*
* "SetPowerReference" - Set the GPIO-2 output for the LO (TX) SI4432 to required
* frequency, or off.
*
* If freq < 0 or > 6 GPIO-2 is grounded
*
* Freq = 0 30MHz
* Freq = 1 15MHz
* Freq = 2 10MHz
* Freq = 3 4MHz
* Freq = 4 3MHz
* Freq = 5 2MHz
* Freq = 6 1MHz
*/
void Si4432::SetPowerReference ( int freq )
{
if ( freq < 0 || freq > 6 ) // Illegal frequency selection?
WriteByte ( REG_GPIO2, 0x1F ); // Set GPIO-2 to ground
else
{
WriteByte ( REG_GPIO2, 0xC0 ); // Maximum drive and microcontroller clock output
WriteByte ( REG_MCOC, freq & 0x07 ); // Set GPIO-2 frequency as specified
}
}
/*
* "SetDrive" can be used to to set the transmitter (aka local oscillator) power
* output level.
*
* The drive value can be from '0' to '7' and the output power will be set
* according to the following table (from page 39 of the Si4432 datasheet):
*
* 0 => +1dBm 4 => +11dBm
* 1 => +2dBm 5 => +14dBm
* 2 => +5dBm 6 => +17dBm
* 3 => +8dBm 7 => +20dBm
*/
void Si4432::SetDrive ( uint8_t level ) // Sets the LO drive level
{
if (( level < 0 ) || ( level > 7 ))
{
// Serial.printf ( "VFO %i Drive request refused level was %i\n", _type, level );
return;
}
else
{
_pwr = level;
WriteByte ( REG_TXPWR, _pwr ); // Set power level
// Serial.printf ( "VFO %i Drive set to %i\n", _type, _pwr );
}
}
/*
* The transmitter module isn't always in transmit mode and the receiver module isn't
* always in receive mode, so these two functions allow us to switch modes for one or
* the other:
*/
void Si4432::TxMode ( uint8_t level ) // Put module in TX mode
{
WriteByte ( REG_OFC1, ( TXON | PLLON | XTON )); // Transmitter, PLL and "Ready Mode" all on
SetDrive ( level );
}
void Si4432::RxMode () // Put module in RX mode
{
WriteByte ( REG_OFC1, ( RXON | PLLON | XTON )); // Receiver, PLL and "Ready Mode" all on
}
void Si4432::ReadyMode () // Put module into ready mode, rx and tx off
{
WriteByte ( REG_OFC1, XTON ); // "Ready Mode" on
}
void Si4432::SetMode (uint8_t newMode)
{
WriteByte ( REG_OFC1, newMode ); // "Ready Mode" on
}
uint8_t Si4432::GetMode ()
{
return ReadByte ( REG_OFC1 );
}
/*
* "Tune" sets the crystal tuning capacitance.
*
* We need to cycle the PLL to force a recalibration after the tune
*/
void Si4432::Tune ( uint8_t cap ) // Set the crystal tuning capacitance
{
_capacitance = cap; // Save in local data
WriteByte ( REG_COLC, _capacitance ); // Send to the Si4432
uint8_t currentMode = ReadByte ( REG_OFC1 );
ReadyMode ();
delayMicroseconds(300);
SetMode ( currentMode );
}
/*
* Get frequency from Si4432
*/
uint32_t Si4432::GetFrequency ()
{
return _freq;
}
uint32_t Si4432::ReadFrequency ()
{
uint8_t fbs = ReadByte(REG_FBS);
uint16_t ncf1 = ReadByte(REG_NCF1);
uint16_t ncf0 = ReadByte(REG_NCF0);
uint32_t fc = ( ncf1<<8 ) + ncf0;
uint32_t fb = ( fbs & F_BAND ) ;
uint32_t hb = ( fbs & HBSEL ) >> 5; // HBSEL is bit 5
// Serial.printf ( "FBS=%X ncf1=%X ncf0=%X HBSEL=%X F_BAND=%X fc=%X (%u)\n",
// fbs, ncf1, ncf0, hb, fb, fc, fc);
uint32_t f = (uint32_t) ( 10000000.0 * ( (float) hb + 1.0 )
* ( (float) fb + 24.0 + ( (float) fc ) / 64000.0 ));
return f;
}
/*
* "GetBandpassFilter10" - Get filter bandwidth * 10 from the specifed element of
* the bandpassfilter array
*/
uint16_t Si4432::GetBandpassFilter10 ( uint8_t index )
{
if ( index >= _bpfCount )
return 0;
else
return _bandpassFilters[index].bandwidth10;
}
uint8_t Si4432::GetBandpassFilterCount ()
{
return _bpfCount;
}
/*
* For debugging, we can do a register dump on the module.
*/
void Si4432::PrintRegs () // Dump all the Si4432 registers
{
for ( int r = 0; r < 0x80; r++)
if ( _type == RX_4432 ) // Receiver?
Serial.printf ( "RX Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TX_4432 ) // Transmitter?
Serial.printf ( "TX Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TGIF_4432 ) // Transmitter?
Serial.printf ( "TGIF Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
else if ( _type == TGLO_4432 ) // Transmitter?
Serial.printf ( "TGLO Reg[0x%02X] = 0x%02X\n", r, ReadByte ( r ));
}

252
si4432.h Normal file
View File

@ -0,0 +1,252 @@
/*
* "Si4432.h"
*
* Added to the program in Version 1.1 by John Price (WA2FZW):
*
* Modified in Version 1.7 to create a "true" class/object implementation for
* handling the Si4432 modules.
* Modified by M0WID for 2.6 to remove dependencies on tinySA specific include files
* and use SPI class pointer in the constructor
*
* This file contains symbolic definitions for all the Si4432 registers that are
* used in the program and symbols for some of the values that get loaded into or
* read from them. For full explanations (whether you can make sense out of them
* or not) of the registers please refer to the "Silicon Labs Si4430/31/32 - B1"
* data sheet and "Silicon Labs Technical Note A440".
*
* Some of the functions in here apply only to the receiver and some apply only
* to the transmitter. The "Init" functions remember which type of module is
* being initiated. Eventually we will use that to avoid doing things which don't
* make sense for one type or the other.
*
* It also contains some structure definitions and some data elements we keep
* to ourselves.
*/
#ifndef SI4432A_H_
#define SI4432A_H_ // Prevent double inclusion
#include <Arduino.h> // Basic Arduino definitions
#include <SPI.h> // SPI bus related stuff
#define ELEMENTS(x) ( sizeof ( x ) / sizeof ( x[0] ))
/*
* In the original program, these were the indicies into the "SI_nSEL" array used
* to indicate whether the receiver or transmitter module was selected to perform
* an operation on.
*
* In this implementation, the "_type" variable is set to one or the other in the
* initialization functions to remember which type we are.
*
* The "_type" value will eventually be used to prevent certain operations from
* being accidently performed on the wrong type of module; for example, setting the
* RBW ("SetRBW") is not valid for the transmitter module or "Get_RSSI" is only
* applicable to the receiver module.
*/
#define RX_4432 0 // Receiver is Si4432 #0
#define TX_4432 1 // Transmitter is Si4432 #1
#define TGIF_4432 2
#define TGLO_4432 3
/*
* The maximum SPI bus speed for the Si4432 is 10MHz. It operates in SPI MODE0 and
* the address and data are transmitted MSBFIRST.
*
* That being the case, the following definitions will be used in the read and write
* functions to set those parameters before each transaction:
*/
#define BUS_SPEED 10000000 // 10 MHz
#define BUS_MODE SPI_MODE0 // Data is read on rising edge of the clock
#define BUS_ORDER MSBFIRST // Send stuff MSB first
/*
* "WRITE_DELAY" is a time in microseconds that seems to be required after each write
* to one of the Si4432 registers on some (but not all) Si4431 modules. You can try
* reducing the value to '1', but if you have problems getting one of them to actually
* work, try increasing the value. '25' is the value that works for my modules.
*/
#define WRITE_DELAY 25
/*
* Si4432 Registers used in the program and bit definitions for some of them:
*/
#define REG_IS2 0x04 // Interrupt Status #1
#define ICHIPRDY 0x02 // Chip ready
#define REG_INT1 0x06 // Interrupt enable register 1
#define REG_OFC1 0x07 // Operating & Function Control 1
#define XTON 0x01 // Ready mode on (see datasheet)
#define PLLON 0x02 // PLL on
#define RXON 0x04 // Receiver on
#define TXON 0x08 // Transmitter on
#define X32KSEL 0x10 // Use external 32KHz crystal
#define ENLBD 0x40 // Device enabled
#define SW_RESET 0x80 // System reset
#define REG_COLC 0x09 // Crystal Oscillator Load Capacitance
#define REG_MCOC 0x0A // Microcontroller Output Clock
#define REG_GPIO0 0x0B // GPIO0 Configuration
#define REG_GPIO1 0x0C // GPIO1 Configuration
#define REG_GPIO2 0x0D // GPIO2 Configuration
#define REG_IFBW 0x1C // IF Filter Bandwidth
#define REG_AFCGSO 0x1D // AFC Loop Gearshift Override
#define REG_AFCTC 0x1E // AFC Timing Control
#define REG_CRGO 0x1F // Clock Recovery Gearshift Override
#define REG_CROSR 0x20 // Clock Recovery Oversampling Ratio
#define REG_CRO2 0x21 // Clock Recovery Offset 2
#define REG_CRO1 0x22 // Clock Recovery Offset 1
#define REG_CRO0 0x23 // Clock Recovery Offset 0
#define REG_CRTLG1 0x24 // Clock Recovery Timing Loop Gain 1
#define REG_CRTLG0 0x25 // Clock Recovery Timing Loop Gain 0
#define REG_RSSI 0x26 // Received Signal Strength Indicator
#define REG_AFCLIM 0x2A // AFC Limiter
#define REG_OOKC1 0x2C // OOK Counter Value 1
#define REG_OOKC2 0x2D // OOK Counter Value 2
#define REG_SPH 0x2E // Slicer Peak Hold
#define REG_DATAC 0x30 // Data access control
#define REG_AGCOR1 0x69 // AGC Override 1
#define LNAGAIN 0x10 // LNA enabled
#define AGCEN 0x20 // AGC enabled
#define PGAGAIN 0x0F // PGA Gain value in dB/3
#define AGC_ON 0x60 // Turn the AGC on
#define REG_TXPWR 0x6D // Transmitter Power
#define REG_FOFF1 0x73 // Frequency Offset registers
#define REG_FOFF2 0x74
#define REG_FBS 0x75 // Frequency Band Select
#define SBSEL 0x40
#define HBSEL 0x20
#define F_BAND 0x1F
#define REG_NCF1 0x76 // Nominal Carrier Frequency 1
#define REG_NCF0 0x77 // Nominal Carrier Frequency 0
enum {DO_NOT_USE, USE_IN_AUTO, AVAILABLE}; // bandpass filter status values
typedef struct
{
float bandwidth10; // Just one decimal point to save space (kHz * 10)
unsigned long settleTime; // Narrower bandwidth filter needs longer settling time before RSSI is stable
uint16_t dwn3_bypass; // Bypass decimate by 3 stage if set
uint16_t ndec_exp; // IF Filter decimation rate = 2^ndec_exp. larger number -> lower bandwidth (range 0-5)
uint16_t filset; // IF Filter coefficient set. Predefined FIR filter sets (1-15)
uint16_t Status; // 0 = DO_NOT_USE, 1 = USE_IN_AUTO, 2 = AVAILABLE
} bandpassFilter_t;
class Si4432
{
public:
explicit Si4432 ( SPIClass* spi, uint8_t cs, uint8_t id ); // Constructor
bool Init ( uint8_t cap ); // Initialize the SI4432 module, return false if failed
//bool TX_Init ( uint8_t cap, uint8_t power ); // Initialize the transmitter module, return false if failed
void SetFrequency ( uint32_t Freq ); // Set module's frequency
uint32_t GetFrequency (); // Get the module's frequency
uint32_t ReadFrequency (); // Read frequency from SI4432
void SetOffsetFreq ( int32_t freq ); // Set a frequency offset from the nominal frequency
void SetOffset ( int32_t offset ); // Offset based on number of offset increments
void SetPowerReference ( int freq ); // Set the GPIO output for the LO
void SetDrive ( uint8_t level ); // Sets the drive level
float SetRBW ( float reqBandwidth10, unsigned long* delaytime_p, uint16_t* filter_p ); // "reqBandwidth" in kHz * 10
void SetPreampGain ( int gain ); // Sets preamp gain
int GetPreampGain (); // Get current gain register
int ReadPreampGain (); // Read gain register from SI4432
bool PreAmpAGC(); // Reads the register and return true if agc set to auto
bool GetPreAmpAGC (); // Return true if agc set to auto
uint8_t GetRSSI (); // Get Receiver Signal Strength
void RxMode (); // Put module in RX mode
void TxMode ( uint8_t level ); // Put module in TX mode
void ReadyMode (); // Put module in READY mode, crystal on, PLL off
uint8_t GetMode ();
void SetMode (uint8_t newMode);
void Tune ( uint8_t cap ); // Set the crystal tuning capacitance
void WriteByte ( byte reg, byte setting ); // Write a byte of data to the specified register
uint8_t ReadByte ( uint8_t reg ); // Read a byte of data from the specified register
uint16_t GetBandpassFilter10 ( uint8_t index ); // Return the filter bandwidth for selected element of bandpassFilters array
uint8_t GetBandpassFilterCount ();
void PrintRegs (); // Dump all the Si4432 registers
/*
* These are common functions that are only accessible from within the object.
*/
private:
void SubInit (); // Initialization common to both modules
bool Reset (); // Initialize the module
/*
* Private data elements:
*/
uint8_t _cs; // Chip select pin number for the module
float _bw; // Current bandwidth setting
uint32_t _dt; // Current delay time setting
uint8_t _pwr; // Current power output (TX only?)
uint8_t _type; // Transmitter or receiver
uint8_t _capacitance; // Crystal load capacitance
SPIClass* _spi; // Pointer to the SPI object
int _hbsel; // High band (1) or low band (0)
uint8_t _fbs; // Current value of frequency band select register
uint8_t _ncf1; // Current value for most significant byte of carrier
uint32_t _freq; // Current actual frequency
// can be different to that requested due
// to resolution)
int _gainReg; // value of the gain reg written to the device
bool _autoGain; // true if auto
static bandpassFilter_t _bandpassFilters[];
static uint8_t _bpfCount; // Number of elements in bandpassFilters array
}; // End of class "Si4432"
#endif

375
simpleSA.h Normal file
View File

@ -0,0 +1,375 @@
/*
* "tinySA.h"
*
* This file contains various parameters for the TinySA spectrum analyzer software.
*
* In general, the user should not have any need to change anything defined in here.
* All the things that a user might need to (or want to) change can be found in the
* "My_SA.h" file.
*
* The starting point for this version is the "tinySA_touch02" software developed
* by Dave (M0WID). That software is based on the original version by Erik Kaashoek.
*
* Modified by John Price (WA2FZW):
*
* Version 1.0:
*
* Just add comments and try to figure out how it all works!
*
*
* Version 1.7:
*
* Moved lots of definitions from the main file to here to reduce the clutter
* in that file.
*/
#ifndef TINYSA_H_
#define TINYSA_H_ // Prevent double inclusion
#include "my_SA.h" // User settable parameters
#include <Arduino.h> // General Arduino definitions
#include <TFT_eSPI.h>
#define PROGRAM_NAME "simpleSA" // These are for the WiFi interface
#define PROGRAM_VERSION "Version 0.11" // Current version - beta!
/*
* I think this symbol defines the number of different trace types that are available,
* but I'm not sure about that yet. This is the way Dave has it set, so until I figure
* it out, it will stay set at '1'.
*/
#define TRACE_COUNT 1 // Number of different traces available
/*
* Define variables and functions associated with drawing stuff on the screen.
*/
#define DISPLAY_POINTS 290 // Number of scan points in a sweep
#define BANDSCOPE_POINTS 80 // Number of scan points in a bandscope sweep
#define CHAR_HEIGHT 8 // Height of a character
#define HALF_CHAR_H 4 // Half a character height
#define CHAR_WIDTH 6 // Width of a character
#define X_GRID 10 // Number of vertical grid lines
#define Y_GRID 10 // Number of horizontal grid lines
#define DELTA_X ( DISPLAY_POINTS / X_GRID ) // Spacing of x axis grid lines
#define DELTA_Y ( 21 ) // Spacing of y axis grid lines
#define X_ORIGIN 27 // 'X' origin of checkerboard
#define Y_ORIGIN ( CHAR_HEIGHT * 2 + 3 ) // 'Y' origin of checkerboard
#define GRID_HEIGHT ( Y_GRID * DELTA_Y ) // Height of checkerboard
#define WATERFALL_HEIGHT 100 // Height of waterfall in Bandscope mode
/*
* Definitions used in signal generator mode
*/
enum {SIG_MENU_KEY, SIG_FM_KEY, SIG_AM_KEY, SIG_ON_KEY, SIG_FREQ_KEY, SIG_MOD_KEY};
#define SA_FONT_LARGE "NotoSansBold56"
// sig gen mode key position, size and font
#define KEY_W 50 // Width and height
#define KEY_H 40
#define NUM_W 31 // width for numeric digits
#define NUM_H 33 // height for numeric digit +/- keys
#define KEY_FONT "NotoSansSCM14" //Semi Condensed Monospaced 14pt
#define KEY_COLOR TFT_WHITE
#define KEY_SEL_COLOR TFT_CYAN
#define KEY_ON_COLOR TFT_GREEN
#define KEY_OFF_COLOR TFT_PINK
#define SIG_KEY_COUNT 18
#define MAX_SIGLO_FREQ 250000000
#define MIN_SIGLO_FREQ 100
/*
* Symbols for the various attenuator options
*/
#define PE4302_PCF 1
#define PE4302_GPIO 2
#define PE4302_SERIAL 3
/*
* Color definitions for the standard displays; Again, these need to be moved to
* a separate header file.
*
* Modified in M0WID Version 05 - Eliminate all but the ILI9431 color definitions.
* Modified in WA2FZW Version 1.1 - Change all "DISPLAY_color" to simply "color".
*
* The "TFT_color" values are defined in the "TFT_eSPI.h" file in the library.
*/
#define WHITE TFT_WHITE
#define BLACK TFT_BLACK
#define DARKGREY TFT_DARKGREY
#define YELLOW TFT_YELLOW
#define ORANGE TFT_ORANGE
#define RED TFT_RED
#define GREEN TFT_GREEN
#define BLUE TFT_BLUE
#define PINK TFT_PINK
#define LT_BLUE 0x6F1F
#define MAGENTA TFT_MAGENTA
#define INVERSE TFT_WHITE
#define BACKGROUND TFT_BLACK // Default background color
#define SCREEN_WIDTH 320 // Display width, in pixels
#define SCREEN_HEIGHT 240 // Display height, in pixels
#define DELAY_ERROR 2 // Time in seconds to show error message on display
#define ERR_INFO 1 // Informational type error
#define ERR_WARN 2 // Warning
#define ERR_FATAL 3 // Fatal error
/*
* A factor used to increase the number of measurement points above that calculated by
* just dividing the sweep span by the RBW. Allows for some overlap to reduce the effect
* of the 3dB drop at the filter edges
*/
#define OVERLAP 1.1
/*
* Minimum delay left for the websockets to be checked while waiting
* for the SI4432 filters to settle
* Webocket.loop() takes around 35microseconds, varies a bit
*/
#define MIN_DELAY_WEBSOCKETS 50
/*
* These are the minimum and maximum values for the "IF Frequency". In reality, testing has
* shown that settings of more than about 100KHz from the normal 433.92MHz cause lots of
* spurs, but some SAW filters may behave differently.
*/
#define MIN_IF_FREQ 433000000UL // 433MHz
#define MAX_IF_FREQ 435000000UL // 435MHz
/*
* Tracking Generator offset limits - note signed
*/
#define MIN_TG_OFFSET -1000000L // -1MHz
#define MAX_TG_OFFSET 1000000L // +1MHz
/*
* SI4432 max and min drive levels
*/
#define MIN_DRIVE 0
#define MAX_DRIVE 7
/*
* SI4432 MAx no of bandpass filters
*/
#define MAX_SI4432_FILTERS 57
/*
* The various operating modes:
*
* Only SA_LOW_RANGE implemented so far - some of these may never get implemented!
* Low range is using the mixer, range around 1Mhz-250Mhz depending on low pass
* filter installed.
*
* High range is direct to the LO SI4432, bypassing mixer, attenuator and filters,
* range approx 240MHz - 930Mhz
*
* High range performance will be limited due to no bandpass filtering other than
* in SI4432 itself.
*/
enum { SA_LOW_RANGE, SA_HIGH_RANGE, SIG_GEN_LOW_RANGE, SIG_GEN_HIGH_RANGE,
IF_SWEEP, ZERO_SPAN_LOW_RANGE, ZERO_SPAN_HIGH_RANGE, BANDSCOPE, RX_SWEEP, OTA_UPDATE };
/*
* The "AV_XXXX" symbols define various options for how readings are averaged"
*/
enum { AV_OFF, AV_MIN, AV_MAX, AV_2, AV_4, AV_8 };
/*
* Modulation types for signal generator mode
*/
enum { MOD_OFF, MOD_AM, MOD_FM, NOISE };
/*
* This is a macro that is used to determine the number of elements in an array. It figures
* that out by dividing the total size of the array by the size of a single element. This is
* how we will calculate the number of entries in the "msgTable" array.
*/
#define ELEMENTS(x) ( sizeof ( x ) / sizeof ( x[0] ))
#define min(a,b) ( a<b ? a : b )
/*
* The "peak_t" type structure is used for recording the locations of the markers:
*/
typedef struct {
uint8_t Level;
uint16_t Index;
uint32_t Freq;
} peak_t;
/*
* The "settings_t" structure defines the parameters used to configure the
* spectrum analyzer for a particular scan.
*
* The "settings" are saved as a file in the flash memory and can be recalled
* from there.
*/
typedef struct {
uint32_t ScanStart = 0; // Scan start frequency (***)
uint32_t ScanStop = 100000000; // Scan end frequency (***)
uint32_t IF_Freq = 433920000; // Default IF frequency (***)
int16_t MaxGrid = -10; // Default dB for top line of graph (***)
int16_t MinGrid = -110; // Default dB for bottom line of graph (***)
int8_t Attenuate = 0; // Attenuator setting (***)
double ExternalGain = 0.0; // External gain or attenuation
int8_t Generate = 0; // Signal generator mode if not zero (***)
int16_t Bandwidth10 = 0; // Resolution Bandwidth setting*10; 0 = auto
double LevelOffset = 0; // Calibration value (move to config?)
int8_t ReferenceOut = 1; // Transmitter GPIO2 set to 15MHz
int16_t PowerGrid = 10; // dB/vertical divison on the grid
bool Spur = 0; // Spur reduction on or off
int32_t SpurOffset = 300000; // Amount to offset IF if spur reduction on. Can be -ve
uint8_t Average = 0; // Averaging setting (0 - 5)
bool ShowStorage = 0; // Display stored scan (on or off)
bool SubtractStorage = 0; // Subtract stored scan (on or off)
bool ShowGain = 1; // Display gain trace (on or off)
bool ShowSweep = 1; // Display dB trace (on or off)
uint8_t Drive = 6; // LO Drive power (***) (move to config?)
uint8_t SigGenDrive = 5; // Signal generator drive (for RX SI4432)
uint8_t spareUint8t_1 = 5; // spare
uint8_t spareUint8t_2 = 5; // spare
uint8_t PreampGain = 0x60; // AGC on
uint16_t Mode = SA_LOW_RANGE; // Default to low freq range Spectrum analyser mode
uint16_t Timebase = 100; // Timebase for Zero IF modes (milliSeconds)
int16_t TriggerLevel = -40; // Trigger level for ZeroIF mode (dBm)
uint32_t BandscopeStart = 14000000; // Start frequency for bandscope sweep
uint32_t BandscopeSpan = 200000; // Span for the bandscope sweep
uint16_t BandscopePoints = BANDSCOPE_POINTS; // No of points in the sweep. 80/160/320
int16_t BandscopeMaxGrid= -30;
int16_t BandscopeMinGrid= -130;
int16_t BandscopeRBW10 = 28; // Resolution Bandwidth setting*10;
int16_t WaterfallMin = 17; // RSSI values below this are treated as zero
double WaterfallGain = 1.5; // Multiply RSSI value to get colour intensity
/*
* The following line should read:
*
* MkrStatus[MARKER_COUNT] = { MKR_ACTIVE, 0, 0, 0 };
*
* however, there is a chicken & egg thing going on between this file and "Marker.h"
* so for now, the initialization values are hard-coded.
*/
uint8_t MkrStatus[4] = { 0x08, 0, 0, 0 };
/*
* The "Dummy" entry is here for testing purposes. Bu enabling or disabling it, one
* can force the default settings to be used at startup as opposed to the saved ones.
*/
uint32_t Dummy;
} settings_t;
/*
* Setting structure for signal generator mode
*
* Saved as a file to flash.
*/
typedef struct {
uint32_t Frequency = 14000000; // in Hz
uint8_t LO_Drive = 6; // 0 = -1, 1=2, 2=5, 3=8, 4=11, 5=14, 6=17, 7=20 dBm
uint8_t RX_Drive = 3; // as above, B3555 SAW filter max 10dBm, assume some loss in switch, so 4 would be the max allowed
uint8_t ModulationType = MOD_OFF; // see enum
uint16_t ModFrequency = 1000; // in Hz
uint8_t ModDepth = 100; // in %
int16_t Calibration = -13; // in dBm, max power out from your unit with no attenuation
int16_t Power = -15; // in dBm. Required output power.
uint8_t Dummy = 123; // dummy to check if the data is valid
} sigGenSettings_t;
/*
* Setting structure for tracking generator
* If two SI4432 are used for tracking generator IF and LO then
* the Tracking Generator can also be used as a signal generator
*
* Saved as a file to flash.
*/
typedef struct {
uint8_t Mode = 0; // 0 = off, 1 = track, 2 = sig gen
uint32_t Frequency = 14000000; // in Hz
int32_t Offset = 0; // Offset from LO in Hz. Can be -ve
uint8_t LO_Drive = 6; // 0 = -1, 1=2, 2=5, 3=8, 4=11, 5=14, 6=17, 7=20 dBm
uint8_t IF_Drive = 3; // as above, B3555 SAW filter max 10dBm, assume some loss in switch, so 4 would be the max allowed
uint8_t ModulationType = MOD_OFF; // see enum
uint16_t ModFrequency = 1000; // in Hz
uint8_t ModDepth = 100; // in %
int16_t Calibration = -13; // in dBm, max power out from your unit with no attenuation
int16_t Power = -15; // in dBm. Required output power.
uint8_t Dummy = 99; // dummy to check if the data is valid
} trackGenSettings_t;
/*
* The "config" structure defines some general display parameters.
*
*/
typedef struct {
int32_t magic = 1234;
#ifdef __DAC__
uint16_t dac_value;
#endif
uint16_t grid_color = BLACK;
uint16_t menu_normal_color = WHITE;
uint16_t menu_active_color = LT_BLUE;
uint16_t trace_color[TRACE_COUNT] = { LT_BLUE };
uint32_t harmonic_freq_threshold = 0;
int16_t vbat_offset = 0;
uint16_t touch_cal[5] = { 398, 3479, 335, 3465, 1 }; // Default values for TFT_eSPI touch
uint8_t RX_capacitance = RX_CAPACITANCE; // allows some calibration of frequency
uint8_t TX_capacitance = TX_CAPACITANCE;
uint8_t tgLO_capacitance = TG_LO_CAPACITANCE;
uint8_t tgIF_capacitance = TG_IF_CAPACITANCE;
int32_t checksum = 0;
} config_t;
/*
* Used for keys in Sig Gen mode
*/
typedef struct {
uint16_t x;
uint16_t y;
uint16_t width;
uint16_t height;
uint16_t color;
uint16_t activeColor;
const char * text; // pointer to key text
const char * activeText;
} sig_key_t;
#endif // #ifndef TINYSA_H_

1961
sketch_jan8a.ino Normal file

File diff suppressed because it is too large Load Diff

3503
ui.cpp Normal file

File diff suppressed because it is too large Load Diff

32
ui.h Normal file
View File

@ -0,0 +1,32 @@
/*
* "ui.h"
*
* This file contains the definitions of things related to the TinySA touch screen
* interface.
*/
#include <Arduino.h> // Standard stuff
#include "simpleSA.h" // General definitions for the program
#include "preferences.h" // Things to save in flash memory
#ifndef _UI_H_
#define _UI_H_ // Prevent double inclusion
/*
* The "UI_XXXX" symbols define the various modes that the user interface might
* be in such as normal mode, using a touch menu, reading a keypad, etc.
*/
enum { UI_NORMAL, UI_MENU, UI_NUMERIC, UI_KEYPAD };
#define MENU_STACK_DEPTH 6 // Maximum number of menu levels
/*
* "UiProcessTouch" is called from the "loop" function in the main program:
*/
void UiProcessTouch ( void );
void ShowSplash ( void );
#endif