Compare commits

..

1 Commits

Author SHA1 Message Date
Kevin Jahns
256d08cb2a v13.0.0-18 -- distribution files 2017-09-20 11:34:24 +02:00
128 changed files with 11967 additions and 11964 deletions

View File

@@ -1,10 +0,0 @@
{
"source": "./src",
"destination": "./docs",
"plugins": [{
"name": "esdoc-standard-plugin",
"option": {
"accessor": {"access": ["public"], "autoPrivate": true}
}
}]
}

View File

@@ -5,8 +5,6 @@
[include]
./src/
./tests-lib/
./test/
[libs]
./declarations/

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
node_modules
bower_components
docs
/y.*
/examples/yjs-dist.js*

View File

@@ -64,19 +64,6 @@ missing modules.
<script src="./bower_components/yjs/y.js"></script>
```
### CDN
```
<script src="https://cdn.jsdelivr.net/npm/yjs@12/src/y.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-websockets-client@8/dist/y-websockets-client.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-memory@8/dist/y-memory.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-map@10/dist/y-map.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-text@9/dist/y-text.js"></script>
// ..
// do the same for all modules you want to use
```
### Npm
```
npm install --save yjs % add all y-* modules you want to use

View File

@@ -24,8 +24,7 @@
<body>
<div id="aceContainer"></div>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/yjs/y.js"></script>
<script src="../bower_components/ace-builds/src/ace.js"></script>
<script src="./index.js"></script>

View File

@@ -1,17 +1,24 @@
/* global Y, ace */
let y = new Y('ace-example', {
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'ace-example'
},
sourceDir: '/bower_components',
share: {
ace: 'Text' // y.share.textarea is of type Y.Text
}
}).then(function (y) {
window.yAce = y
// bind the textarea to a shared text element
var editor = ace.edit('aceContainer')
editor.setTheme('ace/theme/chrome')
editor.getSession().setMode('ace/mode/javascript')
y.share.ace.bindAce(editor)
})
window.yAce = y
// bind the textarea to a shared text element
var editor = ace.edit('aceContainer')
editor.setTheme('ace/theme/chrome')
editor.getSession().setMode('ace/mode/javascript')
y.define('ace', Y.Text).bindAce(editor)

View File

@@ -13,7 +13,11 @@
<input type="submit" value="Send">
</form>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-map/dist/y-map.js"></script>
<script src="../../../y-text/dist/y-text.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/dist/y-websockets-client.js"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,65 +1,73 @@
/* global Y */
/* global Y, chat */
let y = new Y('chat-example', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'chat-example'
},
sourceDir: '/bower_components',
share: {
chat: 'Array'
}
})
window.yChat = y
let chatprotocol = y.define('chatprotocol', Y.Array)
let chatcontainer = document.querySelector('#chat')
// This functions inserts a message at the specified position in the DOM
function appendMessage (message, position) {
var p = document.createElement('p')
var uname = document.createElement('span')
uname.appendChild(document.createTextNode(message.username + ': '))
p.appendChild(uname)
p.appendChild(document.createTextNode(message.message))
chatcontainer.insertBefore(p, chatcontainer.children[position] || null)
}
// This function makes sure that only 7 messages exist in the chat history.
// The rest is deleted
function cleanupChat () {
if (chatprotocol.length > 7) {
chatprotocol.delete(0, chatprotocol.length - 7)
}).then(function (y) {
window.yChat = y
// This functions inserts a message at the specified position in the DOM
function appendMessage (message, position) {
var p = document.createElement('p')
var uname = document.createElement('span')
uname.appendChild(document.createTextNode(message.username + ': '))
p.appendChild(uname)
p.appendChild(document.createTextNode(message.message))
document.querySelector('#chat').insertBefore(p, chat.children[position] || null)
}
}
cleanupChat()
// Insert the initial content
chatprotocol.toArray().forEach(appendMessage)
// whenever content changes, make sure to reflect the changes in the DOM
chatprotocol.observe(function (event) {
// concurrent insertions may result in a history > 7, so cleanup here
cleanupChat()
chatcontainer.innerHTML = ''
chatprotocol.toArray().forEach(appendMessage)
})
document.querySelector('#chatform').onsubmit = function (event) {
// the form is submitted
var message = {
username: this.querySelector('[name=username]').value,
message: this.querySelector('[name=message]').value
}
if (message.username.length > 0 && message.message.length > 0) {
if (chatprotocol.length > 6) {
// If we are goint to insert the 8th element, make sure to delete first.
chatprotocol.delete(0)
// This function makes sure that only 7 messages exist in the chat history.
// The rest is deleted
function cleanupChat () {
if (y.share.chat.length > 7) {
y.share.chat.delete(0, y.chat.length - 7)
}
// Here we insert a message in the shared chat type.
// This will call the observe function (see line 40)
// and reflect the change in the DOM
chatprotocol.push([message])
this.querySelector('[name=message]').value = ''
}
// Do not send this form!
event.preventDefault()
return false
}
// Insert the initial content
y.share.chat.toArray().forEach(appendMessage)
cleanupChat()
// whenever content changes, make sure to reflect the changes in the DOM
y.share.chat.observe(function (event) {
if (event.type === 'insert') {
for (let i = 0; i < event.length; i++) {
appendMessage(event.values[i], event.index + i)
}
} else if (event.type === 'delete') {
for (let i = 0; i < event.length; i++) {
chat.children[event.index].remove()
}
}
// concurrent insertions may result in a history > 7, so cleanup here
cleanupChat()
})
document.querySelector('#chatform').onsubmit = function (event) {
// the form is submitted
var message = {
username: this.querySelector('[name=username]').value,
message: this.querySelector('[name=message]').value
}
if (message.username.length > 0 && message.message.length > 0) {
if (y.share.chat.length > 6) {
// If we are goint to insert the 8th element, make sure to delete first.
y.share.chat.delete(0)
}
// Here we insert a message in the shared chat type.
// This will call the observe function (see line 40)
// and reflect the change in the DOM
y.share.chat.push([message])
this.querySelector('[name=message]').value = ''
}
// Do not send this form!
event.preventDefault()
return false
}
})

View File

@@ -5,8 +5,7 @@
<body>
<div id="codeMirrorContainer"></div>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/yjs/y.js"></script>
<script src="../bower_components/codemirror/lib/codemirror.js"></script>
<script src="../bower_components/codemirror/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../bower_components/codemirror/lib/codemirror.css">

View File

@@ -1,16 +1,24 @@
/* global Y, CodeMirror */
let y = new Y('codemirror-example', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'codemirror-example'
},
sourceDir: '/bower_components',
share: {
codemirror: 'Text' // y.share.codemirror is of type Y.Text
}
})
}).then(function (y) {
window.yCodeMirror = y
window.yCodeMirror = y
var editor = CodeMirror(document.querySelector('#codeMirrorContainer'), {
mode: 'javascript',
lineNumbers: true
var editor = CodeMirror(document.querySelector('#codeMirrorContainer'), {
mode: 'javascript',
lineNumbers: true
})
y.share.codemirror.bindCodeMirror(editor)
})
y.define('codemirror', Y.Text).bindCodeMirror(editor)

View File

@@ -13,8 +13,11 @@
<button type="button" id="clearDrawingCanvas">Clear Drawing</button>
<svg id="drawingCanvas" viewbox="0 0 100 100" width="100%"></svg>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/d3/d3.min.js"></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-map/dist/y-map.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="../bower_components/d3/d3.js"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,74 +1,84 @@
/* globals Y, d3 */
'strict mode'
let y = new Y('drawing-example', {
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'drawing-example',
url: 'localhost:1234'
},
sourceDir: '/bower_components',
share: {
drawing: 'Array'
}
})
}).then(function (y) {
window.yDrawing = y
var drawing = y.share.drawing
var renderPath = d3.svg.line()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] })
.interpolate('basis')
window.yDrawing = y
var drawing = y.define('drawing', Y.Array)
var renderPath = d3.svg.line()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] })
.interpolate('basic')
var svg = d3.select('#drawingCanvas')
.call(d3.behavior.drag()
.on('dragstart', dragstart)
.on('drag', drag)
.on('dragend', dragend))
var svg = d3.select('#drawingCanvas')
.call(d3.behavior.drag()
.on('dragstart', dragstart)
.on('drag', drag)
.on('dragend', dragend))
// create line from a shared array object and update the line when the array changes
function drawLine (yarray) {
var line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
yarray.observe(function (event) {
line.remove()
line = svg.append('path').datum(yarray.toArray())
// create line from a shared array object and update the line when the array changes
function drawLine (yarray) {
var line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
})
}
// call drawLine every time an array is appended
drawing.observe(function (event) {
event.removedElements.forEach(function () {
// if one is deleted, all will be deleted!!
svg.selectAll('path').remove()
})
event.addedElements.forEach(function (path) {
drawLine(path)
})
})
// draw all existing content
for (var i = 0; i < drawing.length; i++) {
drawLine(drawing.get(i))
}
// clear canvas on request
document.querySelector('#clearDrawingCanvas').onclick = function () {
drawing.delete(0, drawing.length)
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 10)
sharedLine.push([d3.mouse(this)])
yarray.observe(function (event) {
// we only implement insert events that are appended to the end of the array
event.values.forEach(function (value) {
line.datum().push(value)
})
line.attr('d', renderPath)
})
}
// call drawLine every time an array is appended
y.share.drawing.observe(function (event) {
if (event.type === 'insert') {
event.values.forEach(drawLine)
} else {
// just remove all elements (thats what we do anyway)
svg.selectAll('path').remove()
}
})
// draw all existing content
for (var i = 0; i < drawing.length; i++) {
drawLine(drawing.get(i))
}
}
function dragend () {
sharedLine = null
window.clearTimeout(ignoreDrag)
ignoreDrag = null
}
// clear canvas on request
document.querySelector('#clearDrawingCanvas').onclick = function () {
drawing.delete(0, drawing.length)
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 33)
sharedLine.push([d3.mouse(this)])
}
}
function dragend () {
sharedLine = null
window.clearTimeout(ignoreDrag)
ignoreDrag = null
}
})

View File

@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
</head>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/d3/d3.min.js"></script>
<script src="./index.js"></script>
<style>
magic-drawing .drawingCanvas path {
fill: none;
stroke: blue;
stroke-width: 2px;
stroke-linejoin: round;
stroke-linecap: round;
}
magic-drawing .drawingCanvas {
width: 500px;
height: 500px;
cursor: default;
padding:1px;
border:1px solid #021a40;
}
magic-drawing .clearDrawingButton {
position: absolute;
top: 0;
left: 0;
}
magic-drawing {
position: relative;
display: block;
}
</style>
</head>
<body contenteditable="true">
</body>
</html>

View File

@@ -1,134 +0,0 @@
/* global Y, d3 */
const hooks = {
'magic-drawing': {
fillType: function (dom, type) {
initDrawingBindings(type, dom)
},
createDom: function (type) {
const dom = document.createElement('magic-drawing')
initDrawingBindings(type, dom)
return dom
}
}
}
window.onload = function () {
window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { hooks })
}
window.addMagicDrawing = function addMagicDrawing () {
let mt = document.createElement('magic-drawing')
mt.setAttribute('data-yjs-hook', 'magic-drawing')
document.body.append(mt)
}
var renderPath = d3.svg.line()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] })
.interpolate('basic')
function initDrawingBindings (type, dom) {
dom.contentEditable = 'false'
dom.setAttribute('data-yjs-hook', 'magic-drawing')
var drawing = type.get('drawing')
if (drawing === undefined) {
drawing = type.set('drawing', new Y.Array())
}
var canvas = dom.querySelector('.drawingCanvas')
if (canvas == null) {
canvas = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
canvas.setAttribute('class', 'drawingCanvas')
canvas.setAttribute('viewbox', '0 0 100 100')
dom.insertBefore(canvas, null)
}
var clearDrawingButton = dom.querySelector('.clearDrawingButton')
if (clearDrawingButton == null) {
clearDrawingButton = document.createElement('button')
clearDrawingButton.setAttribute('type', 'button')
clearDrawingButton.setAttribute('class', 'clearDrawingButton')
clearDrawingButton.innerText = 'Clear Drawing'
dom.insertBefore(clearDrawingButton, null)
}
var svg = d3.select(canvas)
.call(d3.behavior.drag()
.on('dragstart', dragstart)
.on('drag', drag)
.on('dragend', dragend))
// create line from a shared array object and update the line when the array changes
function drawLine (yarray, svg) {
var line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
yarray.observe(function (event) {
line.remove()
line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
})
}
// call drawLine every time an array is appended
drawing.observe(function (event) {
event.removedElements.forEach(function () {
// if one is deleted, all will be deleted!!
svg.selectAll('path').remove()
})
event.addedElements.forEach(function (path) {
drawLine(path, svg)
})
})
// draw all existing content
for (var i = 0; i < drawing.length; i++) {
drawLine(drawing.get(i), svg)
}
// clear canvas on request
clearDrawingButton.onclick = function () {
drawing.delete(0, drawing.length)
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 10)
sharedLine.push([d3.mouse(this)])
}
}
function dragend () {
sharedLine = null
window.clearTimeout(ignoreDrag)
ignoreDrag = null
}
}
let y = new Y('html-editor-drawing-hook-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.yXml = y
window.yXmlType = y.define('xml', Y.XmlFragment)
window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
captureTimeout: 500
})
document.onkeydown = function interceptUndoRedo (e) {
if (e.keyCode === 90 && e.metaKey) {
if (!e.shiftKey) {
window.undoManager.undo()
} else {
window.undoManager.redo()
}
e.preventDefault()
}
}

View File

@@ -1,645 +0,0 @@
/*
CanvasJS HTML5 & JavaScript Charts - v1.9.10 GA - https://canvasjs.com/
Copyright 2017 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-canvasjs/
*/
(function(){function T(a,c){a.prototype=Sa(c.prototype);a.prototype.constructor=a;a.base=c.prototype}function Sa(a){function c(){}c.prototype=a;return new c}function Ia(a,c,b){"millisecond"===b?a.setMilliseconds(a.getMilliseconds()+1*c):"second"===b?a.setSeconds(a.getSeconds()+1*c):"minute"===b?a.setMinutes(a.getMinutes()+1*c):"hour"===b?a.setHours(a.getHours()+1*c):"day"===b?a.setDate(a.getDate()+1*c):"week"===b?a.setDate(a.getDate()+7*c):"month"===b?a.setMonth(a.getMonth()+1*c):"year"===b&&a.setFullYear(a.getFullYear()+
1*c);return a}function P(a,c){var b=!1;0>a&&(b=!0,a*=-1);a=""+a;for(c=c?c:1;a.length<c;)a="0"+a;return b?"-"+a:a}function ma(a){if(!a)return a;a=a.replace(/^\s\s*/,"");for(var c=/\s/,b=a.length;c.test(a.charAt(--b)););return a.slice(0,b+1)}function Ja(a){a.roundRect=function(a,b,d,f,g,l,k,h){k&&(this.fillStyle=k);h&&(this.strokeStyle=h);"undefined"===typeof g&&(g=5);this.lineWidth=l;this.beginPath();this.moveTo(a+g,b);this.lineTo(a+d-g,b);this.quadraticCurveTo(a+d,b,a+d,b+g);this.lineTo(a+d,b+f-g);
this.quadraticCurveTo(a+d,b+f,a+d-g,b+f);this.lineTo(a+g,b+f);this.quadraticCurveTo(a,b+f,a,b+f-g);this.lineTo(a,b+g);this.quadraticCurveTo(a,b,a+g,b);this.closePath();k&&this.fill();h&&0<l&&this.stroke()}}function Aa(a,c){return a-c}function Ka(a,c){return a.x-c.x}function D(a){var c=((a&16711680)>>16).toString(16),b=((a&65280)>>8).toString(16);a=((a&255)>>0).toString(16);c=2>c.length?"0"+c:c;b=2>b.length?"0"+b:b;a=2>a.length?"0"+a:a;return"#"+c+b+a}function Ta(a,c){var b=this.length>>>0,d=Number(c)||
0,d=0>d?Math.ceil(d):Math.floor(d);for(0>d&&(d+=b);d<b;d++)if(d in this&&this[d]===a)return d;return-1}function y(a){return null===a||"undefined"===typeof a}function sa(a){a.indexOf||(a.indexOf=Ta);return a}function Ua(a){if(I.fSDec)a[I.fSDec("`eeDwdouMhrudods")](I.fSDec("e`u`@ohl`uhnoHuds`uhnoDoe"),function(){I._fTWm&&I._fTWm(a)})}function La(a,c,b){b=b||"normal";var d=a+"_"+c+"_"+b,f=Ma[d];if(isNaN(f)){try{a="position:absolute; left:0px; top:-20000px; padding:0px;margin:0px;border:none;white-space:pre;line-height:normal;font-family:"+
a+"; font-size:"+c+"px; font-weight:"+b+";";if(!da){var g=document.body;da=document.createElement("span");da.innerHTML="";var l=document.createTextNode("Mpgyi");da.appendChild(l);g.appendChild(da)}da.style.display="";da.setAttribute("style",a);f=Math.round(da.offsetHeight);da.style.display="none"}catch(k){f=Math.ceil(1.1*c)}f=Math.max(f,c);Ma[d]=f}return f}function F(a,c){var b=[];if(b={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]}[a||"solid"])for(var d=0;d<b.length;d++)b[d]*=c;else b=[];return b}function J(a,c,b,d){return a.addEventListener?(a.addEventListener(c,b,d||!1),b):a.attachEvent?(d=function(d){d=d||window.event;d.preventDefault=d.preventDefault||function(){d.returnValue=!1};d.stopPropagation=d.stopPropagation||function(){d.cancelBubble=!0};b.call(a,d)},a.attachEvent("on"+c,d),d):!1}function Na(a,c,b){a*=Q;c*=Q;a=b.getImageData(a,
c,2,2).data;c=!0;for(b=0;4>b;b++)if(a[b]!==a[b+4]|a[b]!==a[b+8]|a[b]!==a[b+12]){c=!1;break}return c?a[0]<<16|a[1]<<8|a[2]:0}function K(a,c,b){return a in c?c[a]:b[a]}function ta(a,c,b){if(t&&Oa){var d=a.getContext("2d");ua=d.webkitBackingStorePixelRatio||d.mozBackingStorePixelRatio||d.msBackingStorePixelRatio||d.oBackingStorePixelRatio||d.backingStorePixelRatio||1;Q=Ba/ua;a.width=c*Q;a.height=b*Q;Ba!==ua&&(a.style.width=c+"px",a.style.height=b+"px",d.scale(Q,Q))}else a.width=c,a.height=b}function Va(a){if(!Pa){var c=
!1,b=!1;"undefined"===typeof ea.Chart.creditHref?(a.creditHref=S("iuuqr;..b`ow`rkr/bnl."),a.creditText=S("B`ow`rKR/bnl")):(c=a.updateOption("creditText"),b=a.updateOption("creditHref"));if(a.creditHref&&a.creditText){a._creditLink||(a._creditLink=document.createElement("a"),a._creditLink.setAttribute("class","canvasjs-chart-credit"),a._creditLink.setAttribute("style","outline:none;margin:0px;position:absolute;right:2px;top:"+(a.height-14)+"px;color:dimgrey;text-decoration:none;font-size:11px;font-family: Calibri, Lucida Grande, Lucida Sans Unicode, Arial, sans-serif"),
a._creditLink.setAttribute("tabIndex",-1),a._creditLink.setAttribute("target","_blank"));if(0===a.renderCount||c||b)a._creditLink.setAttribute("href",a.creditHref),a._creditLink.innerHTML=a.creditText;a._creditLink&&a.creditHref&&a.creditText?(a._creditLink.parentElement||a._canvasJSContainer.appendChild(a._creditLink),a._creditLink.style.top=a.height-14+"px"):a._creditLink.parentElement&&a._canvasJSContainer.removeChild(a._creditLink)}}}function ia(a,c){var b=document.createElement("canvas");b.setAttribute("class",
"canvasjs-chart-canvas");ta(b,a,c);t||"undefined"===typeof G_vmlCanvasManager||G_vmlCanvasManager.initElement(b);return b}function Ca(a,c,b){if(a&&c&&b){b=b+"."+c;var d="image/"+c;a=a.toDataURL(d);var f=!1,g=document.createElement("a");g.download=b;g.href=a;g.target="_blank";if("undefined"!==typeof Blob&&new Blob){for(var l=a.replace(/^data:[a-z\/]*;base64,/,""),l=atob(l),k=new ArrayBuffer(l.length),k=new Uint8Array(k),h=0;h<l.length;h++)k[h]=l.charCodeAt(h);c=new Blob([k.buffer],{type:"image/"+c});
try{window.navigator.msSaveBlob(c,b),f=!0}catch(m){g.dataset.downloadurl=[d,g.download,g.href].join(":"),g.href=window.URL.createObjectURL(c)}}if(!f)try{event=document.createEvent("MouseEvents"),event.initMouseEvent("click",!0,!1,window,0,0,0,0,0,!1,!1,!1,!1,0,null),g.dispatchEvent?g.dispatchEvent(event):g.fireEvent&&g.fireEvent("onclick")}catch(p){c=window.open(),c.document.write("<img src='"+a+"'></img><div>Please right click on the image and save it to your device</div>"),c.document.close()}}}
function $(a,c,b){c.getAttribute("state")!==b&&(c.setAttribute("state",b),c.setAttribute("type","button"),c.style.position="relative",c.style.margin="0px 0px 0px 0px",c.style.padding="3px 4px 0px 4px",c.style.cssFloat="left",c.setAttribute("title",a._cultureInfo[b+"Text"]),c.innerHTML="<img style='height:16px;' src='"+Wa[b].image+"' alt='"+a._cultureInfo[b+"Text"]+"' />")}function va(){for(var a=null,c=0;c<arguments.length;c++)a=arguments[c],a.style&&(a.style.display="inline")}function aa(){for(var a=
null,c=0;c<arguments.length;c++)(a=arguments[c])&&a.style&&(a.style.display="none")}function L(a,c,b,d){this._defaultsKey=a;this.parent=d;this._eventListeners=[];d={};b&&(ka[b]&&ka[b][a])&&(d=ka[b][a]);this.options=c?c:{_isPlaceholder:!0};this.setOptions(this.options,d)}function A(a,c){c=c||{};A.base.constructor.call(this,"Chart",c,c.theme?c.theme:"theme1");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.panEnabled=this.disableToolTip=this.animatedRender=!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 d=0,f=0,d=this.options.width?this.width:0<this.container.clientWidth?this.container.clientWidth:
this.width,f=this.options.height?this.height:0<this.container.clientHeight?this.container.clientHeight:this.height;this.width=d;this.height=f;this.x1=this.y1=0;this.x2=this.width;this.y2=this.height;this._selectedColorSet="undefined"!==typeof ja[this.colorSet]?ja[this.colorSet]:ja.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";t||(this._canvasJSContainer.style.height="0px");this.container.appendChild(this._canvasJSContainer);this.canvas=ia(d,f);this.canvas.style.position="absolute";this.canvas.getContext&&(this._canvasJSContainer.appendChild(this.canvas),this.ctx=this.canvas.getContext("2d"),this.ctx.textBaseline="top",Ja(this.ctx),t?this.plotArea.ctx=this.ctx:(this.plotArea.canvas=ia(d,f),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=ia(d,f),this.overlaidCanvas.style.position="absolute",this._canvasJSContainer.appendChild(this.overlaidCanvas),this.overlaidCanvasCtx=this.overlaidCanvas.getContext("2d"),this.overlaidCanvasCtx.textBaseline="top",this._eventManager=new na(this),this.windowResizeHandler=J(window,"resize",function(){b._updateSize()&&b.render()}),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},J(this.overlaidCanvas,"click",function(a){b._mouseEventHandler(a)}),J(this.overlaidCanvas,"mousemove",function(a){b._mouseEventHandler(a)}),J(this.overlaidCanvas,"mouseup",function(a){b._mouseEventHandler(a)}),J(this.overlaidCanvas,"mousedown",function(a){b._mouseEventHandler(a);
aa(b._dropdownMenu)}),J(this.overlaidCanvas,"mouseout",function(a){b._mouseEventHandler(a)}),J(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerDown":"touchstart",function(a){b._touchEventHandler(a)}),J(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerMove":"touchmove",function(a){b._touchEventHandler(a)}),J(this.overlaidCanvas,window.navigator.msPointerEnabled?"MSPointerUp":"touchend",function(a){b._touchEventHandler(a)}),J(this.overlaidCanvas,window.navigator.msPointerEnabled?
"MSPointerCancel":"touchcancel",function(a){b._touchEventHandler(a)}),this.toolTip=new R(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 wa(a,c){for(var b=[],d,f=0;f<a.length;f++)if(0==f)b.push(a[0]);else{var g,l,k;k=f-1;g=0===k?0:k-1;l=k===a.length-1?k:k+
1;d=Math.abs((a[l].x-a[g].x)/(0===a[l].x-a[k].x?0.01:a[l].x-a[k].x))*(c-1)/2+1;var h=(a[l].x-a[g].x)/d;d=(a[l].y-a[g].y)/d;b[b.length]=a[k].x>a[g].x&&0<h||a[k].x<a[g].x&&0>h?{x:a[k].x+h/3,y:a[k].y+d/3}:{x:a[k].x,y:a[k].y+d/9};k=f;g=0===k?0:k-1;l=k===a.length-1?k:k+1;d=Math.abs((a[l].x-a[g].x)/(0===a[k].x-a[g].x?0.01:a[k].x-a[g].x))*(c-1)/2+1;h=(a[l].x-a[g].x)/d;d=(a[l].y-a[g].y)/d;b[b.length]=a[k].x>a[g].x&&0<h||a[k].x<a[g].x&&0>h?{x:a[k].x-h/3,y:a[k].y-d/3}:{x:a[k].x,y:a[k].y-d/9};b[b.length]=a[f]}return b}
function Qa(a,c){if(null===a||"undefined"===typeof a)return c;var b=parseFloat(a.toString())*(0<=a.toString().indexOf("%")?c/100:1);return!isNaN(b)&&b<=c&&0<=b?b:c}function la(a,c,b,d,f){"undefined"===typeof f&&(f=0);this._padding=f;this._x1=a;this._y1=c;this._x2=b;this._y2=d;this._rightOccupied=this._leftOccupied=this._bottomOccupied=this._topOccupied=this._padding}function V(a,c){V.base.constructor.call(this,"TextBlock",c);this.ctx=a;this._isDirty=!0;this._wrappedText=null}function oa(a,c){oa.base.constructor.call(this,
"Title",c,a.theme,a);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.optionsName="title";if(y(this.options.margin)&&a.options.subtitles)for(var b=a.options.subtitles,d=0;d<b.length;d++)if((y(b[d].horizontalAlign)&&"center"===this.horizontalAlign||b[d].horizontalAlign===this.horizontalAlign)&&(y(b[d].verticalAlign)&&"top"===this.verticalAlign||b[d].verticalAlign===this.verticalAlign)&&!b[d].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 xa(a,c){xa.base.constructor.call(this,"Subtitle",c,a.theme,a);this.chart=a;this.canvas=a.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 ya(a,
c){ya.base.constructor.call(this,"Legend",c,a.theme,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=La(this.fontFamily,this.fontSize,this.fontWeight);this.horizontalSpacing=
this.fontSize}function Da(a,c){Da.base.constructor.call(this,c);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx}function ca(a,c,b,d){ca.base.constructor.call(this,"DataSeries",c,a.theme,a);this.chart=a;this.canvas=a.canvas;this._ctx=a.canvas.ctx;this.index=b;this.noDataPointsInPlotArea=0;this.id=d;this.chart._eventManager.objectMap[d]={id:d,objectType:"dataSeries",dataSeriesIndex:b};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 B(a,c,b,d,f){B.base.constructor.call(this,"Axis",c,a.theme,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"===b?("left"===d||"bottom"===d?(this.optionsName="axisX",y(this.chart.sessionVariables.axisX[f])&&(this.chart.sessionVariables.axisX[f]={}),this.sessionVariables=this.chart.sessionVariables.axisX[f]):(this.optionsName="axisX2",y(this.chart.sessionVariables.axisX2[f])&&(this.chart.sessionVariables.axisX2[f]={}),this.sessionVariables=this.chart.sessionVariables.axisX2[f]),
this.options.interval||(this.intervalType=null),"theme2"===this.chart.theme&&y(this.options.lineThickness)&&(this.lineThickness=2)):"left"===d||"top"===d?(this.optionsName="axisY",y(this.chart.sessionVariables.axisY[f])&&(this.chart.sessionVariables.axisY[f]={}),this.sessionVariables=this.chart.sessionVariables.axisY[f]):(this.optionsName="axisY2",y(this.chart.sessionVariables.axisY2[f])&&(this.chart.sessionVariables.axisY2[f]={}),this.sessionVariables=this.chart.sessionVariables.axisY2[f]);"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=b;"axisX"!==b||c&&"undefined"!==typeof c.gridThickness||(this.gridThickness=0);this._position=d;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);if(this.options.stripLines&&0<this.options.stripLines.length)for(this.stripLines=[],c=0;c<this.options.stripLines.length;c++)this.stripLines.push(new pa(this.chart,this.options.stripLines[c],a.theme,++this.chart._eventManager.lastObjectId,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 pa(a,c,b,d,f){pa.base.constructor.call(this,"StripLine",c,b,f);this.id=d;this.chart=a;this.ctx=this.chart.ctx;this.label=
this.label;this.axis=f;this.optionsName="stripLines";this.isOptionsInArray=!0;this._thicknessType="pixel";null!==this.startValue&&null!==this.endValue&&(this.value=f.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.thickness=f.logarithmic?Math.log(this.endValue/
this.startValue)/Math.log(f.logarithmBase):Math.max(this.endValue-this.startValue),this._thicknessType="value")}function R(a,c){R.base.constructor.call(this,"ToolTip",c,a.theme,a);this.chart=a;this.canvas=a.canvas;this.ctx=this.chart.ctx;this.currentDataPointIndex=this.currentSeriesIndex=-1;this._timerId=0;this._prevY=this._prevX=NaN;this.optionsName="toolTip";this._initialize()}function na(a){this.chart=a;this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=[];this.previousDataPointEventObject=
null;this.ghostCanvas=ia(this.chart.width,this.chart.height);this.ghostCtx=this.ghostCanvas.getContext("2d");this.mouseoveredObjectMaps=[]}function qa(a){var c;a&&ra[a]&&(c=ra[a]);qa.base.constructor.call(this,"CultureInfo",c)}function Ea(a){this.chart=a;this.ctx=this.chart.plotArea.ctx;this.animations=[];this.animationRequestId=null}var I={},t=!!document.createElement("canvas").getContext,ea={Chart:{width:500,height:400,zoomEnabled:!1,zoomType:"x",backgroundColor:"white",theme:"theme1",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",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:t?"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"}},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:t?"transparent":null,borderColor:t?"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:t?"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:t?"transparent":
null,titleBorderColor:t?"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:t?"transparent":null,labelBorderColor:t?"transparent":null,labelBorderThickness:0,labelCornerRadius:0,prefix:"",suffix:"",includeZero:!0,tickLength:5,tickColor:"black",tickThickness:1,lineColor:"black",
lineThickness:1,lineDashType:"solid",gridColor:"A0A0A0",gridThickness:0,gridDashType:"solid",interlacedColor:t?"transparent":null,valueFormatString:null,margin:2,stripLines:[],publicProperties:{options:"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:t?"transparent":null,labelBorderColor:t?
"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"}},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",fillOpacity:null,startAngle:0,radius:null,innerRadius:null,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:t?"transparent":null,legendMarkerBorderThickness:0,markerType:"circle",markerColor:null,markerSize:null,markerBorderColor:t?"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(" ")}},ra={en:{}},ja={colorSet1:"#369EAD #C24642 #7F6084 #86B402 #A2D1CF #C8B631 #6DBCEB #52514E #4F81BC #A064A1 #F79647".split(" "),
colorSet2:"#4F81BC #C0504E #9BBB58 #23BFAA #8064A1 #4AACC5 #F79647 #33558B".split(" "),colorSet3:"#8CA1BC #36845C #017E82 #8CB9D0 #708C98 #94838D #F08891 #0366A7 #008276 #EE7757 #E5BA3A #F2990B #03557B #782970".split(" ")},ka={theme1:{Chart:{colorSet:"colorSet1"},Title:{fontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",fontSize:33,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"top",margin:5},Subtitle:{fontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":
"calibri",fontSize:16,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"top",margin:5},Axis:{titleFontSize:26,titleFontColor:"#666666",titleFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontSize:18,labelFontColor:"grey",tickColor:"#BBBBBB",tickThickness:2,gridThickness:2,gridColor:"#BBBBBB",lineThickness:2,lineColor:"#BBBBBB"},Legend:{verticalAlign:"bottom",horizontalAlign:"center",
fontFamily:t?"monospace, sans-serif,arial black":"calibri"},DataSeries:{indexLabelFontColor:"grey",indexLabelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",indexLabelFontSize:18,indexLabelLineThickness:1}},theme2:{Chart:{colorSet:"colorSet2"},Title:{fontFamily:"impact, charcoal, arial black, sans-serif",fontSize:32,fontColor:"#333333",verticalAlign:"top",margin:5},Subtitle:{fontFamily:"impact, charcoal, arial black, sans-serif",fontSize:14,fontColor:"#333333",verticalAlign:"top",
margin:5},Axis:{titleFontSize:22,titleFontColor:"rgb(98,98,98)",titleFontFamily:t?"monospace, sans-serif,arial black":"arial",titleFontWeight:"bold",labelFontFamily:t?"monospace, Courier New, Courier":"arial",labelFontSize:16,labelFontColor:"grey",labelFontWeight:"bold",tickColor:"grey",tickThickness:2,gridThickness:2,gridColor:"grey",lineColor:"grey",lineThickness:0},Legend:{verticalAlign:"bottom",horizontalAlign:"center",fontFamily:t?"monospace, sans-serif,arial black":"arial"},DataSeries:{indexLabelFontColor:"grey",
indexLabelFontFamily:t?"Courier New, Courier, monospace":"arial",indexLabelFontWeight:"bold",indexLabelFontSize:18,indexLabelLineThickness:1}},theme3:{Chart:{colorSet:"colorSet1"},Title:{fontFamily:t?"Candara, Optima, Trebuchet MS, Helvetica Neue, Helvetica, Trebuchet MS, serif":"calibri",fontSize:32,fontColor:"#3A3A3A",fontWeight:"bold",verticalAlign:"top",margin:5},Subtitle:{fontFamily:t?"Candara, Optima, Trebuchet MS, Helvetica Neue, Helvetica, Trebuchet MS, serif":"calibri",fontSize:16,fontColor:"#3A3A3A",
fontWeight:"bold",verticalAlign:"top",margin:5},Axis:{titleFontSize:22,titleFontColor:"rgb(98,98,98)",titleFontFamily:t?"Verdana, Geneva, Calibri, sans-serif":"calibri",labelFontFamily:t?"Calibri, Optima, Candara, Verdana, Geneva, sans-serif":"calibri",labelFontSize:18,labelFontColor:"grey",tickColor:"grey",tickThickness:2,gridThickness:2,gridColor:"grey",lineThickness:2,lineColor:"grey"},Legend:{verticalAlign:"bottom",horizontalAlign:"center",fontFamily:t?"monospace, sans-serif,arial black":"calibri"},
DataSeries:{bevelEnabled:!0,indexLabelFontColor:"grey",indexLabelFontFamily:t?"Candara, Optima, Calibri, Verdana, Geneva, sans-serif":"calibri",indexLabelFontSize:18,indexLabelLineColor:"lightgrey",indexLabelLineThickness:2}}},G={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(){I.fSDec=
function(a){for(var c="",b=0;b<a.length;b++)c+=String.fromCharCode(Math.ceil(a.length/57/5)^a.charCodeAt(b));return c};I.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 ea[I.fSDec("Bi`su")][I.fSDec("bsdehuIsdg")];I.pro={sCH:ea[I.fSDec("Bi`su")][I.fSDec("bsdehuIsdg")]};I._fTWm=function(a){if("undefined"===typeof I.pro.sCH&&!Pa)try{var c=a[I.fSDec(I.obj.cntx)];
c[I.fSDec(I.obj.txtBl)]=I.fSDec(I.obj.tp);c[I.fSDec(I.obj.fnt)]=11+I.fSDec(I.obj.fntStr);c[I.fSDec(I.obj.fSy)]=I.fSDec(I.obj.grClr);c[I.fSDec(I.obj.fTx)](I.fSDec(I.obj.trVs),2,a.height-11-2)}catch(b){}}})();var Ma={},da=null,Fa=function(){var a=/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,c="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),b="Sun Mon Tue Wed Thu Fri Sat".split(" "),d="January February March April May June July August September October November December".split(" "),
f="Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec".split(" "),g=/\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,l=/[^-+\dA-Z]/g;return function(k,h,m){var p=m?m.days:c,n=m?m.months:d,e=m?m.shortDays:b,q=m?m.shortMonths:f;m="";var r=!1;k=k&&k.getTime?k:k?new Date(k):new Date;if(isNaN(k))throw SyntaxError("invalid date");"UTC:"===h.slice(0,4)&&(h=h.slice(4),r=!0);m=r?"getUTC":"get";var s=k[m+"Date"](),x=k[m+"Day"](),
u=k[m+"Month"](),v=k[m+"FullYear"](),z=k[m+"Hours"](),w=k[m+"Minutes"](),t=k[m+"Seconds"](),y=k[m+"Milliseconds"](),A=r?0:k.getTimezoneOffset();return m=h.replace(a,function(a){switch(a){case "D":return s;case "DD":return P(s,2);case "DDD":return e[x];case "DDDD":return p[x];case "M":return u+1;case "MM":return P(u+1,2);case "MMM":return q[u];case "MMMM":return n[u];case "Y":return parseInt(String(v).slice(-2));case "YY":return P(String(v).slice(-2),2);case "YYY":return P(String(v).slice(-3),3);case "YYYY":return P(v,
4);case "h":return z%12||12;case "hh":return P(z%12||12,2);case "H":return z;case "HH":return P(z,2);case "m":return w;case "mm":return P(w,2);case "s":return t;case "ss":return P(t,2);case "f":return String(y).slice(0,1);case "ff":return P(String(y).slice(0,2),2);case "fff":return P(String(y).slice(0,3),3);case "t":return 12>z?"a":"p";case "tt":return 12>z?"am":"pm";case "T":return 12>z?"A":"P";case "TT":return 12>z?"AM":"PM";case "K":return r?"UTC":(String(k).match(g)||[""]).pop().replace(l,"");
case "z":return(0<A?"-":"+")+Math.floor(Math.abs(A)/60);case "zz":return(0<A?"-":"+")+P(Math.floor(Math.abs(A)/60),2);case "zzz":return(0<A?"-":"+")+P(Math.floor(Math.abs(A)/60),2)+P(Math.abs(A)%60,2);default:return a.slice(1,a.length-1)}})}}(),fa=function(a,c,b){if(null===a)return"";a=Number(a);var d=0>a?!0:!1;d&&(a*=-1);var f=b?b.decimalSeparator:".",g=b?b.digitGroupSeparator:",",l="";c=String(c);var l=1,k=b="",h=-1,m=[],p=[],n=0,e=0,q=0,r=!1,s=0,k=c.match(/"[^"]*"|'[^']*'|[eE][+-]*[0]+|[,]+[.]|\u2030|./g);
c=null;for(var x=0;k&&x<k.length;x++)if(c=k[x],"."===c&&0>h)h=x;else{if("%"===c)l*=100;else if("\u2030"===c){l*=1E3;continue}else if(","===c[0]&&"."===c[c.length-1]){l/=Math.pow(1E3,c.length-1);h=x+c.length-1;continue}else"E"!==c[0]&&"e"!==c[0]||"0"!==c[c.length-1]||(r=!0);0>h?(m.push(c),"#"===c||"0"===c?n++:","===c&&q++):(p.push(c),"#"!==c&&"0"!==c||e++)}r&&(c=Math.floor(a),k=-Math.floor(Math.log(a)/Math.LN10+1),s=0===a?0:0===c?-(n+k):String(c).length-n,l/=Math.pow(10,s));0>h&&(h=x);l=(a*l).toFixed(e);
c=l.split(".");l=(c[0]+"").split("");a=(c[1]+"").split("");l&&"0"===l[0]&&l.shift();for(r=k=x=e=h=0;0<m.length;)if(c=m.pop(),"#"===c||"0"===c)if(h++,h===n){var u=l,l=[];if("0"===c)for(c=n-e-(u?u.length:0);0<c;)u.unshift("0"),c--;for(;0<u.length;)b=u.pop()+b,r++,0===r%k&&(x===q&&0<u.length)&&(b=g+b)}else 0<l.length?(b=l.pop()+b,e++,r++):"0"===c&&(b="0"+b,e++,r++),0===r%k&&(x===q&&0<l.length)&&(b=g+b);else"E"!==c[0]&&"e"!==c[0]||"0"!==c[c.length-1]||!/[eE][+-]*[0]+/.test(c)?","===c?(x++,k=r,r=0,0<l.length&&
(b=g+b)):b=1<c.length&&('"'===c[0]&&'"'===c[c.length-1]||"'"===c[0]&&"'"===c[c.length-1])?c.slice(1,c.length-1)+b:c+b:(c=0>s?c.replace("+","").replace("-",""):c.replace("-",""),b+=c.replace(/[0]+/,function(a){return P(s,a.length)}));g="";for(m=!1;0<p.length;)c=p.shift(),"#"===c||"0"===c?0<a.length&&0!==Number(a.join(""))?(g+=a.shift(),m=!0):"0"===c&&(g+="0",m=!0):1<c.length&&('"'===c[0]&&'"'===c[c.length-1]||"'"===c[0]&&"'"===c[c.length-1])?g+=c.slice(1,c.length-1):"E"!==c[0]&&"e"!==c[0]||"0"!==c[c.length-
1]||!/[eE][+-]*[0]+/.test(c)?g+=c:(c=0>s?c.replace("+","").replace("-",""):c.replace("-",""),g+=c.replace(/[0]+/,function(a){return P(s,a.length)}));b+=(m?f:"")+g;return d?"-"+b:b},za=function(a){var c=0,b=0;a=a||window.event;a.offsetX||0===a.offsetX?(c=a.offsetX,b=a.offsetY):a.layerX||0==a.layerX?(c=a.layerX,b=a.layerY):(c=a.pageX-a.target.offsetLeft,b=a.pageY-a.target.offsetTop);return{x:c,y:b}},Oa=!0,Ba=window.devicePixelRatio||1,ua=1,Q=Oa?Ba/ua:1,S=function(a){for(var c="",b=0;b<a.length;b++)c+=
String.fromCharCode(Math.ceil(a.length/57/5)^a.charCodeAt(b));return c},Pa=window&&window[S("mnb`uhno")]&&window[S("mnb`uhno")].href&&window[S("mnb`uhno")].href.indexOf&&(-1!==window[S("mnb`uhno")].href.indexOf(S("b`ow`rkr/bnl"))||-1!==window[S("mnb`uhno")].href.indexOf(S("gdonqhy/bnl"))||-1!==window[S("mnb`uhno")].href.indexOf(S("gheemd"))),Wa={reset:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAcCAYAAAAAwr0iAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAKRSURBVEiJrdY/iF1FFMfxzwnZrGISUSR/JLGIhoh/QiRNBLWxMLIWEkwbgiAoFgoW2mhlY6dgpY2IlRBRxBSKhSAKIklWJRYuMZKAhiyopAiaTY7FvRtmZ+/ed9/zHRjezLw5v/O9d86cuZGZpmURAfdn5o9DfdZNLXpjz+LziPgyIl6MiG0jPTJzZBuyDrP4BVm0P/AKbljTb4ToY/gGewYA7KyCl+1b3DUYANvwbiHw0gCAGRzBOzjTAXEOu0cC4Ch+r5x/HrpdrcZmvIDFSucMtnYCYC++6HmNDw8FKDT34ETrf639/azOr5vwRk/g5fbeuABtgC04XWk9VQLciMP4EH/3AFzErRNC7MXlQmsesSoHsGPE23hmEoBW+61K66HMXFmIMvN8myilXS36R01ub+KfYvw43ZXwYDX+AHP4BAci4pFJomfmr/ihmNofESsBImJGk7mlncrM45n5JPbhz0kAWpsv+juxaX21YIPmVJS2uNzJMS6ZNexC0d+I7fUWXLFyz2kSZlpWPvASlmqAf/FXNXf3FAF2F/1LuFifAlionB6dRuSI2IwHi6lzmXmp6xR8XY0fiIh7psAwh+3FuDkRHQVjl+a8lkXjo0kLUKH7XaV5oO86PmZ1FTzyP4K/XGl9v/zwfbW7BriiuETGCP5ch9bc9f97HF/vcFzCa5gdEPgWq+t/4v0V63oE1uF4h0DiFJ7HnSWMppDdh1dxtsPvJ2wcBNAKbsJXa0Ck5opdaBPsRNu/usba09i1KsaAVzmLt3sghrRjuK1Tf4xkegInxwy8gKf7dKMVH2QRsV5zXR/Cftyu+aKaKbbkQrsdH+PTzLzcqzkOQAVzM+7FHdiqqe2/YT4zF/t8S/sPmawyvC974vcAAAAASUVORK5CYII="},
pan:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAJVSURBVFiFvZe7a1RBGMV/x2hWI4JpfKCIiSBKOoOCkID/wP4BFqIIFkE02ChIiC8QDKlSiI3YqRBsBVGwUNAUdiIEUgjiAzQIIsuKJsfizsXr5t7d+8jmwLDfzHz3nLOzc7+ZxTZlGyDgZiWOCuJ9wH2gCUyuqQFgF/AGcKJNrYkBYBj40CIet+muGQi/96kM4WS7C/Tm5VUg7whJg8BkEGkCR4BDYfodsADUgP6wErO5iCtswsuJb32hdbXy8qzL5TIdmzJinHdZoZIBZcSFkGlAKs1Z3YCketZcBtouuaQNkrblMiBpBrhme7mAgU4wMCvpcFsDkq4C54DFVRTH9h+i6vlE0r5UA5ImgCuh28jB28iIs7BIVCOeStoZD64P4uPAjUTygKSx2FsK2TIwkugfk9Qkfd/E+yMWHQCeSRqx/R3gOp3LazfaS2C4B5gHDgD7U9x3E3uAH7KNpC3AHHAwTL4FHgM9GQ8vAaPA0dB/Abxqk2/gBLA9MXba9r1k/d4LfA3JtwueBeM58ucS+edXnAW23wP10N3advEi9CXizTnyN4bPS7Zn4sH/dq3t18AY4e1YLYSy3g/csj2VnFshZPuOpOeSKHCodUINuGj7YetE6je1PV9QoNPJ9StNHKodx7nRbiWrGHBGXAi5DUiqtQwtpcWK0Jubt8CltA5MEV1IfwO7+VffPwGfia5m34CT4bXujIIX0Qna1/cGMNqV/wUJE2czxD8CQ4X5Sl7Jz7SILwCDpbjKPBRMHAd+EtX4HWV5Spdc2w8kDQGPbH8py/MXMygM69/FKz4AAAAASUVORK5CYII="},
zoom:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAMqSURBVFiFvdfbj91TFMDxz57U6GUEMS1aYzyMtCSSDhWjCZMInpAI3khE/QHtgzdRkXgSCS8SES9epKLi0oRKNETjRahREq2KS1stdRujtDPtbA97n5zdn9+5zJxTK9k5v3POXmt991p7r71+IcaoGwkhTOIebMRqzOBTvIG3Y4zTXRmqSoyx5cAKbMJOHMFJnMZ8/jyFaXyMR7G6nb1aH22cP4BvcBxziG3GKfyTIR9D6BYg1KUghPBCDveFlb/24Av8iuUYw41YVsz5G7uxKcZ4aMEpwGt5NY3V/YbHsQ6rcAHOw/kYxigewr5CZw4fYGxBKcCLOFEYehXrMdRhr5yLETxVScsOLOkKAPfn1TYMPIvLFrShUlS2FDZm8XRHACzFAWl3R2xbqPMCYhmeLCAOYEMngAczbcTvuHYxzguIy/FesR9e6gSwU/OoPYHBHgHgviIKX2Flq7k34KhmcVnbi/PC8JX4MgMcxb118wZwdz5aISscqx7VRcox7MrPQ7i+btIAJrAkf9+bI9EPmZY2IAxiTSuAldLq4Y9+AcSUh78KP0tbAcwU35cXMD1JCIFUoGiehlqAz6TNB1f1C0DK+0h+nsNPrQC2a4bqGmlD9kOGcWt+Po6pVgDvSxfJaSkFd4UQBvoAsBYbCoB3a2flM7slA0R8iyt6rAFDeDPbm8eOTpVwGD9qVq7nLbIaZnmksPU1JtsCZMXNmpdRxFasWITzh6Xj3LCzra1OxcD2QjHiGVzdpfORnMqZio2PcF23ABdJF1Np4BPptlyPi6WzPYBzpJZtHe7A6xW9cnyP8TqA//SEIYRL8Bxul7rihvwgtVn78WcGGZXa9HGd5TDujDHuOePXNiHdKjWgZX/YbsxLx/ktqbjVzTlcjUSnvI5JrdlUVp6WesZZ6R1hRrpq9+EVTGS9jTjYAuKIouGpbcurEkIYxC051KNSamazsc+xK8b4S0VnEi/j0hqTP+M27O258egQwZuzs7pI7Mf4WQXIEDc5s9sux+5+1Py2EmP8UOq6GvWhIScxfdYjUERiAt9Jd84J6a16zf8JEKT3yCm8g1UxRv8CC4pyRhzR1uUAAAAASUVORK5CYII="},
menu:{image:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgCAYAAAAbifjMAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAK6wAACusBgosNWgAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAAWdEVYdENyZWF0aW9uIFRpbWUAMDcvMTUvMTTPsvU0AAAAP0lEQVRIie2SMQoAIBDDUvH/X667g8sJJ9KOhYYOkW0qGaU1MPdC0vGSbV19EACo3YMPAFH5BUBUjsqfAPpVXtNgGDfxEDCtAAAAAElFTkSuQmCC"}};L.prototype.setOptions=function(a,c){if(ea[this._defaultsKey]){var b=ea[this._defaultsKey],d;for(d in b)"publicProperties"!==d&&b.hasOwnProperty(d)&&(this[d]=
a&&d in a?a[d]:c&&d in c?c[d]:b[d])}};L.prototype.get=function(a){var c=ea[this._defaultsKey];if("options"===a)return this.options&&this.options._isPlaceholder?null:this.options;if(c.hasOwnProperty(a)||c.publicProperties&&c.publicProperties.hasOwnProperty(a))return this[a];window.console&&window.console.log('Property "'+a+"\" doesn't exist. Please check for typo.")};L.prototype.set=function(a,c,b){b="undefined"===typeof b?!0:b;var d=ea[this._defaultsKey];if("options"===a)this.createUserOptions(c);
else if(d.hasOwnProperty(a)||d.publicProperties&&d.publicProperties.hasOwnProperty(a)&&"readWrite"===d.publicProperties[a])this.options._isPlaceholder&&this.createUserOptions(),this.options[a]=c;else{window.console&&(d.publicProperties&&d.publicProperties.hasOwnProperty(a)&&"readOnly"===d.publicProperties[a]?window.console.log('Property "'+a+'" is read-only.'):window.console.log('Property "'+a+"\" doesn't exist. Please check for typo."));return}b&&(this.chart||this).render()};L.prototype.addTo=function(a,
c,b,d){d="undefined"===typeof d?!0:d;var f=ea[this._defaultsKey];f.hasOwnProperty(a)||f.publicProperties&&f.publicProperties.hasOwnProperty(a)&&"readWrite"===f.publicProperties[a]?(this.options._isPlaceholder&&this.createUserOptions(),"undefined"===typeof this.options[a]&&(this.options[a]=[]),a=this.options[a],b="undefined"===typeof b||null===b?a.length:b,a.splice(b,0,c),d&&(this.chart||this).render()):window.console&&(f.publicProperties&&f.publicProperties.hasOwnProperty(a)&&"readOnly"===f.publicProperties[a]?
window.console.log('Property "'+a+'" is read-only.'):window.console.log('Property "'+a+"\" doesn't exist. Please check for typo."))};L.prototype.createUserOptions=function(a){if("undefined"!==typeof a||this.options._isPlaceholder)if(this.parent.options._isPlaceholder&&this.parent.createUserOptions(),this.isOptionsInArray){this.parent.options[this.optionsName]||(this.parent.options[this.optionsName]=[]);var c=this.parent.options[this.optionsName],b=c.length;this.options._isPlaceholder||(sa(c),b=c.indexOf(this.options));
this.options="undefined"===typeof a?{}:a;c[b]=this.options}else this.options="undefined"===typeof a?{}:a,a=this.parent.options,this.optionsName?c=this.optionsName:(c=this._defaultsKey)&&0!==c.length?(b=c.charAt(0).toLowerCase(),1<c.length&&(b=b.concat(c.slice(1))),c=b):c=void 0,a[c]=this.options};L.prototype.remove=function(a){a="undefined"===typeof a?!0:a;if(this.isOptionsInArray){var c=this.parent.options[this.optionsName];sa(c);var b=c.indexOf(this.options);0<=b&&c.splice(b,1)}else delete this.parent.options[this.optionsName];
a&&(this.chart||this).render()};L.prototype.updateOption=function(a){var c=ea[this._defaultsKey],b=this.options.theme?this.options.theme:this.chart&&this.chart.options.theme?this.chart.options.theme:"theme1",d={},f=this[a];b&&(ka[b]&&ka[b][this._defaultsKey])&&(d=ka[b][this._defaultsKey]);a in c&&(f=a in this.options?this.options[a]:d&&a in d?d[a]:c[a]);if(f===this[a])return!1;this[a]=f;return!0};L.prototype.trackChanges=function(a){if(!this.sessionVariables)throw"Session Variable Store not set";
this.sessionVariables[a]=this.options[a]};L.prototype.isBeingTracked=function(a){this.options._oldOptions||(this.options._oldOptions={});return this.options._oldOptions[a]?!0:!1};L.prototype.hasOptionChanged=function(a){if(!this.sessionVariables)throw"Session Variable Store not set";return this.sessionVariables[a]!==this.options[a]};L.prototype.addEventListener=function(a,c,b){a&&c&&(this._eventListeners[a]=this._eventListeners[a]||[],this._eventListeners[a].push({context:b||this,eventHandler:c}))};
L.prototype.removeEventListener=function(a,c){if(a&&c&&this._eventListeners[a])for(var b=this._eventListeners[a],d=0;d<b.length;d++)if(b[d].eventHandler===c){b[d].splice(d,1);break}};L.prototype.removeAllEventListeners=function(){this._eventListeners=[]};L.prototype.dispatchEvent=function(a,c,b){if(a&&this._eventListeners[a]){c=c||{};for(var d=this._eventListeners[a],f=0;f<d.length;f++)d[f].eventHandler.call(d[f].context,c)}"function"===typeof this[a]&&this[a].call(b||this.chart,c)};T(A,L);A.prototype.destroy=
function(){var a=window,c=this.windowResizeHandler;a.removeEventListener?a.removeEventListener("resize",c):a.detachEvent&&a.detachEvent("onresize",c)};A.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 ja[this.colorSet]?ja[this.colorSet]:ja.colorSet1);this.updateOption("backgroundColor");this.backgroundColor||(this.backgroundColor="rgba(0,0,0,0)");this.updateOption("culture");this._cultureInfo=new qa(this.options.culture);this.updateOption("animationEnabled");this.animationEnabled=this.animationEnabled&&t;this.updateOption("animationDuration");this.updateOption("rangeChanging");this.updateOption("rangeChanged");this.updateOption("exportEnabled");this.updateOption("exportFileName");this.updateOption("zoomType");
this.options.zoomEnabled?(this._zoomButton||(aa(this._zoomButton=document.createElement("button")),$(this,this._zoomButton,"pan"),this._toolBar.appendChild(this._zoomButton),J(this._zoomButton,"click",function(){a.zoomEnabled?(a.zoomEnabled=!1,a.panEnabled=!0,$(a,a._zoomButton,"zoom")):(a.zoomEnabled=!0,a.panEnabled=!1,$(a,a._zoomButton,"pan"));a.render()})),this._resetButton||(aa(this._resetButton=document.createElement("button")),$(this,this._resetButton,"reset"),this._toolBar.appendChild(this._resetButton),
J(this._resetButton,"click",function(){a.toolTip.hide();a.zoomEnabled||a.panEnabled?(a.zoomEnabled=!0,a.panEnabled=!1,$(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 b=0;b<a.sessionVariables.axisX.length;b++)a.sessionVariables.axisX[b].newViewportMinimum=null,a.sessionVariables.axisX[b].newViewportMaximum=null;if(a.sessionVariables.axisX2)for(b=0;b<a.sessionVariables.axisX2.length;b++)a.sessionVariables.axisX2[b].newViewportMinimum=
null,a.sessionVariables.axisX2[b].newViewportMaximum=null;if(a.sessionVariables.axisY)for(b=0;b<a.sessionVariables.axisY.length;b++)a.sessionVariables.axisY[b].newViewportMinimum=null,a.sessionVariables.axisY[b].newViewportMaximum=null;if(a.sessionVariables.axisY2)for(b=0;b<a.sessionVariables.axisY2.length;b++)a.sessionVariables.axisY2[b].newViewportMinimum=null,a.sessionVariables.axisY2[b].newViewportMaximum=null;a.resetOverlayedCanvas();aa(a._zoomButton,a._resetButton);a._dispatchRangeEvent("rangeChanging",
"reset");a.render();a._dispatchRangeEvent("rangeChanged","reset")}),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),va(a._zoomButton,a._resetButton)):(this.zoomEnabled=!0,this.panEnabled=!1))):this.panEnabled=this.zoomEnabled=!1;this._menuButton?this.exportEnabled?va(this._menuButton):aa(this._menuButton):
this.exportEnabled&&t&&(this._menuButton=document.createElement("button"),$(this,this._menuButton,"menu"),this._toolBar.appendChild(this._menuButton),J(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())},!0));if(!this._dropdownMenu&&this.exportEnabled&&t){this._dropdownMenu=document.createElement("div");this._dropdownMenu.setAttribute("tabindex",
-1);this._dropdownMenu.style.cssText="position: absolute; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer;right: 1px;top: 25px;min-width: 120px;outline: 0;border: 1px solid silver;font-size: 14px;font-family: Calibri, Verdana, sans-serif;padding: 5px 0px 5px 0px;text-align: left;background-color: #fff;line-height: 20px;box-shadow: 2px 2px 10px #888888;";a._dropdownMenu.style.display="none";this._toolBar.appendChild(this._dropdownMenu);J(this._dropdownMenu,
"blur",function(){aa(a._dropdownMenu);a._dropDownCloseTime=new Date},!0);var c=document.createElement("div");c.style.cssText="padding: 2px 15px 2px 10px";c.innerHTML=this._cultureInfo.printText;this._dropdownMenu.appendChild(c);J(c,"mouseover",function(){this.style.backgroundColor="#EEEEEE"},!0);J(c,"mouseout",function(){this.style.backgroundColor="transparent"},!0);J(c,"click",function(){a.print();aa(a._dropdownMenu)},!0);c=document.createElement("div");c.style.cssText="padding: 2px 15px 2px 10px";
c.innerHTML=this._cultureInfo.saveJPGText;this._dropdownMenu.appendChild(c);J(c,"mouseover",function(){this.style.backgroundColor="#EEEEEE"},!0);J(c,"mouseout",function(){this.style.backgroundColor="transparent"},!0);J(c,"click",function(){Ca(a.canvas,"jpeg",a.exportFileName);aa(a._dropdownMenu)},!0);c=document.createElement("div");c.style.cssText="padding: 2px 15px 2px 10px";c.innerHTML=this._cultureInfo.savePNGText;this._dropdownMenu.appendChild(c);J(c,"mouseover",function(){this.style.backgroundColor=
"#EEEEEE"},!0);J(c,"mouseout",function(){this.style.backgroundColor="transparent"},!0);J(c,"click",function(){Ca(a.canvas,"png",a.exportFileName);aa(a._dropdownMenu)},!0)}"none"!==this._toolBar.style.display&&this._zoomButton&&(this.panEnabled?$(a,a._zoomButton,"zoom"):$(a,a._zoomButton,"pan"),a._resetButton.getAttribute("state")!==a._cultureInfo.resetText&&$(a,a._resetButton,"reset"));this.options.toolTip&&this.toolTip.options!==this.options.toolTip&&(this.toolTip.options=this.options.toolTip);for(var b in this.toolTip.options)this.toolTip.options.hasOwnProperty(b)&&
this.toolTip.updateOption(b)};A.prototype._updateSize=function(){var a=0,c=0;this.options.width?a=this.width:this.width=a=0<this.container.clientWidth?this.container.clientWidth:this.width;this.options.height?c=this.height:this.height=c=0<this.container.clientHeight?this.container.clientHeight:this.height;return this.canvas.width!==a*Q||this.canvas.height!==c*Q?(ta(this.canvas,a,c),ta(this.overlaidCanvas,a,c),ta(this._eventManager.ghostCanvas,a,c),!0):!1};A.prototype._initialize=function(){this._animator?
this._animator.cancelAllAnimations():this._animator=new Ea(this);this.removeAllEventListeners();this.disableToolTip=!1;this._axes=[];this.pieDoughnutClickHandler=null;this.animationRequestId&&this.cancelRequestAnimFrame.call(window,this.animationRequestId);this._updateOptions();this.animatedRender=t&&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,axisXValueType:null,plotTypes:[]};this.layoutManager=new la(0,0,this.width,this.height,2);this.plotArea.layoutManager&&this.plotArea.layoutManager.reset();this.data=[];var a=0;if(this.options.data)for(var c=0;c<this.options.data.length;c++)if(a++,!this.options.data[c].type||0<=A._supportedChartTypes.indexOf(this.options.data[c].type)){var b=new ca(this,this.options.data[c],a-1,++this._eventManager.lastObjectId);
null===b.name&&(b.name="DataSeries "+a);null===b.color?1<this.options.data.length?(b._colorSet=[this._selectedColorSet[b.index%this._selectedColorSet.length]],b.color=this._selectedColorSet[b.index%this._selectedColorSet.length]):b._colorSet="line"===b.type||"stepLine"===b.type||"spline"===b.type||"area"===b.type||"stepArea"===b.type||"splineArea"===b.type||"stackedArea"===b.type||"stackedArea100"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type||"candlestick"===b.type||"ohlc"===b.type?
[this._selectedColorSet[0]]:this._selectedColorSet:b._colorSet=[b.color];null===b.markerSize&&(("line"===b.type||"stepLine"===b.type||"spline"===b.type||0<=b.type.toLowerCase().indexOf("area"))&&b.dataPoints&&b.dataPoints.length<this.width/16||"scatter"===b.type)&&(b.markerSize=8);"bubble"!==b.type&&"scatter"!==b.type||!b.dataPoints||(b.dataPoints.some?b.dataPoints.some(function(a){return a.x})&&b.dataPoints.sort(Ka):b.dataPoints.sort(Ka));this.data.push(b);var d=b.axisPlacement,f;"normal"===d?"xySwapped"===
this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with bar chart':"none"===this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="normal"):"xySwapped"===d?"normal"===this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with line, area, column or pie chart':"none"===this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with pie chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement=
"xySwapped"):"none"==d&&("normal"===this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with line, area, column or bar chart':"xySwapped"===this.plotInfo.axisPlacement?f='You cannot combine "'+b.type+'" with bar chart':null===this.plotInfo.axisPlacement&&(this.plotInfo.axisPlacement="none"));if(f&&window.console){window.console.log(f);return}}this._objectsInitialized=!0};A._supportedChartTypes=sa("line stepLine spline column area stepArea splineArea bar bubble scatter stackedColumn stackedColumn100 stackedBar stackedBar100 stackedArea stackedArea100 candlestick ohlc rangeColumn rangeBar rangeArea rangeSplineArea pie doughnut funnel".split(" "));
A.prototype.render=function(a){a&&(this.options=a);this._initialize();Ua(this);Va(this);var c=[];for(a=0;a<this.data.length;a++)if("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement){if(!this.data[a].axisYType||"primary"===this.data[a].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 B(this,this.options.axisY[b],"axisY",
"left",b)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY[b]=new B(this,this.options.axisY[b],"axisY","bottom",b));this.data[a].axisY=this.axisY[0<=this.data[a].axisYIndex&&this.data[a].axisYIndex<this.axisY.length?this.data[a].axisYIndex:0];this.axisY[0<=this.data[a].axisYIndex&&this.data[a].axisYIndex<this.axisY.length?this.data[a].axisYIndex:0].dataSeries.push(this.data[a])}else this.axisY.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY[0]=new B(this,
this.options.axisY,"axisY","left",0)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY[0]=new B(this,this.options.axisY,"axisY","bottom",0))),this.data[a].axisY=this.axisY[0],this.axisY[0].dataSeries.push(this.data[a]);if("secondary"===this.data[a].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 B(this,this.options.axisY2[b],
"axisY","right",b)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY2[b]=new B(this,this.options.axisY2[b],"axisY","top",b));this.data[a].axisY=this.axisY2[0<=this.data[a].axisYIndex&&this.data[a].axisYIndex<this.axisY2.length?this.data[a].axisYIndex:0];this.axisY2[0<=this.data[a].axisYIndex&&this.data[a].axisYIndex<this.axisY2.length?this.data[a].axisYIndex:0].dataSeries.push(this.data[a])}else this.axisY2.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisY2[0]=
new B(this,this.options.axisY2,"axisY","right",0)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisY2[0]=new B(this,this.options.axisY2,"axisY","top",0))),this.data[a].axisY=this.axisY2[0],this.axisY2[0].dataSeries.push(this.data[a]);if(!this.data[a].axisXType||"primary"===this.data[a].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 B(this,
this.options.axisX[b],"axisX","bottom",b)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX[b]=new B(this,this.options.axisX[b],"axisX","left",b));this.data[a].axisX=this.axisX[0<=this.data[a].axisXIndex&&this.data[a].axisXIndex<this.axisX.length?this.data[a].axisXIndex:0];this.axisX[0<=this.data[a].axisXIndex&&this.data[a].axisXIndex<this.axisX.length?this.data[a].axisXIndex:0].dataSeries.push(this.data[a])}else this.axisX.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisX[0]=
new B(this,this.options.axisX,"axisX","bottom",0)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX[0]=new B(this,this.options.axisX,"axisX","left",0))),this.data[a].axisX=this.axisX[0],this.axisX[0].dataSeries.push(this.data[a]);if("secondary"===this.data[a].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 B(this,this.options.axisX2[b],
"axisX","top",b)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX2[b]=new B(this,this.options.axisX2[b],"axisX","right",b));this.data[a].axisX=this.axisX2[0<=this.data[a].axisXIndex&&this.data[a].axisXIndex<this.axisX2.length?this.data[a].axisXIndex:0];this.axisX2[0<=this.data[a].axisXIndex&&this.data[a].axisXIndex<this.axisX2.length?this.data[a].axisXIndex:0].dataSeries.push(this.data[a])}else this.axisX2.length||("normal"===this.plotInfo.axisPlacement?this._axes.push(this.axisX2[0]=
new B(this,this.options.axisX2,"axisX","top",0)):"xySwapped"===this.plotInfo.axisPlacement&&this._axes.push(this.axisX2[0]=new B(this,this.options.axisX2,"axisX","right",0))),this.data[a].axisX=this.axisX2[0],this.axisX2[0].dataSeries.push(this.data[a])}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(a=0;a<this._axes.length;a++)if(null!==this._axes[a].viewportMinimum||null!==this._axes[a].viewportMaximum){b=!0;break}b?va(this._zoomButton,this._resetButton):(aa(this._zoomButton,this._resetButton),this.options.zoomEnabled&&(this.zoomEnabled=!0,this.panEnabled=!1));this._processData();this.options.title&&(this.title=
new oa(this,this.options.title),this.title.dockInsidePlotArea?c.push(this.title):this.title.render());if(this.options.subtitles)for(this.subtitles=[],a=0;a<this.options.subtitles.length;a++)b=new xa(this,this.options.subtitles[a]),this.subtitles.push(b),b.dockInsidePlotArea?c.push(b):b.render();this.legend=new ya(this,this.options.legend);for(a=0;a<this.data.length;a++)(this.data[a].showInLegend||"pie"===this.data[a].type||"doughnut"===this.data[a].type)&&this.legend.dataSeries.push(this.data[a]);
this.legend.dockInsidePlotArea?c.push(this.legend):this.legend.render();if("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)B.setLayoutAndRender(this.axisX,this.axisX2,this.axisY,this.axisY2,this.plotInfo.axisPlacement,this.layoutManager.getFreeSpace());else if("none"===this.plotInfo.axisPlacement)this.preparePlotArea();else return;for(a=0;a<c.length;a++)c[a].render();var d=[];if(this.animatedRender){var f=ia(this.width,this.height);f.getContext("2d").drawImage(this.canvas,
0,0,this.width,this.height)}var c=this.ctx.miterLimit,g;this.ctx.miterLimit=3;for(a=0;a<this.plotInfo.plotTypes.length;a++)for(var l=this.plotInfo.plotTypes[a],k=0;k<l.plotUnits.length;k++){var h=l.plotUnits[k],m=null;h.targetCanvas=null;this.animatedRender&&(h.targetCanvas=ia(this.width,this.height),h.targetCanvasCtx=h.targetCanvas.getContext("2d"),g=h.targetCanvasCtx.miterLimit,h.targetCanvasCtx.miterLimit=3);"line"===h.type?m=this.renderLine(h):"stepLine"===h.type?m=this.renderStepLine(h):"spline"===
h.type?m=this.renderSpline(h):"column"===h.type?m=this.renderColumn(h):"bar"===h.type?m=this.renderBar(h):"area"===h.type?m=this.renderArea(h):"stepArea"===h.type?m=this.renderStepArea(h):"splineArea"===h.type?m=this.renderSplineArea(h):"stackedColumn"===h.type?m=this.renderStackedColumn(h):"stackedColumn100"===h.type?m=this.renderStackedColumn100(h):"stackedBar"===h.type?m=this.renderStackedBar(h):"stackedBar100"===h.type?m=this.renderStackedBar100(h):"stackedArea"===h.type?m=this.renderStackedArea(h):
"stackedArea100"===h.type?m=this.renderStackedArea100(h):"bubble"===h.type?m=m=this.renderBubble(h):"scatter"===h.type?m=this.renderScatter(h):"pie"===h.type?this.renderPie(h):"doughnut"===h.type?this.renderPie(h):"candlestick"===h.type?m=this.renderCandlestick(h):"ohlc"===h.type?m=this.renderCandlestick(h):"rangeColumn"===h.type?m=this.renderRangeColumn(h):"rangeBar"===h.type?m=this.renderRangeBar(h):"rangeArea"===h.type?m=this.renderRangeArea(h):"rangeSplineArea"===h.type&&(m=this.renderRangeSplineArea(h));
for(b=0;b<h.dataSeriesIndexes.length;b++)this._dataInRenderedOrder.push(this.data[h.dataSeriesIndexes[b]]);this.animatedRender&&(h.targetCanvasCtx.miterLimit=g,m&&d.push(m))}this.ctx.miterLimit=c;this.animatedRender&&0<this._indexLabels.length&&(g=ia(this.width,this.height).getContext("2d"),d.push(this.renderIndexLabels(g)));var p=this;0<d.length?(p.disableToolTip=!0,p._animator.animate(200,p.animationDuration,function(a){p.ctx.clearRect(0,0,p.width,p.height);p.ctx.drawImage(f,0,0,Math.floor(p.width*
Q),Math.floor(p.height*Q),0,0,p.width,p.height);for(var b=0;b<d.length;b++)m=d[b],1>a&&"undefined"!==typeof m.startTimePercent?a>=m.startTimePercent&&m.animationCallback(m.easingFunction(a-m.startTimePercent,0,1,1-m.startTimePercent),m):m.animationCallback(m.easingFunction(a,0,1,1),m);p.dispatchEvent("dataAnimationIterationEnd",{chart:p})},function(){d=[];for(var a=0;a<p.plotInfo.plotTypes.length;a++)for(var b=p.plotInfo.plotTypes[a],c=0;c<b.plotUnits.length;c++)b.plotUnits[c].targetCanvas=null;f=
null;p.disableToolTip=!1})):(0<p._indexLabels.length&&p.renderIndexLabels(),p.dispatchEvent("dataAnimationIterationEnd",{chart:p}));this.attachPlotAreaEventHandlers();this.zoomEnabled||(this.panEnabled||!this._zoomButton||"none"===this._zoomButton.style.display)||aa(this._zoomButton,this._resetButton);this.toolTip._updateToolTip();this.renderCount++};A.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})};A.prototype.categoriseDataSeries=function(){for(var a="",c=0;c<this.data.length;c++)if(a=this.data[c],a.dataPoints&&(0!==a.dataPoints.length&&a.visible)&&0<=A._supportedChartTypes.indexOf(a.type)){for(var b=null,d=!1,f=null,g=!1,l=0;l<this.plotInfo.plotTypes.length;l++)if(this.plotInfo.plotTypes[l].type===a.type){d=!0;b=this.plotInfo.plotTypes[l];break}d||(b={type:a.type,totalDataSeries:0,
plotUnits:[]},this.plotInfo.plotTypes.push(b));for(l=0;l<b.plotUnits.length;l++)if(b.plotUnits[l].axisYType===a.axisYType&&b.plotUnits[l].axisXType===a.axisXType&&b.plotUnits[l].axisYIndex===a.axisYIndex&&b.plotUnits[l].axisXIndex===a.axisXIndex){g=!0;f=b.plotUnits[l];break}g||(f={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(f));b.totalDataSeries++;f.dataSeriesIndexes.push(c);a.plotUnit=f}for(c=0;c<this.plotInfo.plotTypes.length;c++)for(b=this.plotInfo.plotTypes[c],l=
a=0;l<b.plotUnits.length;l++)b.plotUnits[l].previousDataSeriesCount=a,a+=b.plotUnits[l].dataSeriesIndexes.length};A.prototype.assignIdToDataPoints=function(){for(var a=0;a<this.data.length;a++){var c=this.data[a];if(c.dataPoints)for(var b=c.dataPoints.length,d=0;d<b;d++)c.dataPointIds[d]=++this._eventManager.lastObjectId}};A.prototype._processData=function(){this.assignIdToDataPoints();this.categoriseDataSeries();for(var a=0;a<this.plotInfo.plotTypes.length;a++)for(var c=this.plotInfo.plotTypes[a],
b=0;b<c.plotUnits.length;b++){var d=c.plotUnits[b];"line"===d.type||"stepLine"===d.type||"spline"===d.type||"column"===d.type||"area"===d.type||"stepArea"===d.type||"splineArea"===d.type||"bar"===d.type||"bubble"===d.type||"scatter"===d.type?this._processMultiseriesPlotUnit(d):"stackedColumn"===d.type||"stackedBar"===d.type||"stackedArea"===d.type?this._processStackedPlotUnit(d):"stackedColumn100"===d.type||"stackedBar100"===d.type||"stackedArea100"===d.type?this._processStacked100PlotUnit(d):"candlestick"!==
d.type&&"ohlc"!==d.type&&"rangeColumn"!==d.type&&"rangeBar"!==d.type&&"rangeArea"!==d.type&&"rangeSplineArea"!==d.type||this._processMultiYPlotUnit(d)}};A.prototype._processMultiseriesPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var c=a.axisY.dataInfo,b=a.axisX.dataInfo,d,f,g=!1,l=0;l<a.dataSeriesIndexes.length;l++){var k=this.data[a.dataSeriesIndexes[l]],h=0,m=!1,p=!1,n;if("normal"===k.axisPlacement||"xySwapped"===k.axisPlacement)var e=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,q=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(k.dataPoints[h].x&&k.dataPoints[h].x.getTime||"dateTime"===k.xValueType)g=!0;for(h=0;h<k.dataPoints.length;h++){"undefined"===typeof k.dataPoints[h].x&&(k.dataPoints[h].x=h+(a.axisX.logarithmic?1:0));k.dataPoints[h].x.getTime?(g=!0,d=k.dataPoints[h].x.getTime()):d=k.dataPoints[h].x;f=k.dataPoints[h].y;d<b.min&&(b.min=d);d>b.max&&(b.max=d);f<c.min&&"number"===typeof f&&(c.min=f);f>c.max&&"number"===typeof f&&(c.max=f);if(0<h){if(a.axisX.logarithmic){var r=d/k.dataPoints[h-1].x;1>r&&(r=1/r);b.minDiff>
r&&1!==r&&(b.minDiff=r)}else r=d-k.dataPoints[h-1].x,0>r&&(r*=-1),b.minDiff>r&&0!==r&&(b.minDiff=r);null!==f&&null!==k.dataPoints[h-1].y&&(a.axisY.logarithmic?(r=f/k.dataPoints[h-1].y,1>r&&(r=1/r),c.minDiff>r&&1!==r&&(c.minDiff=r)):(r=f-k.dataPoints[h-1].y,0>r&&(r*=-1),c.minDiff>r&&0!==r&&(c.minDiff=r)))}if(d<e&&!m)null!==f&&(n=d);else{if(!m&&(m=!0,0<h)){h-=2;continue}if(d>q&&!p)p=!0;else if(d>q&&p)continue;k.dataPoints[h].label&&(a.axisX.labels[d]=k.dataPoints[h].label);d<b.viewPortMin&&(b.viewPortMin=
d);d>b.viewPortMax&&(b.viewPortMax=d);null===f?b.viewPortMin===d&&n<d&&(b.viewPortMin=n):(f<c.viewPortMin&&"number"===typeof f&&(c.viewPortMin=f),f>c.viewPortMax&&"number"===typeof f&&(c.viewPortMax=f))}}this.plotInfo.axisXValueType=k.xValueType=g?"dateTime":"number"}};A.prototype._processStackedPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var c=a.axisY.dataInfo,b=a.axisX.dataInfo,d,f,g=!1,l=[],k=[],h=Infinity,m=-Infinity,p=0;p<a.dataSeriesIndexes.length;p++){var n=
this.data[a.dataSeriesIndexes[p]],e=0,q=!1,r=!1,s;if("normal"===n.axisPlacement||"xySwapped"===n.axisPlacement)var x=this.sessionVariables.axisX.newViewportMinimum?this.sessionVariables.axisX.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:-Infinity,u=this.sessionVariables.axisX.newViewportMaximum?this.sessionVariables.axisX.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(n.dataPoints[e].x&&n.dataPoints[e].x.getTime||"dateTime"===n.xValueType)g=!0;for(e=0;e<n.dataPoints.length;e++){"undefined"===typeof n.dataPoints[e].x&&(n.dataPoints[e].x=e+(a.axisX.logarithmic?1:0));n.dataPoints[e].x.getTime?(g=!0,d=n.dataPoints[e].x.getTime()):d=n.dataPoints[e].x;f=y(n.dataPoints[e].y)?0:n.dataPoints[e].y;d<b.min&&(b.min=d);
d>b.max&&(b.max=d);if(0<e){if(a.axisX.logarithmic){var v=d/n.dataPoints[e-1].x;1>v&&(v=1/v);b.minDiff>v&&1!==v&&(b.minDiff=v)}else v=d-n.dataPoints[e-1].x,0>v&&(v*=-1),b.minDiff>v&&0!==v&&(b.minDiff=v);null!==f&&null!==n.dataPoints[e-1].y&&(a.axisY.logarithmic?0<f&&(v=f/n.dataPoints[e-1].y,1>v&&(v=1/v),c.minDiff>v&&1!==v&&(c.minDiff=v)):(v=f-n.dataPoints[e-1].y,0>v&&(v*=-1),c.minDiff>v&&0!==v&&(c.minDiff=v)))}if(d<x&&!q)null!==n.dataPoints[e].y&&(s=d);else{if(!q&&(q=!0,0<e)){e-=2;continue}if(d>u&&
!r)r=!0;else if(d>u&&r)continue;n.dataPoints[e].label&&(a.axisX.labels[d]=n.dataPoints[e].label);d<b.viewPortMin&&(b.viewPortMin=d);d>b.viewPortMax&&(b.viewPortMax=d);null===n.dataPoints[e].y?b.viewPortMin===d&&s<d&&(b.viewPortMin=s):(a.yTotals[d]=(a.yTotals[d]?a.yTotals[d]:0)+Math.abs(f),0<=f?l[d]?l[d]+=f:(l[d]=f,h=Math.min(f,h)):k[d]?k[d]+=f:(k[d]=f,m=Math.max(f,m)))}}this.plotInfo.axisXValueType=n.xValueType=g?"dateTime":"number"}for(e in l)l.hasOwnProperty(e)&&!isNaN(e)&&(a=l[e],a<c.min&&(c.min=
Math.min(a,h)),a>c.max&&(c.max=a),e<b.viewPortMin||e>b.viewPortMax||(a<c.viewPortMin&&(c.viewPortMin=Math.min(a,h)),a>c.viewPortMax&&(c.viewPortMax=a)));for(e in k)k.hasOwnProperty(e)&&!isNaN(e)&&(a=k[e],a<c.min&&(c.min=a),a>c.max&&(c.max=Math.max(a,m)),e<b.viewPortMin||e>b.viewPortMax||(a<c.viewPortMin&&(c.viewPortMin=a),a>c.viewPortMax&&(c.viewPortMax=Math.max(a,m))))}};A.prototype._processStacked100PlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length)){for(var c=a.axisY.dataInfo,
b=a.axisX.dataInfo,d,f,g=!1,l=!1,k=!1,h=[],m=0;m<a.dataSeriesIndexes.length;m++){var p=this.data[a.dataSeriesIndexes[m]],n=0,e=!1,q=!1,r;if("normal"===p.axisPlacement||"xySwapped"===p.axisPlacement)var s=this.sessionVariables.axisX.newViewportMinimum?this.sessionVariables.axisX.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:-Infinity,x=this.sessionVariables.axisX.newViewportMaximum?
this.sessionVariables.axisX.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(p.dataPoints[n].x&&p.dataPoints[n].x.getTime||"dateTime"===p.xValueType)g=!0;for(n=0;n<p.dataPoints.length;n++){"undefined"===typeof p.dataPoints[n].x&&(p.dataPoints[n].x=n+(a.axisX.logarithmic?1:0));p.dataPoints[n].x.getTime?(g=!0,d=p.dataPoints[n].x.getTime()):d=p.dataPoints[n].x;
f=y(p.dataPoints[n].y)?null:p.dataPoints[n].y;d<b.min&&(b.min=d);d>b.max&&(b.max=d);if(0<n){if(a.axisX.logarithmic){var u=d/p.dataPoints[n-1].x;1>u&&(u=1/u);b.minDiff>u&&1!==u&&(b.minDiff=u)}else u=d-p.dataPoints[n-1].x,0>u&&(u*=-1),b.minDiff>u&&0!==u&&(b.minDiff=u);y(f)||null===p.dataPoints[n-1].y||(a.axisY.logarithmic?0<f&&(u=f/p.dataPoints[n-1].y,1>u&&(u=1/u),c.minDiff>u&&1!==u&&(c.minDiff=u)):(u=f-p.dataPoints[n-1].y,0>u&&(u*=-1),c.minDiff>u&&0!==u&&(c.minDiff=u)))}if(d<s&&!e)null!==f&&(r=d);
else{if(!e&&(e=!0,0<n)){n-=2;continue}if(d>x&&!q)q=!0;else if(d>x&&q)continue;p.dataPoints[n].label&&(a.axisX.labels[d]=p.dataPoints[n].label);d<b.viewPortMin&&(b.viewPortMin=d);d>b.viewPortMax&&(b.viewPortMax=d);null===f?b.viewPortMin===d&&r<d&&(b.viewPortMin=r):(a.yTotals[d]=(a.yTotals[d]?a.yTotals[d]:0)+Math.abs(f),0<=f?l=!0:0>f&&(k=!0),h[d]=h[d]?h[d]+Math.abs(f):Math.abs(f))}}this.plotInfo.axisXValueType=p.xValueType=g?"dateTime":"number"}a.axisY.logarithmic?(c.max=y(c.viewPortMax)?99*Math.pow(a.axisY.logarithmBase,
-0.05):Math.max(c.viewPortMax,99*Math.pow(a.axisY.logarithmBase,-0.05)),c.min=y(c.viewPortMin)?1:Math.min(c.viewPortMin,1)):l&&!k?(c.max=y(c.viewPortMax)?99:Math.max(c.viewPortMax,99),c.min=y(c.viewPortMin)?1:Math.min(c.viewPortMin,1)):l&&k?(c.max=y(c.viewPortMax)?99:Math.max(c.viewPortMax,99),c.min=y(c.viewPortMin)?-99:Math.min(c.viewPortMin,-99)):!l&&k&&(c.max=y(c.viewPortMax)?-1:Math.max(c.viewPortMax,-1),c.min=y(c.viewPortMin)?-99:Math.min(c.viewPortMin,-99));c.viewPortMin=c.min;c.viewPortMax=
c.max;a.dataPointYSums=h}};A.prototype._processMultiYPlotUnit=function(a){if(a.dataSeriesIndexes&&!(1>a.dataSeriesIndexes.length))for(var c=a.axisY.dataInfo,b=a.axisX.dataInfo,d,f,g,l,k=!1,h=0;h<a.dataSeriesIndexes.length;h++){var m=this.data[a.dataSeriesIndexes[h]],p=0,n=!1,e=!1,q,r,s;if("normal"===m.axisPlacement||"xySwapped"===m.axisPlacement)var x=this.sessionVariables.axisX.newViewportMinimum?this.sessionVariables.axisX.newViewportMinimum:this.options.axisX&&this.options.axisX.viewportMinimum?
this.options.axisX.viewportMinimum:this.options.axisX&&this.options.axisX.minimum?this.options.axisX.minimum:-Infinity,u=this.sessionVariables.axisX.newViewportMaximum?this.sessionVariables.axisX.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[p].x&&m.dataPoints[p].x.getTime||"dateTime"===m.xValueType)k=!0;for(p=0;p<m.dataPoints.length;p++){"undefined"===
typeof m.dataPoints[p].x&&(m.dataPoints[p].x=p+(a.axisX.logarithmic?1:0));m.dataPoints[p].x.getTime?(k=!0,d=m.dataPoints[p].x.getTime()):d=m.dataPoints[p].x;if((f=m.dataPoints[p].y)&&f.length){g=Math.min.apply(null,f);l=Math.max.apply(null,f);r=!0;for(var v=0;v<f.length;v++)null===f.k&&(r=!1);r&&(n||(s=q),q=d)}d<b.min&&(b.min=d);d>b.max&&(b.max=d);g<c.min&&(c.min=g);l>c.max&&(c.max=l);0<p&&(a.axisX.logarithmic?(r=d/m.dataPoints[p-1].x,1>r&&(r=1/r),b.minDiff>r&&1!==r&&(b.minDiff=r)):(r=d-m.dataPoints[p-
1].x,0>r&&(r*=-1),b.minDiff>r&&0!==r&&(b.minDiff=r)),f&&(null!==f[0]&&m.dataPoints[p-1].y&&null!==m.dataPoints[p-1].y[0])&&(a.axisY.logarithmic?(r=f[0]/m.dataPoints[p-1].y[0],1>r&&(r=1/r),c.minDiff>r&&1!==r&&(c.minDiff=r)):(r=f[0]-m.dataPoints[p-1].y[0],0>r&&(r*=-1),c.minDiff>r&&0!==r&&(c.minDiff=r))));if(!(d<x)||n){if(!n&&(n=!0,0<p)){p-=2;q=s;continue}if(d>u&&!e)e=!0;else if(d>u&&e)continue;m.dataPoints[p].label&&(a.axisX.labels[d]=m.dataPoints[p].label);d<b.viewPortMin&&(b.viewPortMin=d);d>b.viewPortMax&&
(b.viewPortMax=d);if(b.viewPortMin===d&&f)for(v=0;v<f.length;v++)if(null===f[v]&&q<d){b.viewPortMin=q;break}null===f?b.viewPortMin===d&&q<d&&(b.viewPortMin=q):(g<c.viewPortMin&&(c.viewPortMin=g),l>c.viewPortMax&&(c.viewPortMax=l))}}this.plotInfo.axisXValueType=m.xValueType=k?"dateTime":"number"}};A.prototype.getDataPointAtXY=function(a,c,b){b=b||!1;for(var d=[],f=this._dataInRenderedOrder.length-1;0<=f;f--){var g=null;(g=this._dataInRenderedOrder[f].getDataPointAtXY(a,c,b))&&d.push(g)}a=null;c=!1;
for(b=0;b<d.length;b++)if("line"===d[b].dataSeries.type||"stepLine"===d[b].dataSeries.type||"area"===d[b].dataSeries.type||"stepArea"===d[b].dataSeries.type)if(f=K("markerSize",d[b].dataPoint,d[b].dataSeries)||8,d[b].distance<=f/2){c=!0;break}for(b=0;b<d.length;b++)c&&"line"!==d[b].dataSeries.type&&"stepLine"!==d[b].dataSeries.type&&"area"!==d[b].dataSeries.type&&"stepArea"!==d[b].dataSeries.type||(a?d[b].distance<=a.distance&&(a=d[b]):a=d[b]);return a};A.prototype.getObjectAtXY=function(a,c,b){var d=
null;if(b=this.getDataPointAtXY(a,c,b||!1))d=b.dataSeries.dataPointIds[b.dataPointIndex];else if(t)d=Na(a,c,this._eventManager.ghostCtx);else for(b=0;b<this.legend.items.length;b++){var f=this.legend.items[b];a>=f.x1&&(a<=f.x2&&c>=f.y1&&c<=f.y2)&&(d=f.id)}return d};A.prototype.getAutoFontSize=function(a,c,b){a/=400;return Math.max(10,Math.round(Math.min(this.width,this.height)*a))};A.prototype.resetOverlayedCanvas=function(){this.overlaidCanvasCtx.clearRect(0,0,this.width,this.height)};A.prototype.clearCanvas=
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))};A.prototype.attachEvent=function(a){this._events.push(a)};A.prototype._touchEventHandler=function(a){if(a.changedTouches&&this.interactivityEnabled){var c=[],b=a.changedTouches,d=b?b[0]:a,f=null;switch(a.type){case "touchstart":case "MSPointerDown":c=["mousemove","mousedown"];this._lastTouchData=za(d);this._lastTouchData.time=new Date;
break;case "touchmove":case "MSPointerMove":c=["mousemove"];break;case "touchend":case "MSPointerUp":c="touchstart"===this._lastTouchEventType||"MSPointerDown"===this._lastTouchEventType?["mouseup","click"]:["mouseup"];break;default:return}if(!(b&&1<b.length)){f=za(d);f.time=new Date;try{var g=f.y-this._lastTouchData.y;if(15<Math.abs(g)&&this._lastTouchData.scroll){this._lastTouchData.scroll=!0;var l=window.parent||window;l&&l.scrollBy&&l.scrollBy(0,-g)}}catch(k){}this._lastTouchEventType=a.type;
if(this._lastTouchData.scroll&&this.zoomEnabled)this.isDrag&&this.resetOverlayedCanvas(),this.isDrag=!1;else for(b=0;b<c.length;b++)f=c[b],g=document.createEvent("MouseEvent"),g.initMouseEvent(f,!0,!0,window,1,d.screenX,d.screenY,d.clientX,d.clientY,!1,!1,!1,!1,0,null),d.target.dispatchEvent(g),a.preventManipulation&&a.preventManipulation()}}};A.prototype._dispatchRangeEvent=function(a,c){var b={chart:this};b.type=a;b.trigger=c;var d=[];this.axisX&&0<this.axisX.length&&d.push("axisX");this.axisX2&&
0<this.axisX2.length&&d.push("axisX2");this.axisY&&0<this.axisY.length&&d.push("axisY");this.axisY2&&0<this.axisY2.length&&d.push("axisY2");for(var f=0;f<d.length;f++)if(y(b[d[f]])&&(b[d[f]]=[]),"axisY"===d[f])for(var g=0;g<this.axisY.length;g++)b[d[f]].push({viewportMinimum:this[d[f]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[d[f]][g].sessionVariables.newViewportMaximum});else if("axisY2"===d[f])for(g=0;g<this.axisY2.length;g++)b[d[f]].push({viewportMinimum:this[d[f]][g].sessionVariables.newViewportMinimum,
viewportMaximum:this[d[f]][g].sessionVariables.newViewportMaximum});else if("axisX"===d[f])for(g=0;g<this.axisX.length;g++)b[d[f]].push({viewportMinimum:this[d[f]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[d[f]][g].sessionVariables.newViewportMaximum});else if("axisX2"===d[f])for(g=0;g<this.axisX2.length;g++)b[d[f]].push({viewportMinimum:this[d[f]][g].sessionVariables.newViewportMinimum,viewportMaximum:this[d[f]][g].sessionVariables.newViewportMaximum});this.dispatchEvent(a,b,this)};
A.prototype._mouseEventHandler=function(a){"undefined"===typeof a.target&&a.srcElement&&(a.target=a.srcElement);var c=za(a),b=a.type,d,f;a.which?f=3==a.which:a.button&&(f=2==a.button);A.capturedEventParam&&(d=A.capturedEventParam,"mouseup"===b&&(A.capturedEventParam=null,d.chart.overlaidCanvas.releaseCapture?d.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",d.chart._mouseEventHandler,!1)),d.hasOwnProperty(b)&&("mouseup"!==b||d.chart.overlaidCanvas.releaseCapture?
a.target===d.chart.overlaidCanvas&&d[b].call(d.context,c.x,c.y):a.target!==d.chart.overlaidCanvas&&(d.chart.isDrag=!1)));if(this.interactivityEnabled)if(this._ignoreNextEvent)this._ignoreNextEvent=!1;else if(a.preventManipulation&&a.preventManipulation(),a.preventDefault&&a.preventDefault(),!f){if(!A.capturedEventParam&&this._events){for(var g=0;g<this._events.length;g++)if(this._events[g].hasOwnProperty(b))if(d=this._events[g],f=d.bounds,c.x>=f.x1&&c.x<=f.x2&&c.y>=f.y1&&c.y<=f.y2){d[b].call(d.context,
c.x,c.y);"mousedown"===b&&!0===d.capture?(A.capturedEventParam=d,this.overlaidCanvas.setCapture?this.overlaidCanvas.setCapture():document.documentElement.addEventListener("mouseup",this._mouseEventHandler,!1)):"mouseup"===b&&(d.chart.overlaidCanvas.releaseCapture?d.chart.overlaidCanvas.releaseCapture():document.documentElement.removeEventListener("mouseup",this._mouseEventHandler,!1));break}else d=null;a.target.style.cursor=d&&d.cursor?d.cursor:this._defaultCursor}b=this.plotArea;if(c.x<b.x1||c.x>
b.x2||c.y<b.y1||c.y>b.y2)this.toolTip&&this.toolTip.enabled?this.toolTip.hide():this.resetOverlayedCanvas();this.isDrag&&this.zoomEnabled||!this._eventManager||this._eventManager.mouseEventHandler(a)}};A.prototype._plotAreaMouseDown=function(a,c){this.isDrag=!0;this.dragStartPoint={x:a,y:c}};A.prototype._plotAreaMouseUp=function(a,c){if(("normal"===this.plotInfo.axisPlacement||"xySwapped"===this.plotInfo.axisPlacement)&&this.isDrag){var b=c-this.dragStartPoint.y,d=a-this.dragStartPoint.x,f=0<=this.zoomType.indexOf("x"),
g=0<=this.zoomType.indexOf("y"),l=!1;this.resetOverlayedCanvas();if("xySwapped"===this.plotInfo.axisPlacement)var k=g,g=f,f=k;if(this.panEnabled||this.zoomEnabled){if(this.panEnabled)for(f=g=0;f<this._axes.length;f++)b=this._axes[f],b.logarithmic?b.viewportMinimum<b.minimum?(g=b.minimum/b.viewportMinimum,b.sessionVariables.newViewportMinimum=b.viewportMinimum*g,b.sessionVariables.newViewportMaximum=b.viewportMaximum*g,l=!0):b.viewportMaximum>b.maximum&&(g=b.viewportMaximum/b.maximum,b.sessionVariables.newViewportMinimum=
b.viewportMinimum/g,b.sessionVariables.newViewportMaximum=b.viewportMaximum/g,l=!0):b.viewportMinimum<b.minimum?(g=b.minimum-b.viewportMinimum,b.sessionVariables.newViewportMinimum=b.viewportMinimum+g,b.sessionVariables.newViewportMaximum=b.viewportMaximum+g,l=!0):b.viewportMaximum>b.maximum&&(g=b.viewportMaximum-b.maximum,b.sessionVariables.newViewportMinimum=b.viewportMinimum-g,b.sessionVariables.newViewportMaximum=b.viewportMaximum-g,l=!0);else if((!f||2<Math.abs(d))&&(!g||2<Math.abs(b))&&this.zoomEnabled){if(!this.dragStartPoint)return;
b=f?this.dragStartPoint.x:this.plotArea.x1;d=g?this.dragStartPoint.y:this.plotArea.y1;f=f?a:this.plotArea.x2;g=g?c:this.plotArea.y2;2<Math.abs(b-f)&&2<Math.abs(d-g)&&this._zoomPanToSelectedRegion(b,d,f,g)&&(l=!0)}l&&(this._ignoreNextEvent=!0,this._dispatchRangeEvent("rangeChanging","zoom"),this.render(),this._dispatchRangeEvent("rangeChanged","zoom"),l&&(this.zoomEnabled&&"none"===this._zoomButton.style.display)&&(va(this._zoomButton,this._resetButton),$(this,this._zoomButton,"pan"),$(this,this._resetButton,
"reset")))}}this.isDrag=!1};A.prototype._plotAreaMouseMove=function(a,c){if(this.isDrag&&"none"!==this.plotInfo.axisPlacement){var b=0,d=0,f=b=null,f=0<=this.zoomType.indexOf("x"),g=0<=this.zoomType.indexOf("y"),l=this;"xySwapped"===this.plotInfo.axisPlacement&&(b=g,g=f,f=b);b=this.dragStartPoint.x-a;d=this.dragStartPoint.y-c;2<Math.abs(b)&&8>Math.abs(b)&&(this.panEnabled||this.zoomEnabled)?this.toolTip.hide():this.panEnabled||this.zoomEnabled||this.toolTip.mouseMoveHandler(a,c);if((!f||2<Math.abs(b)||
!g||2<Math.abs(d))&&(this.panEnabled||this.zoomEnabled))if(this.panEnabled)f={x1:f?this.plotArea.x1+b:this.plotArea.x1,y1:g?this.plotArea.y1+d:this.plotArea.y1,x2:f?this.plotArea.x2+b:this.plotArea.x2,y2:g?this.plotArea.y2+d:this.plotArea.y2},clearTimeout(l._panTimerId),l._panTimerId=setTimeout(function(b,d,e,f){return function(){l._zoomPanToSelectedRegion(b,d,e,f,!0)&&(l._dispatchRangeEvent("rangeChanging","pan"),l.render(),l._dispatchRangeEvent("rangeChanged","pan"),l.dragStartPoint.x=a,l.dragStartPoint.y=
c)}}(f.x1,f.y1,f.x2,f.y2),0);else if(this.zoomEnabled){this.resetOverlayedCanvas();b=this.overlaidCanvasCtx.globalAlpha;this.overlaidCanvasCtx.fillStyle="#A89896";var d=f?this.dragStartPoint.x:this.plotArea.x1,k=g?this.dragStartPoint.y:this.plotArea.y1,h=f?a-this.dragStartPoint.x:this.plotArea.x2-this.plotArea.x1,m=g?c-this.dragStartPoint.y:this.plotArea.y2-this.plotArea.y1;this.validateRegion(d,k,f?a:this.plotArea.x2-this.plotArea.x1,g?c:this.plotArea.y2-this.plotArea.y1,"xy"!==this.zoomType).isValid&&
(this.resetOverlayedCanvas(),this.overlaidCanvasCtx.fillStyle="#99B2B5");this.overlaidCanvasCtx.globalAlpha=0.7;this.overlaidCanvasCtx.fillRect(d,k,h,m);this.overlaidCanvasCtx.globalAlpha=b}}else this.toolTip.mouseMoveHandler(a,c)};A.prototype._zoomPanToSelectedRegion=function(a,c,b,d,f){a=this.validateRegion(a,c,b,d,f);c=a.axesWithValidRange;b=a.axesRanges;if(a.isValid)for(d=0;d<c.length;d++)f=b[d],c[d].setViewPortRange(f.val1,f.val2);return a.isValid};A.prototype.validateRegion=function(a,c,b,d,
f){f=f||!1;for(var g=0<=this.zoomType.indexOf("x"),l=0<=this.zoomType.indexOf("y"),k=!1,h=[],m=[],p=[],n=0;n<this.axisX.length;n++)this.axisX[n]&&g&&m.push(this.axisX[n]);for(n=0;n<this.axisX2.length;n++)this.axisX2[n]&&g&&m.push(this.axisX2[n]);for(n=0;n<this.axisY.length;n++)this.axisY[n]&&l&&m.push(this.axisY[n]);for(n=0;n<this.axisY2.length;n++)this.axisY2[n]&&l&&m.push(this.axisY2[n]);for(g=0;g<m.length;g++){var l=m[g],n=l.convertPixelToValue({x:a,y:c}),e=l.convertPixelToValue({x:b,y:d});if(n>
e)var q=e,e=n,n=q;if(isFinite(l.dataInfo.minDiff))if(!(l.logarithmic&&e/n<Math.pow(l.dataInfo.minDiff,3)||!l.logarithmic&&Math.abs(e-n)<3*Math.abs(l.dataInfo.minDiff)||n<l.minimum||e>l.maximum))h.push(l),p.push({val1:n,val2:e}),k=!0;else if(!f){k=!1;break}}return{isValid:k,axesWithValidRange:h,axesRanges:p}};A.prototype.preparePlotArea=function(){var a=this.plotArea;!t&&(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 c=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=c.x1<c.x2?c.x1:b.lineCoordinates.x1;a.y1=c.y1<b.lineCoordinates.y1?c.y1:b.lineCoordinates.y1;a.x2=c.x2>b.lineCoordinates.x2?c.x2:b.lineCoordinates.x2;a.y2=c.y2>c.y1?c.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=c.x1<c.x2?c.x1:b.lineCoordinates.x1,a.y1=c.y1<b.lineCoordinates.y1?
c.y1:b.lineCoordinates.y1,a.x2=c.x2>b.lineCoordinates.x2?c.x2:b.lineCoordinates.x2,a.y2=c.y2>c.y1?c.y2:b.lineCoordinates.y2,a.width=a.x2-a.x1,a.height=a.y2-a.y1)}else c=this.layoutManager.getFreeSpace(),a.x1=c.x1,a.x2=c.x2,a.y1=c.y1,a.y2=c.y2,a.width=c.width,a.height=c.height;t||(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 la(a.x1,a.y1,a.x2,a.y2,2)};A.prototype.renderIndexLabels=
function(a){var c=a||this.plotArea.ctx,b=this.plotArea,d=0,f=0,g=0,l=0,k=d=l=f=g=0,h=0;for(a=0;a<this._indexLabels.length;a++){var m=this._indexLabels[a],p=m.chartType.toLowerCase(),n,e,k=K("indexLabelFontColor",m.dataPoint,m.dataSeries),h=K("indexLabelFontSize",m.dataPoint,m.dataSeries);n=K("indexLabelFontFamily",m.dataPoint,m.dataSeries);e=K("indexLabelFontStyle",m.dataPoint,m.dataSeries);var l=K("indexLabelFontWeight",m.dataPoint,m.dataSeries),q=K("indexLabelBackgroundColor",m.dataPoint,m.dataSeries),
f=K("indexLabelMaxWidth",m.dataPoint,m.dataSeries),g=K("indexLabelWrap",m.dataPoint,m.dataSeries),r=K("indexLabelLineDashType",m.dataPoint,m.dataSeries),s=K("indexLabelLineColor",m.dataPoint,m.dataSeries),x=y(m.dataPoint.indexLabelLineThickness)?y(m.dataSeries.options.indexLabelLineThickness)?0:m.dataSeries.options.indexLabelLineThickness:m.dataPoint.indexLabelLineThickness,d=0<x?Math.min(10,("normal"===this.plotInfo.axisPlacement?this.plotArea.height:this.plotArea.width)<<0):0,u={percent:null,total:null},
v=null;if(0<=m.dataSeries.type.indexOf("stacked")||"pie"===m.dataSeries.type||"doughnut"===m.dataSeries.type)u=this.getPercentAndTotal(m.dataSeries,m.dataPoint);if(m.dataSeries.indexLabelFormatter||m.dataPoint.indexLabelFormatter)v={chart:this,dataSeries:m.dataSeries,dataPoint:m.dataPoint,index:m.indexKeyword,total:u.total,percent:u.percent};var z=m.dataPoint.indexLabelFormatter?m.dataPoint.indexLabelFormatter(v):m.dataPoint.indexLabel?this.replaceKeywordsWithValue(m.dataPoint.indexLabel,m.dataPoint,
m.dataSeries,null,m.indexKeyword):m.dataSeries.indexLabelFormatter?m.dataSeries.indexLabelFormatter(v):m.dataSeries.indexLabel?this.replaceKeywordsWithValue(m.dataSeries.indexLabel,m.dataPoint,m.dataSeries,null,m.indexKeyword):null;if(null!==z&&""!==z){var u=K("indexLabelPlacement",m.dataPoint,m.dataSeries),v=K("indexLabelOrientation",m.dataPoint,m.dataSeries),w=m.direction,Z=m.dataSeries.axisX,ba=m.dataSeries.axisY,A=!1,q=new V(c,{x:0,y:0,maxWidth:f?f:0.5*this.width,maxHeight:g?5*h:1.5*h,angle:"horizontal"===
v?0:-90,text:z,padding:0,backgroundColor:q,horizontalAlign:"left",fontSize:h,fontFamily:n,fontWeight:l,fontColor:k,fontStyle:e,textBaseline:"top"});q.measureText();m.dataSeries.indexLabelMaxWidth=q.maxWidth;if("stackedarea100"===p){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"===p||"rangesplinearea"===p){if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>Z.viewportMaximum||Math.max.apply(null,m.dataPoint.y)<ba.viewportMinimum||Math.min.apply(null,
m.dataPoint.y)>ba.viewportMaximum)continue}else if(0<=p.indexOf("line")||0<=p.indexOf("area")||0<=p.indexOf("bubble")||0<=p.indexOf("scatter")){if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>Z.viewportMaximum||m.dataPoint.y<ba.viewportMinimum||m.dataPoint.y>ba.viewportMaximum)continue}else if(0<=p.indexOf("column")){if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>Z.viewportMaximum||m.bounds.y1>b.y2||m.bounds.y2<b.y1)continue}else if(0<=p.indexOf("bar")){if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>
Z.viewportMaximum||m.bounds.x1>b.x2||m.bounds.x2<b.x1)continue}else if("candlestick"===p||"ohlc"===p){if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>Z.viewportMaximum||Math.max.apply(null,m.dataPoint.y)<ba.viewportMinimum||Math.min.apply(null,m.dataPoint.y)>ba.viewportMaximum)continue}else if(m.dataPoint.x<Z.viewportMinimum||m.dataPoint.x>Z.viewportMaximum)continue;f=l=2;"horizontal"===v?(k=q.width,h=q.height):(h=q.width,k=q.height);if("normal"===this.plotInfo.axisPlacement){if(0<=p.indexOf("line")||
0<=p.indexOf("area"))u="auto",l=4;else if(0<=p.indexOf("stacked"))"auto"===u&&(u="inside");else if("bubble"===p||"scatter"===p)u="inside";n=m.point.x-k/2;"inside"!==u?(f=b.y1,g=b.y2,0<w?(e=m.point.y-h-l-d,e<f&&(e="auto"===u?Math.max(m.point.y,f)+l+d:f+l+d,A=e+h>m.point.y)):(e=m.point.y+l+d,e>g-h-l-d&&(e="auto"===u?Math.min(m.point.y,g)-h-l-d:g-h-l-d,A=e<m.point.y))):(f=Math.max(m.bounds.y1,b.y1),g=Math.min(m.bounds.y2,b.y2),d=0<=p.indexOf("range")?0<w?Math.max(m.bounds.y1,b.y1)+h/2+l:Math.min(m.bounds.y2,
b.y2)-h/2-l:(Math.max(m.bounds.y1,b.y1)+Math.min(m.bounds.y2,b.y2))/2,0<w?(e=Math.max(m.point.y,d)-h/2,e<f&&("bubble"===p||"scatter"===p)&&(e=Math.max(m.point.y-h-l,b.y1+l))):(e=Math.min(m.point.y,d)-h/2,e>g-h-l&&("bubble"===p||"scatter"===p)&&(e=Math.min(m.point.y+l,b.y2-h-l))),e=Math.min(e,g-h))}else 0<=p.indexOf("line")||0<=p.indexOf("area")||0<=p.indexOf("scatter")?(u="auto",f=4):0<=p.indexOf("stacked")?"auto"===u&&(u="inside"):"bubble"===p&&(u="inside"),e=m.point.y-h/2,"inside"!==u?(l=b.x1,g=
b.x2,0>w?(n=m.point.x-k-f-d,n<l&&(n="auto"===u?Math.max(m.point.x,l)+f+d:l+f+d,A=n+k>m.point.x)):(n=m.point.x+f+d,n>g-k-f-d&&(n="auto"===u?Math.min(m.point.x,g)-k-f-d:g-k-f-d,A=n<m.point.x))):(l=Math.max(m.bounds.x1,b.x1),Math.min(m.bounds.x2,b.x2),d=0<=p.indexOf("range")?0>w?Math.max(m.bounds.x1,b.x1)+k/2+f:Math.min(m.bounds.x2,b.x2)-k/2-f:(Math.max(m.bounds.x1,b.x1)+Math.min(m.bounds.x2,b.x2))/2,n=0>w?Math.max(m.point.x,d)-k/2:Math.min(m.point.x,d)-k/2,n=Math.max(n,l));"vertical"===v&&(e+=h);q.x=
n;q.y=e;q.render(!0);x&&("inside"!==u&&(0>p.indexOf("bar")&&m.point.x>b.x1&&m.point.x<b.x2||!A)&&(0>p.indexOf("column")&&m.point.y>b.y1&&m.point.y<b.y2||!A))&&(c.lineWidth=x,c.strokeStyle=s?s:"gray",c.setLineDash&&c.setLineDash(F(r,x)),c.beginPath(),c.moveTo(m.point.x,m.point.y),0<=p.indexOf("bar")?c.lineTo(n+(0<m.direction?0:k),e+("horizontal"===v?h:-h)/2):0<=p.indexOf("column")?c.lineTo(n+k/2,e+((0<m.direction?h:-h)+("horizontal"===v?h:-h))/2):c.lineTo(n+k/2,e+((e<m.point.y?h:-h)+("horizontal"===
v?h:-h))/2),c.stroke())}}c={source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,easingFunction:E.easing.easeInQuad,animationBase:0,startTimePercent:0.7};for(a=0;a<this._indexLabels.length;a++)m=this._indexLabels[a],q=K("indexLabelBackgroundColor",m.dataPoint,m.dataSeries),m.dataSeries.indexLabelBackgroundColor=y(q)?t?"transparent":null:q;return c};A.prototype.renderLine=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=this._eventManager.ghostCtx;
c.save();var d=this.plotArea;c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();for(var d=[],f,g=0;g<a.dataSeriesIndexes.length;g++){var l=a.dataSeriesIndexes[g],k=this.data[l];c.lineWidth=k.lineThickness;var h=k.dataPoints,m="solid";if(c.setLineDash){var p=F(k.nullDataLineDashType,k.lineThickness),m=k.lineDashType,n=F(m,k.lineThickness);c.setLineDash(n)}var e=k.id;this._eventManager.objectMap[e]={objectType:"dataSeries",dataSeriesIndex:l};e=D(e);b.strokeStyle=e;b.lineWidth=0<k.lineThickness?
Math.max(k.lineThickness,4):0;var e=k._colorSet,q=e=k.lineColor=k.options.lineColor?k.options.lineColor:e[0];c.strokeStyle=e;var r=!0,s=0,x,u;c.beginPath();if(0<h.length){for(var v=!1,s=0;s<h.length;s++)if(x=h[s].x.getTime?h[s].x.getTime():h[s].x,!(x<a.axisX.dataInfo.viewPortMin||x>a.axisX.dataInfo.viewPortMax&&(!k.connectNullData||!v)))if("number"!==typeof h[s].y)0<s&&!(k.connectNullData||v||r)&&(c.stroke(),t&&b.stroke()),v=!0;else{x=a.axisX.convertValueToPixel(x);u=a.axisY.convertValueToPixel(h[s].y);
var z=k.dataPointIds[s];this._eventManager.objectMap[z]={id:z,objectType:"dataPoint",dataSeriesIndex:l,dataPointIndex:s,x1:x,y1:u};r||v?(!r&&k.connectNullData?(c.setLineDash&&(k.options.nullDataLineDashType||m===k.lineDashType&&k.lineDashType!==k.nullDataLineDashType)&&(c.stroke(),c.beginPath(),c.moveTo(f.x,f.y),m=k.nullDataLineDashType,c.setLineDash(p)),c.lineTo(x,u),t&&b.lineTo(x,u)):(c.beginPath(),c.moveTo(x,u),t&&(b.beginPath(),b.moveTo(x,u))),v=r=!1):(c.lineTo(x,u),t&&b.lineTo(x,u),0==s%500&&
(c.stroke(),c.beginPath(),c.moveTo(x,u),t&&(b.stroke(),b.beginPath(),b.moveTo(x,u))));f={x:x,y:u};s<h.length-1&&(q!==(h[s].lineColor||e)||m!==(h[s].lineDashType||k.lineDashType))&&(c.stroke(),c.beginPath(),c.moveTo(x,u),q=h[s].lineColor||e,c.strokeStyle=q,c.setLineDash&&(h[s].lineDashType?(m=h[s].lineDashType,c.setLineDash(F(m,k.lineThickness))):(m=k.lineDashType,c.setLineDash(n))));if(0<h[s].markerSize||0<k.markerSize){var w=k.getMarkerProperties(s,x,u,c);d.push(w);z=D(z);t&&d.push({x:x,y:u,ctx:b,
type:w.type,size:w.size,color:z,borderColor:z,borderThickness:w.borderThickness})}(h[s].indexLabel||k.indexLabel||h[s].indexLabelFormatter||k.indexLabelFormatter)&&this._indexLabels.push({chartType:"line",dataPoint:h[s],dataSeries:k,point:{x:x,y:u},direction:0>h[s].y===a.axisY.reversed?1:-1,color:e})}c.stroke();t&&b.stroke()}}O.drawMarkers(d);c.restore();c.beginPath();t&&b.beginPath();return{source:c,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};
A.prototype.renderStepLine=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=this._eventManager.ghostCtx;c.save();var d=this.plotArea;c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();for(var d=[],f,g=0;g<a.dataSeriesIndexes.length;g++){var l=a.dataSeriesIndexes[g],k=this.data[l];c.lineWidth=k.lineThickness;var h=k.dataPoints,m="solid";if(c.setLineDash){var p=F(k.nullDataLineDashType,k.lineThickness),m=k.lineDashType,n=F(m,k.lineThickness);c.setLineDash(n)}var e=
k.id;this._eventManager.objectMap[e]={objectType:"dataSeries",dataSeriesIndex:l};e=D(e);b.strokeStyle=e;b.lineWidth=0<k.lineThickness?Math.max(k.lineThickness,4):0;var e=k._colorSet,q=e=k.lineColor=k.options.lineColor?k.options.lineColor:e[0];c.strokeStyle=e;var r=!0,s=0,x,u;c.beginPath();if(0<h.length){for(var v=!1,s=0;s<h.length;s++)if(x=h[s].getTime?h[s].x.getTime():h[s].x,!(x<a.axisX.dataInfo.viewPortMin||x>a.axisX.dataInfo.viewPortMax&&(!k.connectNullData||!v)))if("number"!==typeof h[s].y)0<
s&&!(k.connectNullData||v||r)&&(c.stroke(),t&&b.stroke()),v=!0;else{var z=u;x=a.axisX.convertValueToPixel(x);u=a.axisY.convertValueToPixel(h[s].y);var w=k.dataPointIds[s];this._eventManager.objectMap[w]={id:w,objectType:"dataPoint",dataSeriesIndex:l,dataPointIndex:s,x1:x,y1:u};r||v?(!r&&k.connectNullData?(c.setLineDash&&(k.options.nullDataLineDashType||m===k.lineDashType&&k.lineDashType!==k.nullDataLineDashType)&&(c.stroke(),c.beginPath(),c.moveTo(f.x,f.y),m=k.nullDataLineDashType,c.setLineDash(p)),
c.lineTo(x,z),c.lineTo(x,u),t&&(b.lineTo(x,z),b.lineTo(x,u))):(c.beginPath(),c.moveTo(x,u),t&&(b.beginPath(),b.moveTo(x,u))),v=r=!1):(c.lineTo(x,z),t&&b.lineTo(x,z),c.lineTo(x,u),t&&b.lineTo(x,u),0==s%500&&(c.stroke(),c.beginPath(),c.moveTo(x,u),t&&(b.stroke(),b.beginPath(),b.moveTo(x,u))));f={x:x,y:u};s<h.length-1&&(q!==(h[s].lineColor||e)||m!==(h[s].lineDashType||k.lineDashType))&&(c.stroke(),c.beginPath(),c.moveTo(x,u),q=h[s].lineColor||e,c.strokeStyle=q,c.setLineDash&&(h[s].lineDashType?(m=h[s].lineDashType,
c.setLineDash(F(m,k.lineThickness))):(m=k.lineDashType,c.setLineDash(n))));if(0<h[s].markerSize||0<k.markerSize)z=k.getMarkerProperties(s,x,u,c),d.push(z),w=D(w),t&&d.push({x:x,y:u,ctx:b,type:z.type,size:z.size,color:w,borderColor:w,borderThickness:z.borderThickness});(h[s].indexLabel||k.indexLabel||h[s].indexLabelFormatter||k.indexLabelFormatter)&&this._indexLabels.push({chartType:"stepLine",dataPoint:h[s],dataSeries:k,point:{x:x,y:u},direction:0>h[s].y===a.axisY.reversed?1:-1,color:e})}c.stroke();
t&&b.stroke()}}O.drawMarkers(d);c.restore();c.beginPath();t&&b.beginPath();return{source:c,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};A.prototype.renderSpline=function(a){function c(a){a=wa(a,2);if(0<a.length){b.beginPath();t&&d.beginPath();b.moveTo(a[0].x,a[0].y);a[0].newStrokeStyle&&(b.strokeStyle=a[0].newStrokeStyle);a[0].newLineDashArray&&b.setLineDash(a[0].newLineDashArray);t&&d.moveTo(a[0].x,a[0].y);for(var c=0;c<a.length-3;c+=
3)if(b.bezierCurveTo(a[c+1].x,a[c+1].y,a[c+2].x,a[c+2].y,a[c+3].x,a[c+3].y),t&&d.bezierCurveTo(a[c+1].x,a[c+1].y,a[c+2].x,a[c+2].y,a[c+3].x,a[c+3].y),0<c&&0===c%3E3||a[c+3].newStrokeStyle||a[c+3].newLineDashArray)b.stroke(),b.beginPath(),b.moveTo(a[c+3].x,a[c+3].y),a[c+3].newStrokeStyle&&(b.strokeStyle=a[c+3].newStrokeStyle),a[c+3].newLineDashArray&&b.setLineDash(a[c+3].newLineDashArray),t&&(d.stroke(),d.beginPath(),d.moveTo(a[c+3].x,a[c+3].y));b.stroke();t&&d.stroke()}}var b=a.targetCanvasCtx||this.plotArea.ctx;
if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx;b.save();var f=this.plotArea;b.beginPath();b.rect(f.x1,f.y1,f.width,f.height);b.clip();for(var f=[],g=0;g<a.dataSeriesIndexes.length;g++){var l=a.dataSeriesIndexes[g],k=this.data[l];b.lineWidth=k.lineThickness;var h=k.dataPoints,m="solid";if(b.setLineDash){var p=F(k.nullDataLineDashType,k.lineThickness),m=k.lineDashType,n=F(m,k.lineThickness);b.setLineDash(n)}var e=k.id;this._eventManager.objectMap[e]={objectType:"dataSeries",
dataSeriesIndex:l};e=D(e);d.strokeStyle=e;d.lineWidth=0<k.lineThickness?Math.max(k.lineThickness,4):0;var e=k._colorSet,q=e=k.lineColor=k.options.lineColor?k.options.lineColor:e[0];b.strokeStyle=e;var r=0,s,x,u=[];b.beginPath();if(0<h.length)for(x=!1,r=0;r<h.length;r++)if(s=h[r].getTime?h[r].x.getTime():h[r].x,!(s<a.axisX.dataInfo.viewPortMin||s>a.axisX.dataInfo.viewPortMax&&(!k.connectNullData||!x)))if("number"!==typeof h[r].y)0<r&&!x&&(k.connectNullData?b.setLineDash&&(0<u.length&&(k.options.nullDataLineDashType||
!h[r-1].lineDashType))&&(u[u.length-1].newLineDashArray=p,m=k.nullDataLineDashType):(c(u),u=[])),x=!0;else{s=a.axisX.convertValueToPixel(s);x=a.axisY.convertValueToPixel(h[r].y);var v=k.dataPointIds[r];this._eventManager.objectMap[v]={id:v,objectType:"dataPoint",dataSeriesIndex:l,dataPointIndex:r,x1:s,y1:x};u[u.length]={x:s,y:x};r<h.length-1&&(q!==(h[r].lineColor||e)||m!==(h[r].lineDashType||k.lineDashType))&&(q=h[r].lineColor||e,u[u.length-1].newStrokeStyle=q,b.setLineDash&&(h[r].lineDashType?(m=
h[r].lineDashType,u[u.length-1].newLineDashArray=F(m,k.lineThickness)):(m=k.lineDashType,u[u.length-1].newLineDashArray=n)));if(0<h[r].markerSize||0<k.markerSize){var z=k.getMarkerProperties(r,s,x,b);f.push(z);v=D(v);t&&f.push({x:s,y:x,ctx:d,type:z.type,size:z.size,color:v,borderColor:v,borderThickness:z.borderThickness})}(h[r].indexLabel||k.indexLabel||h[r].indexLabelFormatter||k.indexLabelFormatter)&&this._indexLabels.push({chartType:"spline",dataPoint:h[r],dataSeries:k,point:{x:s,y:x},direction:0>
h[r].y===a.axisY.reversed?1:-1,color:e});x=!1}c(u)}O.drawMarkers(f);b.restore();b.beginPath();t&&d.beginPath();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};var N=function(a,c,b,d,f,g,l,k,h,m,p,n,e){"undefined"===typeof e&&(e=1);l=l||0;k=k||"black";var q=15<d-c&&15<f-b?8:0.35*Math.min(d-c,f-b);a.beginPath();a.moveTo(c,b);a.save();a.fillStyle=g;a.globalAlpha=e;a.fillRect(c,b,d-c,f-b);a.globalAlpha=1;0<l&&(e=0===l%2?0:0.5,
a.beginPath(),a.lineWidth=l,a.strokeStyle=k,a.moveTo(c,b),a.rect(c-e,b-e,d-c+2*e,f-b+2*e),a.stroke());a.restore();!0===h&&(a.save(),a.beginPath(),a.moveTo(c,b),a.lineTo(c+q,b+q),a.lineTo(d-q,b+q),a.lineTo(d,b),a.closePath(),l=a.createLinearGradient((d+c)/2,b+q,(d+c)/2,b),l.addColorStop(0,g),l.addColorStop(1,"rgba(255, 255, 255, .4)"),a.fillStyle=l,a.fill(),a.restore());!0===m&&(a.save(),a.beginPath(),a.moveTo(c,f),a.lineTo(c+q,f-q),a.lineTo(d-q,f-q),a.lineTo(d,f),a.closePath(),l=a.createLinearGradient((d+
c)/2,f-q,(d+c)/2,f),l.addColorStop(0,g),l.addColorStop(1,"rgba(255, 255, 255, .4)"),a.fillStyle=l,a.fill(),a.restore());!0===p&&(a.save(),a.beginPath(),a.moveTo(c,b),a.lineTo(c+q,b+q),a.lineTo(c+q,f-q),a.lineTo(c,f),a.closePath(),l=a.createLinearGradient(c+q,(f+b)/2,c,(f+b)/2),l.addColorStop(0,g),l.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=l,a.fill(),a.restore());!0===n&&(a.save(),a.beginPath(),a.moveTo(d,b),a.lineTo(d-q,b+q),a.lineTo(d-q,f-q),a.lineTo(d,f),l=a.createLinearGradient(d-
q,(f+b)/2,d,(f+b)/2),l.addColorStop(0,g),l.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=l,l.addColorStop(0,g),l.addColorStop(1,"rgba(255, 255, 255, 0.1)"),a.fillStyle=l,a.fill(),a.closePath(),a.restore())};A.prototype.renderColumn=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=0,g,l,k,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),f=this.dataPointMinWidth=this.dataPointMinWidth?this.dataPointMinWidth:
this.dataPointWidth?this.dataPointWidth:1,m=this.dataPointMaxWidth=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.width,0.9*(this.plotArea.width/a.plotType.totalDataSeries))<<0,p=a.axisX.dataInfo.minDiff;isFinite(p)||(p=0.3*Math.abs(a.axisX.range));p=this.dataPointWidth=this.dataPointWidth?this.dataPointWidth:0.9*(d.width*(a.axisX.logarithmic?Math.log(p)/Math.log(a.axisX.range):Math.abs(p)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<
0;this.dataPointMaxWidth&&f>m&&(f=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&m<f)&&(m=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,f));p<f&&(p=f);p>m&&(p=m);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(d=0;d<a.dataSeriesIndexes.length;d++){var m=
a.dataSeriesIndexes[d],n=this.data[m],e=n.dataPoints;if(0<e.length)for(var q=5<p&&n.bevelEnabled?!0:!1,f=0;f<e.length;f++)if(e[f].getTime?k=e[f].x.getTime():k=e[f].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&"number"===typeof e[f].y){g=a.axisX.convertValueToPixel(k);l=a.axisY.convertValueToPixel(e[f].y);g=a.axisX.reversed?g+a.plotType.totalDataSeries*p/2-(a.previousDataSeriesCount+d)*p<<0:g-a.plotType.totalDataSeries*p/2+(a.previousDataSeriesCount+d)*p<<0;var r=a.axisX.reversed?
g-p<<0:g+p<<0,s;0<=e[f].y?s=h:(s=l,l=h);l>s&&(b=l,l=s,s=b);b=e[f].color?e[f].color:n._colorSet[f%n._colorSet.length];N(c,g,l,r,s,b,0,null,q&&0<=e[f].y,0>e[f].y&&q,!1,!1,n.fillOpacity);b=n.dataPointIds[f];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:m,dataPointIndex:f,x1:g,y1:l,x2:r,y2:s};b=D(b);t&&N(this._eventManager.ghostCtx,g,l,r,s,b,0,null,!1,!1,!1,!1);(e[f].indexLabel||n.indexLabel||e[f].indexLabelFormatter||n.indexLabelFormatter)&&this._indexLabels.push({chartType:"column",
dataPoint:e[f],dataSeries:n,point:{x:g+(r-g)/2,y:0>e[f].y===a.axisY.reversed?l:s},direction:0>e[f].y===a.axisY.reversed?1:-1,bounds:{x1:g,y1:Math.min(l,s),x2:r,y2:Math.max(l,s)},color:b})}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.yScaleAnimation,easingFunction:E.easing.easeOutQuart,animationBase:h<a.axisY.bounds.y1?a.axisY.bounds.y1:h>a.axisY.bounds.y2?a.axisY.bounds.y2:h}}};A.prototype.renderStackedColumn=function(a){var c=a.targetCanvasCtx||
this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=[],g=[],l=[],k=0,h,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),k=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1,p=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:0.15*this.width<<0,n=a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.dataPointWidth?this.dataPointWidth:0.9*(d.width*
(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&k>p&&(k=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,p));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&p<k)&&(p=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,k));n<k&&(n=k);n>p&&(n=p);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),
this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(p=0;p<a.dataSeriesIndexes.length;p++){var e=a.dataSeriesIndexes[p],q=this.data[e],r=q.dataPoints;if(0<r.length){var s=5<n&&q.bevelEnabled?!0:!1;c.strokeStyle="#4572A7 ";for(k=0;k<r.length;k++)if(b=r[k].x.getTime?r[k].x.getTime():r[k].x,!(b<a.axisX.dataInfo.viewPortMin||b>a.axisX.dataInfo.viewPortMax)&&"number"===typeof r[k].y){var d=a.axisX.convertValueToPixel(b),x=d-a.plotType.plotUnits.length*n/
2+a.index*n<<0,u=x+n<<0,v;if(a.axisY.logarithmic)l[b]=r[k].y+(l[b]?l[b]:0),0<l[b]&&(h=a.axisY.convertValueToPixel(l[b]),v=f[b]?f[b]:m,f[b]=h);else if(h=a.axisY.convertValueToPixel(r[k].y),0<=r[k].y){var z=f[b]?f[b]:0;h-=z;v=m-z;f[b]=z+(v-h)}else z=g[b]?g[b]:0,v=h+z,h=m+z,g[b]=z+(v-h);b=r[k].color?r[k].color:q._colorSet[k%q._colorSet.length];N(c,x,h,u,v,b,0,null,s&&0<=r[k].y,0>r[k].y&&s,!1,!1,q.fillOpacity);b=q.dataPointIds[k];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:e,
dataPointIndex:k,x1:x,y1:h,x2:u,y2:v};b=D(b);t&&N(this._eventManager.ghostCtx,x,h,u,v,b,0,null,!1,!1,!1,!1);(r[k].indexLabel||q.indexLabel||r[k].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedColumn",dataPoint:r[k],dataSeries:q,point:{x:d,y:0<=r[k].y?h:v},direction:0>r[k].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(h,v),x2:u,y2:Math.max(h,v)},color:b})}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.yScaleAnimation,
easingFunction:E.easing.easeOutQuart,animationBase:m<a.axisY.bounds.y1?a.axisY.bounds.y1:m>a.axisY.bounds.y2?a.axisY.bounds.y2:m}}};A.prototype.renderStackedColumn100=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=[],g=[],l=[],k=0,h,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),k=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1,p=this.dataPointMaxWidth?this.dataPointMaxWidth:
this.dataPointWidth?this.dataPointWidth:0.15*this.width<<0,n=a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.dataPointWidth?this.dataPointWidth:0.9*(d.width*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&k>p&&(k=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,p));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&p<k)&&(p=Math.max(this.dataPointWidth?this.dataPointWidth:
-Infinity,k));n<k&&(n=k);n>p&&(n=p);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(p=0;p<a.dataSeriesIndexes.length;p++){var e=a.dataSeriesIndexes[p],q=this.data[e],r=q.dataPoints;if(0<r.length)for(var s=5<n&&q.bevelEnabled?!0:!1,k=0;k<r.length;k++)if(b=r[k].x.getTime?r[k].x.getTime():r[k].x,!(b<a.axisX.dataInfo.viewPortMin||
b>a.axisX.dataInfo.viewPortMax)&&"number"===typeof r[k].y){d=a.axisX.convertValueToPixel(b);h=0!==a.dataPointYSums[b]?100*(r[k].y/a.dataPointYSums[b]):0;var x=d-a.plotType.plotUnits.length*n/2+a.index*n<<0,u=x+n<<0,v;if(a.axisY.logarithmic){l[b]=h+(l[b]?l[b]:0);if(0>=l[b])continue;h=a.axisY.convertValueToPixel(l[b]);v=f[b]?f[b]:m;f[b]=h}else if(h=a.axisY.convertValueToPixel(h),0<=r[k].y){var z=f[b]?f[b]:0;h-=z;v=m-z;f[b]=z+(v-h)}else z=g[b]?g[b]:0,v=h+z,h=m+z,g[b]=z+(v-h);b=r[k].color?r[k].color:
q._colorSet[k%q._colorSet.length];N(c,x,h,u,v,b,0,null,s&&0<=r[k].y,0>r[k].y&&s,!1,!1,q.fillOpacity);b=q.dataPointIds[k];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:e,dataPointIndex:k,x1:x,y1:h,x2:u,y2:v};b=D(b);t&&N(this._eventManager.ghostCtx,x,h,u,v,b,0,null,!1,!1,!1,!1);(r[k].indexLabel||q.indexLabel||r[k].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedColumn100",dataPoint:r[k],dataSeries:q,point:{x:d,y:0<=r[k].y?h:v},direction:0>
r[k].y===a.axisY.reversed?1:-1,bounds:{x1:x,y1:Math.min(h,v),x2:u,y2:Math.max(h,v)},color:b})}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.yScaleAnimation,easingFunction:E.easing.easeOutQuart,animationBase:m<a.axisY.bounds.y1?a.axisY.bounds.y1:m>a.axisY.bounds.y2?a.axisY.bounds.y2:m}}};A.prototype.renderBar=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=0,g,
l,k,h=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),f=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1,m=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0,p=a.axisX.dataInfo.minDiff;isFinite(p)||(p=0.3*Math.abs(a.axisX.range));p=this.dataPointWidth?this.dataPointWidth:0.9*(d.height*(a.axisX.logarithmic?Math.log(p)/
Math.log(a.axisX.range):Math.abs(p)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&f>m&&(f=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,m));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&m<f)&&(m=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,f));p<f&&(p=f);p>m&&(p=m);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,
d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(d=0;d<a.dataSeriesIndexes.length;d++){var m=a.dataSeriesIndexes[d],n=this.data[m],e=n.dataPoints;if(0<e.length){var q=5<p&&n.bevelEnabled?!0:!1;c.strokeStyle="#4572A7 ";for(f=0;f<e.length;f++)if(e[f].getTime?k=e[f].x.getTime():k=e[f].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&"number"===typeof e[f].y){l=a.axisX.convertValueToPixel(k);g=a.axisY.convertValueToPixel(e[f].y);l=a.axisX.reversed?l+a.plotType.totalDataSeries*
p/2-(a.previousDataSeriesCount+d)*p<<0:l-a.plotType.totalDataSeries*p/2+(a.previousDataSeriesCount+d)*p<<0;var r=a.axisX.reversed?l-p<<0:l+p<<0,s;0<=e[f].y?s=h:(s=g,g=h);b=e[f].color?e[f].color:n._colorSet[f%n._colorSet.length];N(c,s,l,g,r,b,0,null,q,!1,!1,!1,n.fillOpacity);b=n.dataPointIds[f];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:m,dataPointIndex:f,x1:s,y1:l,x2:g,y2:r};b=D(b);t&&N(this._eventManager.ghostCtx,s,l,g,r,b,0,null,!1,!1,!1,!1);(e[f].indexLabel||n.indexLabel||
e[f].indexLabelFormatter||n.indexLabelFormatter)&&this._indexLabels.push({chartType:"bar",dataPoint:e[f],dataSeries:n,point:{x:0<=e[f].y?g:s,y:l+(r-l)/2},direction:0>e[f].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(s,g),y1:l,x2:Math.max(s,g),y2:r},color:b})}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.xScaleAnimation,easingFunction:E.easing.easeOutQuart,animationBase:h<a.axisY.bounds.x1?a.axisY.bounds.x1:h>a.axisY.bounds.x2?a.axisY.bounds.x2:
h}}};A.prototype.renderStackedBar=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=[],g=[],l=[],k=0,h,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),k=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1,p=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:0.15*this.height<<0,n=a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));
n=this.dataPointWidth?this.dataPointWidth:0.9*(d.height*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&k>p&&(k=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,p));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&p<k)&&(p=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,k));n<k&&(n=k);n>p&&(n=p);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,
d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(p=0;p<a.dataSeriesIndexes.length;p++){var e=a.dataSeriesIndexes[p],q=this.data[e],r=q.dataPoints;if(0<r.length){var s=5<n&&q.bevelEnabled?!0:!1;c.strokeStyle="#4572A7 ";for(k=0;k<r.length;k++)if(b=r[k].x.getTime?r[k].x.getTime():r[k].x,!(b<a.axisX.dataInfo.viewPortMin||b>a.axisX.dataInfo.viewPortMax)&&"number"===typeof r[k].y){var d=a.axisX.convertValueToPixel(b),
x=d-a.plotType.plotUnits.length*n/2+a.index*n<<0,u=x+n<<0,v;if(a.axisY.logarithmic)l[b]=r[k].y+(l[b]?l[b]:0),0<l[b]&&(v=f[b]?f[b]:m,f[b]=h=a.axisY.convertValueToPixel(l[b]));else if(h=a.axisY.convertValueToPixel(r[k].y),0<=r[k].y){var z=f[b]?f[b]:0;v=m+z;h+=z;f[b]=z+(h-v)}else z=g[b]?g[b]:0,v=h-z,h=m-z,g[b]=z+(h-v);b=r[k].color?r[k].color:q._colorSet[k%q._colorSet.length];N(c,v,x,h,u,b,0,null,s,!1,!1,!1,q.fillOpacity);b=q.dataPointIds[k];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",
dataSeriesIndex:e,dataPointIndex:k,x1:v,y1:x,x2:h,y2:u};b=D(b);t&&N(this._eventManager.ghostCtx,v,x,h,u,b,0,null,!1,!1,!1,!1);(r[k].indexLabel||q.indexLabel||r[k].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar",dataPoint:r[k],dataSeries:q,point:{x:0<=r[k].y?h:v,y:d},direction:0>r[k].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(v,h),y1:x,x2:Math.max(v,h),y2:u},color:b})}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,
animationCallback:E.xScaleAnimation,easingFunction:E.easing.easeOutQuart,animationBase:m<a.axisY.bounds.x1?a.axisY.bounds.x1:m>a.axisY.bounds.x2?a.axisY.bounds.x2:m}}};A.prototype.renderStackedBar100=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=[],g=[],l=[],k=0,h,m=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),k=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:
1,p=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:0.15*this.height<<0,n=a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.dataPointWidth?this.dataPointWidth:0.9*(d.height*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))/a.plotType.plotUnits.length)<<0;this.dataPointMaxWidth&&k>p&&(k=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,p));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&
p<k)&&(p=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,k));n<k&&(n=k);n>p&&(n=p);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(p=0;p<a.dataSeriesIndexes.length;p++){var e=a.dataSeriesIndexes[p],q=this.data[e],r=q.dataPoints;if(0<r.length){var s=5<n&&q.bevelEnabled?!0:!1;c.strokeStyle=
"#4572A7 ";for(k=0;k<r.length;k++)if(b=r[k].x.getTime?r[k].x.getTime():r[k].x,!(b<a.axisX.dataInfo.viewPortMin||b>a.axisX.dataInfo.viewPortMax)&&"number"===typeof r[k].y){var d=a.axisX.convertValueToPixel(b),x;x=0!==a.dataPointYSums[b]?100*(r[k].y/a.dataPointYSums[b]):0;var u=d-a.plotType.plotUnits.length*n/2+a.index*n<<0,v=u+n<<0;if(a.axisY.logarithmic){l[b]=x+(l[b]?l[b]:0);if(0>=l[b])continue;x=f[b]?f[b]:m;f[b]=h=a.axisY.convertValueToPixel(l[b])}else if(h=a.axisY.convertValueToPixel(x),0<=r[k].y){var z=
f[b]?f[b]:0;x=m+z;h+=z;f[b]=z+(h-x)}else z=g[b]?g[b]:0,x=h-z,h=m-z,g[b]=z+(h-x);b=r[k].color?r[k].color:q._colorSet[k%q._colorSet.length];N(c,x,u,h,v,b,0,null,s,!1,!1,!1,q.fillOpacity);b=q.dataPointIds[k];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:e,dataPointIndex:k,x1:x,y1:u,x2:h,y2:v};b=D(b);t&&N(this._eventManager.ghostCtx,x,u,h,v,b,0,null,!1,!1,!1,!1);(r[k].indexLabel||q.indexLabel||r[k].indexLabelFormatter||q.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedBar100",
dataPoint:r[k],dataSeries:q,point:{x:0<=r[k].y?h:x,y:d},direction:0>r[k].y===a.axisY.reversed?1:-1,bounds:{x1:Math.min(x,h),y1:u,x2:Math.max(x,h),y2:v},color:b})}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.xScaleAnimation,easingFunction:E.easing.easeOutQuart,animationBase:m<a.axisY.bounds.x1?a.axisY.bounds.x1:m>a.axisY.bounds.x2?a.axisY.bounds.x2:m}}};A.prototype.renderArea=function(a){var c,b;function d(){w&&(0<e.lineThickness&&
f.stroke(),a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?z=v:0>a.axisY.viewportMaximum?z=k.y1:0<a.axisY.viewportMinimum&&(z=l.y2),f.lineTo(s,z),f.lineTo(w.x,z),f.closePath(),f.globalAlpha=e.fillOpacity,f.fill(),f.globalAlpha=1,t&&(g.lineTo(s,z),g.lineTo(w.x,z),g.closePath(),g.fill()),f.beginPath(),f.moveTo(s,x),g.beginPath(),g.moveTo(s,x),w={x:s,y:x})}var f=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var g=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,
k=a.axisY.lineCoordinates,h=[],m=this.plotArea,p;f.save();t&&g.save();f.beginPath();f.rect(m.x1,m.y1,m.width,m.height);f.clip();t&&(g.beginPath(),g.rect(m.x1,m.y1,m.width,m.height),g.clip());for(m=0;m<a.dataSeriesIndexes.length;m++){var n=a.dataSeriesIndexes[m],e=this.data[n],q=e.dataPoints,h=e.id;this._eventManager.objectMap[h]={objectType:"dataSeries",dataSeriesIndex:n};h=D(h);g.fillStyle=h;h=[];c=!0;var r=0,s,x,u,v=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),z,w=
null;if(0<q.length){var y=e._colorSet[r%e._colorSet.length],ba=e.lineColor=e.options.lineColor||y,A=ba;f.fillStyle=y;f.strokeStyle=ba;f.lineWidth=e.lineThickness;b="solid";if(f.setLineDash){var B=F(e.nullDataLineDashType,e.lineThickness);b=e.lineDashType;var W=F(b,e.lineThickness);f.setLineDash(W)}for(var X=!0;r<q.length;r++)if(u=q[r].x.getTime?q[r].x.getTime():q[r].x,!(u<a.axisX.dataInfo.viewPortMin||u>a.axisX.dataInfo.viewPortMax&&(!e.connectNullData||!X)))if("number"!==typeof q[r].y)e.connectNullData||
(X||c)||d(),X=!0;else{s=a.axisX.convertValueToPixel(u);x=a.axisY.convertValueToPixel(q[r].y);c||X?(!c&&e.connectNullData?(f.setLineDash&&(e.options.nullDataLineDashType||b===e.lineDashType&&e.lineDashType!==e.nullDataLineDashType)&&(c=s,b=x,s=p.x,x=p.y,d(),f.moveTo(p.x,p.y),s=c,x=b,w=p,b=e.nullDataLineDashType,f.setLineDash(B)),f.lineTo(s,x),t&&g.lineTo(s,x)):(f.beginPath(),f.moveTo(s,x),t&&(g.beginPath(),g.moveTo(s,x)),w={x:s,y:x}),X=c=!1):(f.lineTo(s,x),t&&g.lineTo(s,x),0==r%250&&d());p={x:s,y:x};
r<q.length-1&&(A!==(q[r].lineColor||ba)||b!==(q[r].lineDashType||e.lineDashType))&&(d(),A=q[r].lineColor||ba,f.strokeStyle=A,f.setLineDash&&(q[r].lineDashType?(b=q[r].lineDashType,f.setLineDash(F(b,e.lineThickness))):(b=e.lineDashType,f.setLineDash(W))));var H=e.dataPointIds[r];this._eventManager.objectMap[H]={id:H,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:r,x1:s,y1:x};0!==q[r].markerSize&&(0<q[r].markerSize||0<e.markerSize)&&(u=e.getMarkerProperties(r,s,x,f),h.push(u),H=D(H),t&&h.push({x:s,
y:x,ctx:g,type:u.type,size:u.size,color:H,borderColor:H,borderThickness:u.borderThickness}));(q[r].indexLabel||e.indexLabel||q[r].indexLabelFormatter||e.indexLabelFormatter)&&this._indexLabels.push({chartType:"area",dataPoint:q[r],dataSeries:e,point:{x:s,y:x},direction:0>q[r].y===a.axisY.reversed?1:-1,color:y})}d();O.drawMarkers(h)}}f.restore();t&&this._eventManager.ghostCtx.restore();return{source:f,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};
A.prototype.renderSplineArea=function(a){function c(){var c=wa(u,2);if(0<c.length){if(0<m.lineThickness){b.beginPath();b.moveTo(c[0].x,c[0].y);c[0].newStrokeStyle&&(b.strokeStyle=c[0].newStrokeStyle);c[0].newLineDashArray&&b.setLineDash(c[0].newLineDashArray);for(var e=0;e<c.length-3;e+=3)if(b.bezierCurveTo(c[e+1].x,c[e+1].y,c[e+2].x,c[e+2].y,c[e+3].x,c[e+3].y),t&&d.bezierCurveTo(c[e+1].x,c[e+1].y,c[e+2].x,c[e+2].y,c[e+3].x,c[e+3].y),c[e+3].newStrokeStyle||c[e+3].newLineDashArray)b.stroke(),b.beginPath(),
b.moveTo(c[e+3].x,c[e+3].y),c[e+3].newStrokeStyle&&(b.strokeStyle=c[e+3].newStrokeStyle),c[e+3].newLineDashArray&&b.setLineDash(c[e+3].newLineDashArray);b.stroke()}b.beginPath();b.moveTo(c[0].x,c[0].y);t&&(d.beginPath(),d.moveTo(c[0].x,c[0].y));for(e=0;e<c.length-3;e+=3)b.bezierCurveTo(c[e+1].x,c[e+1].y,c[e+2].x,c[e+2].y,c[e+3].x,c[e+3].y),t&&d.bezierCurveTo(c[e+1].x,c[e+1].y,c[e+2].x,c[e+2].y,c[e+3].x,c[e+3].y);a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?s=r:0>a.axisY.viewportMaximum?
s=g.y1:0<a.axisY.viewportMinimum&&(s=f.y2);x={x:c[0].x,y:c[0].y};b.lineTo(c[c.length-1].x,s);b.lineTo(x.x,s);b.closePath();b.globalAlpha=m.fillOpacity;b.fill();b.globalAlpha=1;t&&(d.lineTo(c[c.length-1].x,s),d.lineTo(x.x,s),d.closePath(),d.fill())}}var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,f=a.axisX.lineCoordinates,g=a.axisY.lineCoordinates,l=[],k=this.plotArea;b.save();t&&d.save();b.beginPath();b.rect(k.x1,k.y1,k.width,k.height);
b.clip();t&&(d.beginPath(),d.rect(k.x1,k.y1,k.width,k.height),d.clip());for(k=0;k<a.dataSeriesIndexes.length;k++){var h=a.dataSeriesIndexes[k],m=this.data[h],p=m.dataPoints,l=m.id;this._eventManager.objectMap[l]={objectType:"dataSeries",dataSeriesIndex:h};l=D(l);d.fillStyle=l;var l=[],n=0,e,q,r=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),s,x=null,u=[];if(0<p.length){var v=m._colorSet[n%m._colorSet.length],z=m.lineColor=m.options.lineColor||v,w=z;b.fillStyle=v;b.strokeStyle=
z;b.lineWidth=m.lineThickness;var y="solid";if(b.setLineDash){var A=F(m.nullDataLineDashType,m.lineThickness),y=m.lineDashType,B=F(y,m.lineThickness);b.setLineDash(B)}for(q=!1;n<p.length;n++)if(e=p[n].x.getTime?p[n].x.getTime():p[n].x,!(e<a.axisX.dataInfo.viewPortMin||e>a.axisX.dataInfo.viewPortMax&&(!m.connectNullData||!q)))if("number"!==typeof p[n].y)0<n&&!q&&(m.connectNullData?b.setLineDash&&(0<u.length&&(m.options.nullDataLineDashType||!p[n-1].lineDashType))&&(u[u.length-1].newLineDashArray=A,
y=m.nullDataLineDashType):(c(),u=[])),q=!0;else{e=a.axisX.convertValueToPixel(e);q=a.axisY.convertValueToPixel(p[n].y);var M=m.dataPointIds[n];this._eventManager.objectMap[M]={id:M,objectType:"dataPoint",dataSeriesIndex:h,dataPointIndex:n,x1:e,y1:q};u[u.length]={x:e,y:q};n<p.length-1&&(w!==(p[n].lineColor||z)||y!==(p[n].lineDashType||m.lineDashType))&&(w=p[n].lineColor||z,u[u.length-1].newStrokeStyle=w,b.setLineDash&&(p[n].lineDashType?(y=p[n].lineDashType,u[u.length-1].newLineDashArray=F(y,m.lineThickness)):
(y=m.lineDashType,u[u.length-1].newLineDashArray=B)));if(0!==p[n].markerSize&&(0<p[n].markerSize||0<m.markerSize)){var W=m.getMarkerProperties(n,e,q,b);l.push(W);M=D(M);t&&l.push({x:e,y:q,ctx:d,type:W.type,size:W.size,color:M,borderColor:M,borderThickness:W.borderThickness})}(p[n].indexLabel||m.indexLabel||p[n].indexLabelFormatter||m.indexLabelFormatter)&&this._indexLabels.push({chartType:"splineArea",dataPoint:p[n],dataSeries:m,point:{x:e,y:q},direction:0>p[n].y===a.axisY.reversed?1:-1,color:v});
q=!1}c();O.drawMarkers(l)}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};A.prototype.renderStepArea=function(a){var c,b;function d(){w&&(0<e.lineThickness&&f.stroke(),a.axisY.logarithmic||0>=a.axisY.viewportMinimum&&0<=a.axisY.viewportMaximum?z=v:0>a.axisY.viewportMaximum?z=k.y1:0<a.axisY.viewportMinimum&&(z=l.y2),f.lineTo(s,z),f.lineTo(w.x,z),f.closePath(),f.globalAlpha=
e.fillOpacity,f.fill(),f.globalAlpha=1,t&&(g.lineTo(s,z),g.lineTo(w.x,z),g.closePath(),g.fill()),f.beginPath(),f.moveTo(s,x),g.beginPath(),g.moveTo(s,x),w={x:s,y:x})}var f=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var g=this._eventManager.ghostCtx,l=a.axisX.lineCoordinates,k=a.axisY.lineCoordinates,h=[],m=this.plotArea,p;f.save();t&&g.save();f.beginPath();f.rect(m.x1,m.y1,m.width,m.height);f.clip();t&&(g.beginPath(),g.rect(m.x1,m.y1,m.width,m.height),g.clip());for(m=
0;m<a.dataSeriesIndexes.length;m++){var n=a.dataSeriesIndexes[m],e=this.data[n],q=e.dataPoints,h=e.id;this._eventManager.objectMap[h]={objectType:"dataSeries",dataSeriesIndex:n};h=D(h);g.fillStyle=h;h=[];c=!0;var r=0,s,x,u,v=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),z,w=null;b=!1;if(0<q.length){var y=e._colorSet[r%e._colorSet.length],A=e.lineColor=e.options.lineColor||y,B=A;f.fillStyle=y;f.strokeStyle=A;f.lineWidth=e.lineThickness;var M="solid";if(f.setLineDash){var W=
F(e.nullDataLineDashType,e.lineThickness),M=e.lineDashType,X=F(M,e.lineThickness);f.setLineDash(X)}for(;r<q.length;r++)if(u=q[r].x.getTime?q[r].x.getTime():q[r].x,!(u<a.axisX.dataInfo.viewPortMin||u>a.axisX.dataInfo.viewPortMax&&(!e.connectNullData||!b))){var H=x;"number"!==typeof q[r].y?(e.connectNullData||(b||c)||d(),b=!0):(s=a.axisX.convertValueToPixel(u),x=a.axisY.convertValueToPixel(q[r].y),c||b?(!c&&e.connectNullData?(f.setLineDash&&(e.options.nullDataLineDashType||M===e.lineDashType&&e.lineDashType!==
e.nullDataLineDashType)&&(c=s,b=x,s=p.x,x=p.y,d(),f.moveTo(p.x,p.y),s=c,x=b,w=p,M=e.nullDataLineDashType,f.setLineDash(W)),f.lineTo(s,H),f.lineTo(s,x),t&&(g.lineTo(s,H),g.lineTo(s,x))):(f.beginPath(),f.moveTo(s,x),t&&(g.beginPath(),g.moveTo(s,x)),w={x:s,y:x}),b=c=!1):(f.lineTo(s,H),t&&g.lineTo(s,H),f.lineTo(s,x),t&&g.lineTo(s,x),0==r%250&&d()),p={x:s,y:x},r<q.length-1&&(B!==(q[r].lineColor||A)||M!==(q[r].lineDashType||e.lineDashType))&&(d(),B=q[r].lineColor||A,f.strokeStyle=B,f.setLineDash&&(q[r].lineDashType?
(M=q[r].lineDashType,f.setLineDash(F(M,e.lineThickness))):(M=e.lineDashType,f.setLineDash(X)))),u=e.dataPointIds[r],this._eventManager.objectMap[u]={id:u,objectType:"dataPoint",dataSeriesIndex:n,dataPointIndex:r,x1:s,y1:x},0!==q[r].markerSize&&(0<q[r].markerSize||0<e.markerSize)&&(H=e.getMarkerProperties(r,s,x,f),h.push(H),u=D(u),t&&h.push({x:s,y:x,ctx:g,type:H.type,size:H.size,color:u,borderColor:u,borderThickness:H.borderThickness})),(q[r].indexLabel||e.indexLabel||q[r].indexLabelFormatter||e.indexLabelFormatter)&&
this._indexLabels.push({chartType:"stepArea",dataPoint:q[r],dataSeries:e,point:{x:s,y:x},direction:0>q[r].y===a.axisY.reversed?1:-1,color:y}))}d();O.drawMarkers(h)}}f.restore();t&&this._eventManager.ghostCtx.restore();return{source:f,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};A.prototype.renderStackedArea=function(a){function c(){if(!(1>k.length)){for(0<w.lineThickness&&b.stroke();0<k.length;){var a=k.pop();b.lineTo(a.x,a.y);t&&s.lineTo(a.x,
a.y)}b.closePath();b.globalAlpha=w.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();t&&(s.closePath(),s.fill(),s.beginPath());k=[]}}var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=null,f=[],g=this.plotArea,l=[],k=[],h=[],m=[],p=0,n,e,q,r=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),s=this._eventManager.ghostCtx,x,u,v;t&&s.beginPath();b.save();t&&s.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();t&&(s.beginPath(),
s.rect(g.x1,g.y1,g.width,g.height),s.clip());d=[];for(g=0;g<a.dataSeriesIndexes.length;g++){var z=a.dataSeriesIndexes[g],w=this.data[z],y=w.dataPoints;w.dataPointIndexes=[];for(p=0;p<y.length;p++)z=y[p].x.getTime?y[p].x.getTime():y[p].x,w.dataPointIndexes[z]=p,d[z]||(h.push(z),d[z]=!0);h.sort(Aa)}for(g=0;g<a.dataSeriesIndexes.length;g++){z=a.dataSeriesIndexes[g];w=this.data[z];y=w.dataPoints;u=!0;k=[];p=w.id;this._eventManager.objectMap[p]={objectType:"dataSeries",dataSeriesIndex:z};p=D(p);s.fillStyle=
p;if(0<h.length){var d=w._colorSet[0],A=w.lineColor=w.options.lineColor||d,B=A;b.fillStyle=d;b.strokeStyle=A;b.lineWidth=w.lineThickness;v="solid";if(b.setLineDash){var M=F(w.nullDataLineDashType,w.lineThickness);v=w.lineDashType;var W=F(v,w.lineThickness);b.setLineDash(W)}for(var X=!0,p=0;p<h.length;p++){q=h[p];var H=null,H=0<=w.dataPointIndexes[q]?y[w.dataPointIndexes[q]]:{x:q,y:null};if(!(q<a.axisX.dataInfo.viewPortMin||q>a.axisX.dataInfo.viewPortMax&&(!w.connectNullData||!X)))if("number"!==typeof H.y)w.connectNullData||
(X||u)||c(),X=!0;else{n=a.axisX.convertValueToPixel(q);var ga=l[q]?l[q]:0;if(a.axisY.logarithmic){m[q]=H.y+(m[q]?m[q]:0);if(0>=m[q])continue;e=a.axisY.convertValueToPixel(m[q])}else e=a.axisY.convertValueToPixel(H.y),e-=ga;k.push({x:n,y:r-ga});l[q]=r-e;u||X?(!u&&w.connectNullData?(b.setLineDash&&(w.options.nullDataLineDashType||v===w.lineDashType&&w.lineDashType!==w.nullDataLineDashType)&&(u=k.pop(),v=k[k.length-1],c(),b.moveTo(x.x,x.y),k.push(v),k.push(u),v=w.nullDataLineDashType,b.setLineDash(M)),
b.lineTo(n,e),t&&s.lineTo(n,e)):(b.beginPath(),b.moveTo(n,e),t&&(s.beginPath(),s.moveTo(n,e))),X=u=!1):(b.lineTo(n,e),t&&s.lineTo(n,e),0==p%250&&(c(),b.moveTo(n,e),t&&s.moveTo(n,e),k.push({x:n,y:r-ga})));x={x:n,y:e};p<y.length-1&&(B!==(y[p].lineColor||A)||v!==(y[p].lineDashType||w.lineDashType))&&(c(),b.beginPath(),b.moveTo(n,e),k.push({x:n,y:r-ga}),B=y[p].lineColor||A,b.strokeStyle=B,b.setLineDash&&(y[p].lineDashType?(v=y[p].lineDashType,b.setLineDash(F(v,w.lineThickness))):(v=w.lineDashType,b.setLineDash(W))));
if(0<=w.dataPointIndexes[q]){var ha=w.dataPointIds[w.dataPointIndexes[q]];this._eventManager.objectMap[ha]={id:ha,objectType:"dataPoint",dataSeriesIndex:z,dataPointIndex:w.dataPointIndexes[q],x1:n,y1:e}}0<=w.dataPointIndexes[q]&&0!==H.markerSize&&(0<H.markerSize||0<w.markerSize)&&(q=w.getMarkerProperties(w.dataPointIndexes[q],n,e,b),f.push(q),markerColor=D(ha),t&&f.push({x:n,y:e,ctx:s,type:q.type,size:q.size,color:markerColor,borderColor:markerColor,borderThickness:q.borderThickness}));(H.indexLabel||
w.indexLabel||H.indexLabelFormatter||w.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedArea",dataPoint:H,dataSeries:w,point:{x:n,y:e},direction:0>y[p].y===a.axisY.reversed?1:-1,color:d})}}c();b.moveTo(n,e);t&&s.moveTo(n,e)}delete w.dataPointIndexes}O.drawMarkers(f);b.restore();t&&s.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};A.prototype.renderStackedArea100=function(a){function c(){for(0<w.lineThickness&&
b.stroke();0<k.length;){var a=k.pop();b.lineTo(a.x,a.y);t&&v.lineTo(a.x,a.y)}b.closePath();b.globalAlpha=w.fillOpacity;b.fill();b.globalAlpha=1;b.beginPath();t&&(v.closePath(),v.fill(),v.beginPath());k=[]}var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=null,f=this.plotArea,g=[],l=[],k=[],h=[],m=[],p=0,n,e,q,r,s,x,u=a.axisY.convertValueToPixel(a.axisY.logarithmic?a.axisY.viewportMinimum:0),v=this._eventManager.ghostCtx;b.save();t&&v.save();b.beginPath();b.rect(f.x1,
f.y1,f.width,f.height);b.clip();t&&(v.beginPath(),v.rect(f.x1,f.y1,f.width,f.height),v.clip());d=[];for(f=0;f<a.dataSeriesIndexes.length;f++){var z=a.dataSeriesIndexes[f],w=this.data[z],y=w.dataPoints;w.dataPointIndexes=[];for(p=0;p<y.length;p++)z=y[p].x.getTime?y[p].x.getTime():y[p].x,w.dataPointIndexes[z]=p,d[z]||(h.push(z),d[z]=!0);h.sort(Aa)}for(f=0;f<a.dataSeriesIndexes.length;f++){z=a.dataSeriesIndexes[f];w=this.data[z];y=w.dataPoints;s=!0;d=w.id;this._eventManager.objectMap[d]={objectType:"dataSeries",
dataSeriesIndex:z};d=D(d);v.fillStyle=d;k=[];if(0<h.length){var d=w._colorSet[p%w._colorSet.length],A=w.lineColor=w.options.lineColor||d,B=A;b.fillStyle=d;b.strokeStyle=A;b.lineWidth=w.lineThickness;x="solid";if(b.setLineDash){var M=F(w.nullDataLineDashType,w.lineThickness);x=w.lineDashType;var W=F(x,w.lineThickness);b.setLineDash(W)}for(var X=!0,p=0;p<h.length;p++){q=h[p];var H=null,H=0<=w.dataPointIndexes[q]?y[w.dataPointIndexes[q]]:{x:q,y:null};if(!(q<a.axisX.dataInfo.viewPortMin||q>a.axisX.dataInfo.viewPortMax&&
(!w.connectNullData||!X)))if("number"!==typeof H.y)w.connectNullData||(X||s)||c(),X=!0;else{var ga;ga=0!==a.dataPointYSums[q]?100*(H.y/a.dataPointYSums[q]):0;n=a.axisX.convertValueToPixel(q);var ha=l[q]?l[q]:0;if(a.axisY.logarithmic){m[q]=ga+(m[q]?m[q]:0);if(0>=m[q])continue;e=a.axisY.convertValueToPixel(m[q])}else e=a.axisY.convertValueToPixel(ga),e-=ha;k.push({x:n,y:u-ha});l[q]=u-e;s||X?(!s&&w.connectNullData?(b.setLineDash&&(w.options.nullDataLineDashType||x===w.lineDashType&&w.lineDashType!==
w.nullDataLineDashType)&&(s=k.pop(),x=k[k.length-1],c(),b.moveTo(r.x,r.y),k.push(x),k.push(s),x=w.nullDataLineDashType,b.setLineDash(M)),b.lineTo(n,e),t&&v.lineTo(n,e)):(b.beginPath(),b.moveTo(n,e),t&&(v.beginPath(),v.moveTo(n,e))),X=s=!1):(b.lineTo(n,e),t&&v.lineTo(n,e),0==p%250&&(c(),b.moveTo(n,e),t&&v.moveTo(n,e),k.push({x:n,y:u-ha})));r={x:n,y:e};p<y.length-1&&(B!==(y[p].lineColor||A)||x!==(y[p].lineDashType||w.lineDashType))&&(c(),b.beginPath(),b.moveTo(n,e),k.push({x:n,y:u-ha}),B=y[p].lineColor||
A,b.strokeStyle=B,b.setLineDash&&(y[p].lineDashType?(x=y[p].lineDashType,b.setLineDash(F(x,w.lineThickness))):(x=w.lineDashType,b.setLineDash(W))));if(0<=w.dataPointIndexes[q]){var Ga=w.dataPointIds[w.dataPointIndexes[q]];this._eventManager.objectMap[Ga]={id:Ga,objectType:"dataPoint",dataSeriesIndex:z,dataPointIndex:w.dataPointIndexes[q],x1:n,y1:e}}0<=w.dataPointIndexes[q]&&0!==H.markerSize&&(0<H.markerSize||0<w.markerSize)&&(q=w.getMarkerProperties(p,n,e,b),g.push(q),markerColor=D(Ga),t&&g.push({x:n,
y:e,ctx:v,type:q.type,size:q.size,color:markerColor,borderColor:markerColor,borderThickness:q.borderThickness}));(H.indexLabel||w.indexLabel||H.indexLabelFormatter||w.indexLabelFormatter)&&this._indexLabels.push({chartType:"stackedArea100",dataPoint:H,dataSeries:w,point:{x:n,y:e},direction:0>y[p].y===a.axisY.reversed?1:-1,color:d})}}c();b.moveTo(n,e);t&&v.moveTo(n,e)}delete w.dataPointIndexes}O.drawMarkers(g);b.restore();t&&v.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,
easingFunction:E.easing.linear,animationBase:0}}};A.prototype.renderBubble=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=this.plotArea,d=0,f,g;c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(b.x1,b.y1,b.width,b.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(b.x1,b.y1,b.width,b.height),this._eventManager.ghostCtx.clip());for(var l=-Infinity,k=Infinity,h=0;h<a.dataSeriesIndexes.length;h++)for(var m=
a.dataSeriesIndexes[h],p=this.data[m],n=p.dataPoints,e=0,d=0;d<n.length;d++)f=n[d].getTime?f=n[d].x.getTime():f=n[d].x,f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax||"undefined"===typeof n[d].z||(e=n[d].z,e>l&&(l=e),e<k&&(k=e));for(var q=25*Math.PI,b=Math.max(Math.pow(0.25*Math.min(b.height,b.width)/2,2)*Math.PI,q),h=0;h<a.dataSeriesIndexes.length;h++)if(m=a.dataSeriesIndexes[h],p=this.data[m],n=p.dataPoints,0<n.length)for(c.strokeStyle="#4572A7 ",d=0;d<n.length;d++)if(f=n[d].getTime?
f=n[d].x.getTime():f=n[d].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&"number"===typeof n[d].y){f=a.axisX.convertValueToPixel(f);g=a.axisY.convertValueToPixel(n[d].y);var e=n[d].z,r=2*Math.max(Math.sqrt((l===k?b/2:q+(b-q)/(l-k)*(e-k))/Math.PI)<<0,1),e=p.getMarkerProperties(d,c);e.size=r;c.globalAlpha=p.fillOpacity;O.drawMarker(f,g,c,e.type,e.size,e.color,e.borderColor,e.borderThickness);c.globalAlpha=1;var s=p.dataPointIds[d];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",
dataSeriesIndex:m,dataPointIndex:d,x1:f,y1:g,size:r};r=D(s);t&&O.drawMarker(f,g,this._eventManager.ghostCtx,e.type,e.size,r,r,e.borderThickness);(n[d].indexLabel||p.indexLabel||n[d].indexLabelFormatter||p.indexLabelFormatter)&&this._indexLabels.push({chartType:"bubble",dataPoint:n[d],dataSeries:p,point:{x:f,y:g},direction:1,bounds:{x1:f-e.size/2,y1:g-e.size/2,x2:f+e.size/2,y2:g+e.size/2},color:null})}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,
easingFunction:E.easing.easeInQuad,animationBase:0}}};A.prototype.renderScatter=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=this.plotArea,d=0,f,g;c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(b.x1,b.y1,b.width,b.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(b.x1,b.y1,b.width,b.height),this._eventManager.ghostCtx.clip());for(var l=0;l<a.dataSeriesIndexes.length;l++){var k=a.dataSeriesIndexes[l],
h=this.data[k],m=h.dataPoints;if(0<m.length){c.strokeStyle="#4572A7 ";Math.pow(0.3*Math.min(b.height,b.width)/2,2);for(var p=0,n=0,d=0;d<m.length;d++)if(f=m[d].getTime?f=m[d].x.getTime():f=m[d].x,!(f<a.axisX.dataInfo.viewPortMin||f>a.axisX.dataInfo.viewPortMax)&&"number"===typeof m[d].y){f=a.axisX.convertValueToPixel(f);g=a.axisY.convertValueToPixel(m[d].y);var e=h.getMarkerProperties(d,f,g,c);c.globalAlpha=h.fillOpacity;O.drawMarker(e.x,e.y,e.ctx,e.type,e.size,e.color,e.borderColor,e.borderThickness);
c.globalAlpha=1;Math.sqrt((p-f)*(p-f)+(n-g)*(n-g))<Math.min(e.size,5)&&m.length>Math.min(this.plotArea.width,this.plotArea.height)||(p=h.dataPointIds[d],this._eventManager.objectMap[p]={id:p,objectType:"dataPoint",dataSeriesIndex:k,dataPointIndex:d,x1:f,y1:g},p=D(p),t&&O.drawMarker(e.x,e.y,this._eventManager.ghostCtx,e.type,e.size,p,p,e.borderThickness),(m[d].indexLabel||h.indexLabel||m[d].indexLabelFormatter||h.indexLabelFormatter)&&this._indexLabels.push({chartType:"scatter",dataPoint:m[d],dataSeries:h,
point:{x:f,y:g},direction:1,bounds:{x1:f-e.size/2,y1:g-e.size/2,x2:f+e.size/2,y2:g+e.size/2},color:null}),p=f,n=g)}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,easingFunction:E.easing.easeInQuad,animationBase:0}}};A.prototype.renderCandlestick=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx,b=this._eventManager.ghostCtx;if(!(0>=a.dataSeriesIndexes.length)){var d=null,d=this.plotArea,f=0,g,l,k,h,m,p,f=this.dataPointMinWidth?
this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1;g=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:0.015*this.width;var n=a.axisX.dataInfo.minDiff;isFinite(n)||(n=0.3*Math.abs(a.axisX.range));n=this.dataPointWidth?this.dataPointWidth:0.7*d.width*(a.axisX.logarithmic?Math.log(n)/Math.log(a.axisX.range):Math.abs(n)/Math.abs(a.axisX.range))<<0;this.dataPointMaxWidth&&f>g&&(f=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&
(this.dataPointMinWidth&&g<f)&&(g=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,f));n<f&&(n=f);n>g&&(n=g);c.save();t&&b.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(b.beginPath(),b.rect(d.x1,d.y1,d.width,d.height),b.clip());for(var e=0;e<a.dataSeriesIndexes.length;e++){var q=a.dataSeriesIndexes[e],r=this.data[q],s=r.dataPoints;if(0<s.length)for(var x=5<n&&r.bevelEnabled?!0:!1,f=0;f<s.length;f++)if(s[f].getTime?p=s[f].x.getTime():p=s[f].x,!(p<a.axisX.dataInfo.viewPortMin||
p>a.axisX.dataInfo.viewPortMax)&&null!==s[f].y&&s[f].y.length&&"number"===typeof s[f].y[0]&&"number"===typeof s[f].y[1]&&"number"===typeof s[f].y[2]&&"number"===typeof s[f].y[3]){g=a.axisX.convertValueToPixel(p);l=a.axisY.convertValueToPixel(s[f].y[0]);k=a.axisY.convertValueToPixel(s[f].y[1]);h=a.axisY.convertValueToPixel(s[f].y[2]);m=a.axisY.convertValueToPixel(s[f].y[3]);var u=g-n/2<<0,v=u+n<<0,d=s[f].color?s[f].color:r._colorSet[0],z=Math.round(Math.max(1,0.15*n)),w=0===z%2?0:0.5,y=r.dataPointIds[f];
this._eventManager.objectMap[y]={id:y,objectType:"dataPoint",dataSeriesIndex:q,dataPointIndex:f,x1:u,y1:l,x2:v,y2:k,x3:g,y3:h,x4:g,y4:m,borderThickness:z,color:d};c.strokeStyle=d;c.beginPath();c.lineWidth=z;b.lineWidth=Math.max(z,4);"candlestick"===r.type?(c.moveTo(g-w,k),c.lineTo(g-w,Math.min(l,m)),c.stroke(),c.moveTo(g-w,Math.max(l,m)),c.lineTo(g-w,h),c.stroke(),N(c,u,Math.min(l,m),v,Math.max(l,m),s[f].y[0]<=s[f].y[3]?r.risingColor:d,z,d,x,x,!1,!1,r.fillOpacity),t&&(d=D(y),b.strokeStyle=d,b.moveTo(g-
w,k),b.lineTo(g-w,Math.min(l,m)),b.stroke(),b.moveTo(g-w,Math.max(l,m)),b.lineTo(g-w,h),b.stroke(),N(b,u,Math.min(l,m),v,Math.max(l,m),d,0,null,!1,!1,!1,!1))):"ohlc"===r.type&&(c.moveTo(g-w,k),c.lineTo(g-w,h),c.stroke(),c.beginPath(),c.moveTo(g,l),c.lineTo(u,l),c.stroke(),c.beginPath(),c.moveTo(g,m),c.lineTo(v,m),c.stroke(),t&&(d=D(y),b.strokeStyle=d,b.moveTo(g-w,k),b.lineTo(g-w,h),b.stroke(),b.beginPath(),b.moveTo(g,l),b.lineTo(u,l),b.stroke(),b.beginPath(),b.moveTo(g,m),b.lineTo(v,m),b.stroke()));
(s[f].indexLabel||r.indexLabel||s[f].indexLabelFormatter||r.indexLabelFormatter)&&this._indexLabels.push({chartType:r.type,dataPoint:s[f],dataSeries:r,point:{x:u+(v-u)/2,y:a.axisY.reversed?h:k},direction:1,bounds:{x1:u,y1:Math.min(k,h),x2:v,y2:Math.max(k,h)},color:d})}}c.restore();t&&b.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,easingFunction:E.easing.easeInQuad,animationBase:0}}};A.prototype.renderRangeColumn=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;
if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=0,g,l,f=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1;g=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:0.03*this.width;var k=a.axisX.dataInfo.minDiff;isFinite(k)||(k=0.3*Math.abs(a.axisX.range));k=this.dataPointWidth?this.dataPointWidth:0.9*(d.width*(a.axisX.logarithmic?Math.log(k)/Math.log(a.axisX.range):Math.abs(k)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<
0;this.dataPointMaxWidth&&f>g&&(f=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&g<f)&&(g=Math.max(this.dataPointWidth?this.dataPointWidth:-Infinity,f));k<f&&(k=f);k>g&&(k=g);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(var h=0;h<a.dataSeriesIndexes.length;h++){var m=
a.dataSeriesIndexes[h],p=this.data[m],n=p.dataPoints;if(0<n.length)for(var e=5<k&&p.bevelEnabled?!0:!1,f=0;f<n.length;f++)if(n[f].getTime?l=n[f].x.getTime():l=n[f].x,!(l<a.axisX.dataInfo.viewPortMin||l>a.axisX.dataInfo.viewPortMax)&&null!==n[f].y&&n[f].y.length&&"number"===typeof n[f].y[0]&&"number"===typeof n[f].y[1]){b=a.axisX.convertValueToPixel(l);d=a.axisY.convertValueToPixel(n[f].y[0]);g=a.axisY.convertValueToPixel(n[f].y[1]);var q=a.axisX.reversed?b+a.plotType.totalDataSeries*k/2-(a.previousDataSeriesCount+
h)*k<<0:b-a.plotType.totalDataSeries*k/2+(a.previousDataSeriesCount+h)*k<<0,r=a.axisX.reversed?q-k<<0:q+k<<0,b=n[f].color?n[f].color:p._colorSet[f%p._colorSet.length];if(d>g){var s=d,d=g;g=s}s=p.dataPointIds[f];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataSeriesIndex:m,dataPointIndex:f,x1:q,y1:d,x2:r,y2:g};N(c,q,d,r,g,b,0,b,e,e,!1,!1,p.fillOpacity);b=D(s);t&&N(this._eventManager.ghostCtx,q,d,r,g,b,0,null,!1,!1,!1,!1);if(n[f].indexLabel||p.indexLabel||n[f].indexLabelFormatter||
p.indexLabelFormatter)this._indexLabels.push({chartType:"rangeColumn",dataPoint:n[f],dataSeries:p,indexKeyword:0,point:{x:q+(r-q)/2,y:n[f].y[1]>=n[f].y[0]?g:d},direction:n[f].y[1]>=n[f].y[0]?-1:1,bounds:{x1:q,y1:Math.min(d,g),x2:r,y2:Math.max(d,g)},color:b}),this._indexLabels.push({chartType:"rangeColumn",dataPoint:n[f],dataSeries:p,indexKeyword:1,point:{x:q+(r-q)/2,y:n[f].y[1]>=n[f].y[0]?d:g},direction:n[f].y[1]>=n[f].y[0]?1:-1,bounds:{x1:q,y1:Math.min(d,g),x2:r,y2:Math.max(d,g)},color:b})}}c.restore();
t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,easingFunction:E.easing.easeInQuad,animationBase:0}}};A.prototype.renderRangeBar=function(a){var c=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var b=null,d=this.plotArea,f=0,g,l,k,f=this.dataPointMinWidth?this.dataPointMinWidth:this.dataPointWidth?this.dataPointWidth:1;g=this.dataPointMaxWidth?this.dataPointMaxWidth:this.dataPointWidth?this.dataPointWidth:
Math.min(0.15*this.height,0.9*(this.plotArea.height/a.plotType.totalDataSeries))<<0;var h=a.axisX.dataInfo.minDiff;isFinite(h)||(h=0.3*Math.abs(a.axisX.range));h=this.dataPointWidth?this.dataPointWidth:0.9*(d.height*(a.axisX.logarithmic?Math.log(h)/Math.log(a.axisX.range):Math.abs(h)/Math.abs(a.axisX.range))/a.plotType.totalDataSeries)<<0;this.dataPointMaxWidth&&f>g&&(f=Math.min(this.dataPointWidth?this.dataPointWidth:Infinity,g));!this.dataPointMaxWidth&&(this.dataPointMinWidth&&g<f)&&(g=Math.max(this.dataPointWidth?
this.dataPointWidth:-Infinity,f));h<f&&(h=f);h>g&&(h=g);c.save();t&&this._eventManager.ghostCtx.save();c.beginPath();c.rect(d.x1,d.y1,d.width,d.height);c.clip();t&&(this._eventManager.ghostCtx.beginPath(),this._eventManager.ghostCtx.rect(d.x1,d.y1,d.width,d.height),this._eventManager.ghostCtx.clip());for(var m=0;m<a.dataSeriesIndexes.length;m++){var p=a.dataSeriesIndexes[m],n=this.data[p],e=n.dataPoints;if(0<e.length){var q=5<h&&n.bevelEnabled?!0:!1;c.strokeStyle="#4572A7 ";for(f=0;f<e.length;f++)if(e[f].getTime?
k=e[f].x.getTime():k=e[f].x,!(k<a.axisX.dataInfo.viewPortMin||k>a.axisX.dataInfo.viewPortMax)&&null!==e[f].y&&e[f].y.length&&"number"===typeof e[f].y[0]&&"number"===typeof e[f].y[1]){d=a.axisY.convertValueToPixel(e[f].y[0]);g=a.axisY.convertValueToPixel(e[f].y[1]);l=a.axisX.convertValueToPixel(k);l=a.axisX.reversed?l+a.plotType.totalDataSeries*h/2-(a.previousDataSeriesCount+m)*h<<0:l-a.plotType.totalDataSeries*h/2+(a.previousDataSeriesCount+m)*h<<0;var r=a.axisX.reversed?l-h<<0:l+h<<0;d>g&&(b=d,d=
g,g=b);b=e[f].color?e[f].color:n._colorSet[f%n._colorSet.length];N(c,d,l,g,r,b,0,null,q,!1,!1,!1,n.fillOpacity);b=n.dataPointIds[f];this._eventManager.objectMap[b]={id:b,objectType:"dataPoint",dataSeriesIndex:p,dataPointIndex:f,x1:d,y1:l,x2:g,y2:r};b=D(b);t&&N(this._eventManager.ghostCtx,d,l,g,r,b,0,null,!1,!1,!1,!1);if(e[f].indexLabel||n.indexLabel||e[f].indexLabelFormatter||n.indexLabelFormatter)this._indexLabels.push({chartType:"rangeBar",dataPoint:e[f],dataSeries:n,indexKeyword:0,point:{x:e[f].y[1]>=
e[f].y[0]?d:g,y:l+(r-l)/2},direction:e[f].y[1]>=e[f].y[0]?-1:1,bounds:{x1:Math.min(d,g),y1:l,x2:Math.max(d,g),y2:r},color:b}),this._indexLabels.push({chartType:"rangeBar",dataPoint:e[f],dataSeries:n,indexKeyword:1,point:{x:e[f].y[1]>=e[f].y[0]?g:d,y:l+(r-l)/2},direction:e[f].y[1]>=e[f].y[0]?1:-1,bounds:{x1:Math.min(d,g),y1:l,x2:Math.max(d,g),y2:r},color:b})}}}c.restore();t&&this._eventManager.ghostCtx.restore();return{source:c,dest:this.plotArea.ctx,animationCallback:E.fadeInAnimation,easingFunction:E.easing.easeInQuad,
animationBase:0}}};A.prototype.renderRangeArea=function(a){function c(){if(x){var a=null;0<h.lineThickness&&b.stroke();for(var c=l.length-1;0<=c;c--)a=l[c],b.lineTo(a.x,a.y),d.lineTo(a.x,a.y);b.closePath();b.globalAlpha=h.fillOpacity;b.fill();b.globalAlpha=1;d.fill();if(0<h.lineThickness){b.beginPath();b.moveTo(a.x,a.y);for(c=0;c<l.length;c++)a=l[c],b.lineTo(a.x,a.y);b.stroke()}b.beginPath();b.moveTo(e,q);d.beginPath();d.moveTo(e,q);x={x:e,y:q};l=[];l.push({x:e,y:r})}}var b=a.targetCanvasCtx||this.plotArea.ctx;
if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,f=[],g=this.plotArea;b.save();t&&d.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();t&&(d.beginPath(),d.rect(g.x1,g.y1,g.width,g.height),d.clip());for(g=0;g<a.dataSeriesIndexes.length;g++){var l=[],k=a.dataSeriesIndexes[g],h=this.data[k],m=h.dataPoints,f=h.id;this._eventManager.objectMap[f]={objectType:"dataSeries",dataSeriesIndex:k};f=D(f);d.fillStyle=f;var f=[],p=!0,n=0,e,q,r,s,x=null;if(0<m.length){var u=h._colorSet[n%
h._colorSet.length],v=h.lineColor=h.options.lineColor||u,z=v;b.fillStyle=u;b.strokeStyle=v;b.lineWidth=h.lineThickness;var w="solid";if(b.setLineDash){var y=F(h.nullDataLineDashType,h.lineThickness),w=h.lineDashType,A=F(w,h.lineThickness);b.setLineDash(A)}for(var B=!0;n<m.length;n++)if(s=m[n].x.getTime?m[n].x.getTime():m[n].x,!(s<a.axisX.dataInfo.viewPortMin||s>a.axisX.dataInfo.viewPortMax&&(!h.connectNullData||!B)))if(null!==m[n].y&&m[n].y.length&&"number"===typeof m[n].y[0]&&"number"===typeof m[n].y[1]){e=
a.axisX.convertValueToPixel(s);q=a.axisY.convertValueToPixel(m[n].y[0]);r=a.axisY.convertValueToPixel(m[n].y[1]);p||B?(h.connectNullData&&!p?(b.setLineDash&&(h.options.nullDataLineDashType||w===h.lineDashType&&h.lineDashType!==h.nullDataLineDashType)&&(l[l.length-1].newLineDashArray=A,w=h.nullDataLineDashType,b.setLineDash(y)),b.lineTo(e,q),t&&d.lineTo(e,q),l.push({x:e,y:r})):(b.beginPath(),b.moveTo(e,q),x={x:e,y:q},l=[],l.push({x:e,y:r}),t&&(d.beginPath(),d.moveTo(e,q))),B=p=!1):(b.lineTo(e,q),l.push({x:e,
y:r}),t&&d.lineTo(e,q),0==n%250&&c());s=h.dataPointIds[n];this._eventManager.objectMap[s]={id:s,objectType:"dataPoint",dataSeriesIndex:k,dataPointIndex:n,x1:e,y1:q,y2:r};n<m.length-1&&(z!==(m[n].lineColor||v)||w!==(m[n].lineDashType||h.lineDashType))&&(c(),z=m[n].lineColor||v,l[l.length-1].newStrokeStyle=z,b.strokeStyle=z,b.setLineDash&&(m[n].lineDashType?(w=m[n].lineDashType,l[l.length-1].newLineDashArray=F(w,h.lineThickness),b.setLineDash(l[l.length-1].newLineDashArray)):(w=h.lineDashType,l[l.length-
1].newLineDashArray=A,b.setLineDash(A))));if(0!==m[n].markerSize&&(0<m[n].markerSize||0<h.markerSize)){var M=h.getMarkerProperties(n,e,r,b);f.push(M);var W=D(s);t&&f.push({x:e,y:r,ctx:d,type:M.type,size:M.size,color:W,borderColor:W,borderThickness:M.borderThickness});M=h.getMarkerProperties(n,e,q,b);f.push(M);W=D(s);t&&f.push({x:e,y:q,ctx:d,type:M.type,size:M.size,color:W,borderColor:W,borderThickness:M.borderThickness})}if(m[n].indexLabel||h.indexLabel||m[n].indexLabelFormatter||h.indexLabelFormatter)this._indexLabels.push({chartType:"rangeArea",
dataPoint:m[n],dataSeries:h,indexKeyword:0,point:{x:e,y:q},direction:m[n].y[0]>m[n].y[1]===a.axisY.reversed?-1:1,color:u}),this._indexLabels.push({chartType:"rangeArea",dataPoint:m[n],dataSeries:h,indexKeyword:1,point:{x:e,y:r},direction:m[n].y[0]>m[n].y[1]===a.axisY.reversed?1:-1,color:u})}else B||p||c(),B=!0;c();O.drawMarkers(f)}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};
A.prototype.renderRangeSplineArea=function(a){function c(a,c){var e=wa(q,2);if(0<e.length){if(0<k.lineThickness){b.strokeStyle=c;b.setLineDash&&b.setLineDash(a);b.beginPath();b.moveTo(e[0].x,e[0].y);for(var f=0;f<e.length-3;f+=3){if(e[f].newStrokeStyle||e[f].newLineDashArray)b.stroke(),b.beginPath(),b.moveTo(e[f].x,e[f].y),e[f].newStrokeStyle&&(b.strokeStyle=e[f].newStrokeStyle),e[f].newLineDashArray&&b.setLineDash(e[f].newLineDashArray);b.bezierCurveTo(e[f+1].x,e[f+1].y,e[f+2].x,e[f+2].y,e[f+3].x,
e[f+3].y)}b.stroke()}b.beginPath();b.moveTo(e[0].x,e[0].y);t&&(d.beginPath(),d.moveTo(e[0].x,e[0].y));for(f=0;f<e.length-3;f+=3)b.bezierCurveTo(e[f+1].x,e[f+1].y,e[f+2].x,e[f+2].y,e[f+3].x,e[f+3].y),t&&d.bezierCurveTo(e[f+1].x,e[f+1].y,e[f+2].x,e[f+2].y,e[f+3].x,e[f+3].y);e=wa(r,2);b.lineTo(r[r.length-1].x,r[r.length-1].y);for(f=e.length-1;2<f;f-=3)b.bezierCurveTo(e[f-1].x,e[f-1].y,e[f-2].x,e[f-2].y,e[f-3].x,e[f-3].y),t&&d.bezierCurveTo(e[f-1].x,e[f-1].y,e[f-2].x,e[f-2].y,e[f-3].x,e[f-3].y);b.closePath();
b.globalAlpha=k.fillOpacity;b.fill();t&&(d.closePath(),d.fill());b.globalAlpha=1;if(0<k.lineThickness){b.strokeStyle=c;b.setLineDash&&b.setLineDash(a);b.beginPath();b.moveTo(e[0].x,e[0].y);for(var g=f=0;f<e.length-3;f+=3,g++){if(q[g].newStrokeStyle||q[g].newLineDashArray)b.stroke(),b.beginPath(),b.moveTo(e[f].x,e[f].y),q[g].newStrokeStyle&&(b.strokeStyle=q[g].newStrokeStyle),q[g].newLineDashArray&&b.setLineDash(q[g].newLineDashArray);b.bezierCurveTo(e[f+1].x,e[f+1].y,e[f+2].x,e[f+2].y,e[f+3].x,e[f+
3].y)}b.stroke()}b.beginPath()}}var b=a.targetCanvasCtx||this.plotArea.ctx;if(!(0>=a.dataSeriesIndexes.length)){var d=this._eventManager.ghostCtx,f=[],g=this.plotArea;b.save();t&&d.save();b.beginPath();b.rect(g.x1,g.y1,g.width,g.height);b.clip();t&&(d.beginPath(),d.rect(g.x1,g.y1,g.width,g.height),d.clip());for(g=0;g<a.dataSeriesIndexes.length;g++){var l=a.dataSeriesIndexes[g],k=this.data[l],h=k.dataPoints,f=k.id;this._eventManager.objectMap[f]={objectType:"dataSeries",dataSeriesIndex:l};f=D(f);d.fillStyle=
f;var f=[],m=0,p,n,e,q=[],r=[];if(0<h.length){var s=k._colorSet[m%k._colorSet.length],x=k.lineColor=k.options.lineColor||s,u=x;b.fillStyle=s;b.lineWidth=k.lineThickness;var v="solid",z;if(b.setLineDash){var w=F(k.nullDataLineDashType,k.lineThickness),v=k.lineDashType;z=F(v,k.lineThickness)}for(n=!1;m<h.length;m++)if(p=h[m].x.getTime?h[m].x.getTime():h[m].x,!(p<a.axisX.dataInfo.viewPortMin||p>a.axisX.dataInfo.viewPortMax&&(!k.connectNullData||!n)))if(null!==h[m].y&&h[m].y.length&&"number"===typeof h[m].y[0]&&
"number"===typeof h[m].y[1]){p=a.axisX.convertValueToPixel(p);n=a.axisY.convertValueToPixel(h[m].y[0]);e=a.axisY.convertValueToPixel(h[m].y[1]);var y=k.dataPointIds[m];this._eventManager.objectMap[y]={id:y,objectType:"dataPoint",dataSeriesIndex:l,dataPointIndex:m,x1:p,y1:n,y2:e};q[q.length]={x:p,y:n};r[r.length]={x:p,y:e};m<h.length-1&&(u!==(h[m].lineColor||x)||v!==(h[m].lineDashType||k.lineDashType))&&(u=h[m].lineColor||x,q[q.length-1].newStrokeStyle=u,b.setLineDash&&(h[m].lineDashType?(v=h[m].lineDashType,
q[q.length-1].newLineDashArray=F(v,k.lineThickness)):(v=k.lineDashType,q[q.length-1].newLineDashArray=z)));if(0!==h[m].markerSize&&(0<h[m].markerSize||0<k.markerSize)){var A=k.getMarkerProperties(m,p,n,b);f.push(A);var B=D(y);t&&f.push({x:p,y:n,ctx:d,type:A.type,size:A.size,color:B,borderColor:B,borderThickness:A.borderThickness});A=k.getMarkerProperties(m,p,e,b);f.push(A);B=D(y);t&&f.push({x:p,y:e,ctx:d,type:A.type,size:A.size,color:B,borderColor:B,borderThickness:A.borderThickness})}if(h[m].indexLabel||
k.indexLabel||h[m].indexLabelFormatter||k.indexLabelFormatter)this._indexLabels.push({chartType:"rangeSplineArea",dataPoint:h[m],dataSeries:k,indexKeyword:0,point:{x:p,y:n},direction:h[m].y[0]<=h[m].y[1]?-1:1,color:s}),this._indexLabels.push({chartType:"rangeSplineArea",dataPoint:h[m],dataSeries:k,indexKeyword:1,point:{x:p,y:e},direction:h[m].y[0]<=h[m].y[1]?1:-1,color:s});n=!1}else 0<m&&!n&&(k.connectNullData?b.setLineDash&&(0<q.length&&(k.options.nullDataLineDashType||!h[m-1].lineDashType))&&(q[q.length-
1].newLineDashArray=w,v=k.nullDataLineDashType):(c(z,x),q=[],r=[])),n=!0;c(z,x);O.drawMarkers(f)}}b.restore();t&&this._eventManager.ghostCtx.restore();return{source:b,dest:this.plotArea.ctx,animationCallback:E.xClipAnimation,easingFunction:E.easing.linear,animationBase:0}}};var Ha=function(a,c,b,d,f,g,l,k,h){if(!(0>b)){"undefined"===typeof k&&(k=1);if(!t){var m=Number((l%(2*Math.PI)).toFixed(8));Number((g%(2*Math.PI)).toFixed(8))===m&&(l-=1E-4)}a.save();a.globalAlpha=k;"pie"===f?(a.beginPath(),a.moveTo(c.x,
c.y),a.arc(c.x,c.y,b,g,l,!1),a.fillStyle=d,a.strokeStyle="white",a.lineWidth=2,a.closePath(),a.fill()):"doughnut"===f&&(a.beginPath(),a.arc(c.x,c.y,b,g,l,!1),0<=h&&a.arc(c.x,c.y,h*b,l,g,!0),a.closePath(),a.fillStyle=d,a.strokeStyle="white",a.lineWidth=2,a.fill());a.globalAlpha=1;a.restore()}};A.prototype.renderPie=function(a){function c(){if(m&&p){for(var a=0,b=0,c=0,d=0,f=0;f<p.length;f++){var g=p[f],k=m.dataPointIds[f],l={id:k,objectType:"dataPoint",dataPointIndex:f,dataSeriesIndex:0};q.push(l);
var n={percent:null,total:null},r=null,n=h.getPercentAndTotal(m,g);if(m.indexLabelFormatter||g.indexLabelFormatter)r={chart:h.options,dataSeries:m,dataPoint:g,total:n.total,percent:n.percent};n=g.indexLabelFormatter?g.indexLabelFormatter(r):g.indexLabel?h.replaceKeywordsWithValue(g.indexLabel,g,m,f):m.indexLabelFormatter?m.indexLabelFormatter(r):m.indexLabel?h.replaceKeywordsWithValue(m.indexLabel,g,m,f):g.label?g.label:"";h._eventManager.objectMap[k]=l;l.center={x:z.x,y:z.y};l.y=g.y;l.radius=B;l.percentInnerRadius=
M;l.indexLabelText=n;l.indexLabelPlacement=m.indexLabelPlacement;l.indexLabelLineColor=g.indexLabelLineColor?g.indexLabelLineColor:m.options.indexLabelLineColor?m.options.indexLabelLineColor:g.color?g.color:m._colorSet[f%m._colorSet.length];l.indexLabelLineThickness=y(g.indexLabelLineThickness)?m.indexLabelLineThickness:g.indexLabelLineThickness;l.indexLabelLineDashType=g.indexLabelLineDashType?g.indexLabelLineDashType:m.indexLabelLineDashType;l.indexLabelFontColor=g.indexLabelFontColor?g.indexLabelFontColor:
m.indexLabelFontColor;l.indexLabelFontStyle=g.indexLabelFontStyle?g.indexLabelFontStyle:m.indexLabelFontStyle;l.indexLabelFontWeight=g.indexLabelFontWeight?g.indexLabelFontWeight:m.indexLabelFontWeight;l.indexLabelFontSize=y(g.indexLabelFontSize)?m.indexLabelFontSize:g.indexLabelFontSize;l.indexLabelFontFamily=g.indexLabelFontFamily?g.indexLabelFontFamily:m.indexLabelFontFamily;l.indexLabelBackgroundColor=g.indexLabelBackgroundColor?g.indexLabelBackgroundColor:m.options.indexLabelBackgroundColor?
m.options.indexLabelBackgroundColor:m.indexLabelBackgroundColor;l.indexLabelMaxWidth=g.indexLabelMaxWidth?g.indexLabelMaxWidth:m.indexLabelMaxWidth?m.indexLabelMaxWidth:0.33*e.width;l.indexLabelWrap="undefined"!==typeof g.indexLabelWrap?g.indexLabelWrap:m.indexLabelWrap;l.startAngle=0===f?m.startAngle?m.startAngle/180*Math.PI:0:q[f-1].endAngle;l.startAngle=(l.startAngle+2*Math.PI)%(2*Math.PI);l.endAngle=l.startAngle+2*Math.PI/t*Math.abs(g.y);g=(l.endAngle+l.startAngle)/2;g=(g+2*Math.PI)%(2*Math.PI);
l.midAngle=g;if(l.midAngle>Math.PI/2-u&&l.midAngle<Math.PI/2+u){if(0===a||q[c].midAngle>l.midAngle)c=f;a++}else if(l.midAngle>3*Math.PI/2-u&&l.midAngle<3*Math.PI/2+u){if(0===b||q[d].midAngle>l.midAngle)d=f;b++}l.hemisphere=g>Math.PI/2&&g<=3*Math.PI/2?"left":"right";l.indexLabelTextBlock=new V(h.plotArea.ctx,{fontSize:l.indexLabelFontSize,fontFamily:l.indexLabelFontFamily,fontColor:l.indexLabelFontColor,fontStyle:l.indexLabelFontStyle,fontWeight:l.indexLabelFontWeight,horizontalAlign:"left",backgroundColor:l.indexLabelBackgroundColor,
maxWidth:l.indexLabelMaxWidth,maxHeight:l.indexLabelWrap?5*l.indexLabelFontSize:1.5*l.indexLabelFontSize,text:l.indexLabelText,padding:0,textBaseline:"top"});l.indexLabelTextBlock.measureText()}k=g=0;n=!1;for(f=0;f<p.length;f++)l=q[(c+f)%p.length],1<a&&(l.midAngle>Math.PI/2-u&&l.midAngle<Math.PI/2+u)&&(g<=a/2&&!n?(l.hemisphere="right",g++):(l.hemisphere="left",n=!0));n=!1;for(f=0;f<p.length;f++)l=q[(d+f)%p.length],1<b&&(l.midAngle>3*Math.PI/2-u&&l.midAngle<3*Math.PI/2+u)&&(k<=b/2&&!n?(l.hemisphere=
"left",k++):(l.hemisphere="right",n=!0))}}function b(a){var b=h.plotArea.ctx;b.clearRect(e.x1,e.y1,e.width,e.height);b.fillStyle=h.backgroundColor;b.fillRect(e.x1,e.y1,e.width,e.height);for(b=0;b<p.length;b++){var c=q[b].startAngle,d=q[b].endAngle;if(d>c){var f=0.07*B*Math.cos(q[b].midAngle),g=0.07*B*Math.sin(q[b].midAngle),k=!1;if(p[b].exploded){if(1E-9<Math.abs(q[b].center.x-(z.x+f))||1E-9<Math.abs(q[b].center.y-(z.y+g)))q[b].center.x=z.x+f*a,q[b].center.y=z.y+g*a,k=!0}else if(0<Math.abs(q[b].center.x-
z.x)||0<Math.abs(q[b].center.y-z.y))q[b].center.x=z.x+f*(1-a),q[b].center.y=z.y+g*(1-a),k=!0;k&&(f={},f.dataSeries=m,f.dataPoint=m.dataPoints[b],f.index=b,h.toolTip.highlightObjects([f]));Ha(h.plotArea.ctx,q[b].center,q[b].radius,p[b].color?p[b].color:m._colorSet[b%m._colorSet.length],m.type,c,d,m.fillOpacity,q[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<p.length;b++)c=q[b],c.indexLabelText&&(c.indexLabelTextBlock.y-=
c.indexLabelTextBlock.height/2,d=0,d="left"===c.hemisphere?"inside"!==m.indexLabelPlacement?-(c.indexLabelTextBlock.width+n):-c.indexLabelTextBlock.width/2:"inside"!==m.indexLabelPlacement?n:-c.indexLabelTextBlock.width/2,c.indexLabelTextBlock.x+=d,c.indexLabelTextBlock.render(!0),c.indexLabelTextBlock.x-=d,c.indexLabelTextBlock.y+=c.indexLabelTextBlock.height/2,"inside"!==c.indexLabelPlacement&&0<c.indexLabelLineThickness&&(d=c.center.x+B*Math.cos(c.midAngle),f=c.center.y+B*Math.sin(c.midAngle),
a.strokeStyle=c.indexLabelLineColor,a.lineWidth=c.indexLabelLineThickness,a.setLineDash&&a.setLineDash(F(c.indexLabelLineDashType,c.indexLabelLineThickness)),a.beginPath(),a.moveTo(d,f),a.lineTo(c.indexLabelTextBlock.x,c.indexLabelTextBlock.y),a.lineTo(c.indexLabelTextBlock.x+("left"===c.hemisphere?-n:n),c.indexLabelTextBlock.y),a.stroke()),a.lineJoin="miter");a.save()}function d(a,b){var c=0,c=a.indexLabelTextBlock.y-a.indexLabelTextBlock.height/2,d=a.indexLabelTextBlock.y+a.indexLabelTextBlock.height/
2,e=b.indexLabelTextBlock.y-b.indexLabelTextBlock.height/2,f=b.indexLabelTextBlock.y+b.indexLabelTextBlock.height/2;return c=b.indexLabelTextBlock.y>a.indexLabelTextBlock.y?e-d:c-f}function f(a){for(var b=null,c=1;c<p.length;c++)if(b=(a+c+q.length)%q.length,q[b].hemisphere!==q[a].hemisphere){b=null;break}else if(q[b].indexLabelText&&b!==a&&(0>d(q[b],q[a])||("right"===q[a].hemisphere?q[b].indexLabelTextBlock.y>=q[a].indexLabelTextBlock.y:q[b].indexLabelTextBlock.y<=q[a].indexLabelTextBlock.y)))break;
else b=null;return b}function g(a,b,c){c=(c||0)+1;if(1E3<c)return 0;b=b||0;var e=0,h=z.y-1*s,m=z.y+1*s;if(0<=a&&a<p.length){var k=q[a];if(0>b&&k.indexLabelTextBlock.y<h||0<b&&k.indexLabelTextBlock.y>m)return 0;var l=0,n=0,n=l=l=0;0>b?k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2>h&&k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2+b<h&&(b=-(h-(k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2+b))):k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2<h&&k.indexLabelTextBlock.y+
k.indexLabelTextBlock.height/2+b>m&&(b=k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2+b-m);b=k.indexLabelTextBlock.y+b;h=0;h="right"===k.hemisphere?z.x+Math.sqrt(Math.pow(s,2)-Math.pow(b-z.y,2)):z.x-Math.sqrt(Math.pow(s,2)-Math.pow(b-z.y,2));n=z.x+B*Math.cos(k.midAngle);l=z.y+B*Math.sin(k.midAngle);l=Math.sqrt(Math.pow(h-n,2)+Math.pow(b-l,2));n=Math.acos(B/s);l=Math.acos((s*s+B*B-l*l)/(2*B*s));b=l<n?b-k.indexLabelTextBlock.y:0;h=null;for(m=1;m<p.length;m++)if(h=(a-m+q.length)%q.length,q[h].hemisphere!==
q[a].hemisphere){h=null;break}else if(q[h].indexLabelText&&q[h].hemisphere===q[a].hemisphere&&h!==a&&(0>d(q[h],q[a])||("right"===q[a].hemisphere?q[h].indexLabelTextBlock.y<=q[a].indexLabelTextBlock.y:q[h].indexLabelTextBlock.y>=q[a].indexLabelTextBlock.y)))break;else h=null;n=h;l=f(a);m=h=0;0>b?(m="right"===k.hemisphere?n:l,e=b,null!==m&&(n=-b,b=k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2-(q[m].indexLabelTextBlock.y+q[m].indexLabelTextBlock.height/2),b-n<r&&(h=-n,m=g(m,h,c+1),+m.toFixed(v)>
+h.toFixed(v)&&(e=b>r?-(b-r):-(n-(m-h)))))):0<b&&(m="right"===k.hemisphere?l:n,e=b,null!==m&&(n=b,b=q[m].indexLabelTextBlock.y-q[m].indexLabelTextBlock.height/2-(k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2),b-n<r&&(h=n,m=g(m,h,c+1),+m.toFixed(v)<+h.toFixed(v)&&(e=b>r?b-r:n-(h-m)))));e&&(c=k.indexLabelTextBlock.y+e,b=0,b="right"===k.hemisphere?z.x+Math.sqrt(Math.pow(s,2)-Math.pow(c-z.y,2)):z.x-Math.sqrt(Math.pow(s,2)-Math.pow(c-z.y,2)),k.midAngle>Math.PI/2-u&&k.midAngle<Math.PI/2+u?(h=(a-
1+q.length)%q.length,h=q[h],a=q[(a+1+q.length)%q.length],"left"===k.hemisphere&&"right"===h.hemisphere&&b>h.indexLabelTextBlock.x?b=h.indexLabelTextBlock.x-15:"right"===k.hemisphere&&("left"===a.hemisphere&&b<a.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x+15)):k.midAngle>3*Math.PI/2-u&&k.midAngle<3*Math.PI/2+u&&(h=(a-1+q.length)%q.length,h=q[h],a=q[(a+1+q.length)%q.length],"right"===k.hemisphere&&"left"===h.hemisphere&&b<h.indexLabelTextBlock.x?b=h.indexLabelTextBlock.x+15:"left"===k.hemisphere&&
("right"===a.hemisphere&&b>a.indexLabelTextBlock.x)&&(b=a.indexLabelTextBlock.x-15)),k.indexLabelTextBlock.y=c,k.indexLabelTextBlock.x=b,k.indexLabelAngle=Math.atan2(k.indexLabelTextBlock.y-z.y,k.indexLabelTextBlock.x-z.x))}return e}function l(){var a=h.plotArea.ctx;a.fillStyle="grey";a.strokeStyle="grey";a.font="16px Arial";a.textBaseline="middle";for(var b=a=0,c=0,k=!0,b=0;10>b&&(1>b||0<c);b++){if(m.radius||!m.radius&&"undefined"!==typeof m.innerRadius&&null!==m.innerRadius&&B-c<=E)k=!1;k&&(B-=
c);c=0;if("inside"!==m.indexLabelPlacement){s=B*x;for(a=0;a<p.length;a++){var l=q[a];l.indexLabelTextBlock.x=z.x+s*Math.cos(l.midAngle);l.indexLabelTextBlock.y=z.y+s*Math.sin(l.midAngle);l.indexLabelAngle=l.midAngle;l.radius=B;l.percentInnerRadius=M}for(var u,t,a=0;a<p.length;a++){var l=q[a],w=f(a);if(null!==w){u=q[a];t=q[w];var y=0,y=d(u,t)-r;if(0>y){for(var A=t=0,D=0;D<p.length;D++)D!==a&&q[D].hemisphere===l.hemisphere&&(q[D].indexLabelTextBlock.y<l.indexLabelTextBlock.y?t++:A++);t=y/(t+A||1)*A;
var A=-1*(y-t),C=D=0;"right"===l.hemisphere?(D=g(a,t),A=-1*(y-D),C=g(w,A),+C.toFixed(v)<+A.toFixed(v)&&+D.toFixed(v)<=+t.toFixed(v)&&g(a,-(A-C))):(D=g(w,t),A=-1*(y-D),C=g(a,A),+C.toFixed(v)<+A.toFixed(v)&&+D.toFixed(v)<=+t.toFixed(v)&&g(w,-(A-C)))}}}}else for(a=0;a<p.length;a++)l=q[a],s="pie"===m.type?0.7*B:0.8*B,w=z.x+s*Math.cos(l.midAngle),t=z.y+s*Math.sin(l.midAngle),l.indexLabelTextBlock.x=w,l.indexLabelTextBlock.y=t;for(a=0;a<p.length;a++)if(l=q[a],w=l.indexLabelTextBlock.measureText(),0!==w.height&&
0!==w.width)w=w=0,"right"===l.hemisphere?(w=e.x2-(l.indexLabelTextBlock.x+l.indexLabelTextBlock.width+n),w*=-1):w=e.x1-(l.indexLabelTextBlock.x-l.indexLabelTextBlock.width-n),0<w&&(!k&&l.indexLabelText&&(t="right"===l.hemisphere?e.x2-l.indexLabelTextBlock.x:l.indexLabelTextBlock.x-e.x1,0.3*l.indexLabelTextBlock.maxWidth>t?l.indexLabelText="":l.indexLabelTextBlock.maxWidth=0.85*t,0.3*l.indexLabelTextBlock.maxWidth<t&&(l.indexLabelTextBlock.x-="right"===l.hemisphere?2:-2)),Math.abs(l.indexLabelTextBlock.y-
l.indexLabelTextBlock.height/2-z.y)<B||Math.abs(l.indexLabelTextBlock.y+l.indexLabelTextBlock.height/2-z.y)<B)&&(w/=Math.abs(Math.cos(l.indexLabelAngle)),9<w&&(w*=0.3),w>c&&(c=w)),w=w=0,0<l.indexLabelAngle&&l.indexLabelAngle<Math.PI?(w=e.y2-(l.indexLabelTextBlock.y+l.indexLabelTextBlock.height/2+5),w*=-1):w=e.y1-(l.indexLabelTextBlock.y-l.indexLabelTextBlock.height/2-5),0<w&&(!k&&l.indexLabelText&&(t=0<l.indexLabelAngle&&l.indexLabelAngle<Math.PI?-1:1,0===g(a,w*t)&&g(a,2*t)),Math.abs(l.indexLabelTextBlock.x-
z.x)<B&&(w/=Math.abs(Math.sin(l.indexLabelAngle)),9<w&&(w*=0.3),w>c&&(c=w)));var F=function(a,b,c){for(var d=[],e=0;d.push(q[b]),b!==c;b=(b+1+p.length)%p.length);d.sort(function(a,b){return a.y-b.y});for(b=0;b<d.length;b++)if(c=d[b],e<0.7*a)e+=c.indexLabelTextBlock.height,c.indexLabelTextBlock.text="",c.indexLabelText="",c.indexLabelTextBlock.measureText();else break};(function(){for(var a=-1,b=-1,c=0,e=!1,g=0;g<p.length;g++)if(e=!1,u=q[g],u.indexLabelText){var h=f(g);if(null!==h){var k=q[h];y=0;
y=d(u,k);var l;if(l=0>y){l=u.indexLabelTextBlock.x;var m=u.indexLabelTextBlock.y-u.indexLabelTextBlock.height/2,r=u.indexLabelTextBlock.y+u.indexLabelTextBlock.height/2,s=k.indexLabelTextBlock.y-k.indexLabelTextBlock.height/2,x=k.indexLabelTextBlock.x+k.indexLabelTextBlock.width,v=k.indexLabelTextBlock.y+k.indexLabelTextBlock.height/2;l=u.indexLabelTextBlock.x+u.indexLabelTextBlock.width<k.indexLabelTextBlock.x-n||l>x+n||m>v+n||r<s-n?!1:!0}l?(0>a&&(a=g),h!==a&&(b=h,c+=-y),0===g%Math.max(p.length/
10,3)&&(e=!0)):e=!0;e&&(0<c&&0<=a&&0<=b)&&(F(c,a,b),b=a=-1,c=0)}}0<c&&F(c,a,b)})()}}function k(){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.render()}var h=this;if(!(0>=a.dataSeriesIndexes.length)){var m=this.data[a.dataSeriesIndexes[0]],p=m.dataPoints,n=10,e=this.plotArea,q=[],r=2,s,x=1.3,u=20/180*Math.PI,v=6,z={x:(e.x2+e.x1)/2,y:(e.y2+e.y1)/2},t=0;a=!1;for(var A=0;A<p.length;A++)t+=Math.abs(p[A].y),!a&&("undefined"!==typeof p[A].indexLabel&&null!==p[A].indexLabel&&0<p[A].indexLabel.toString().length)&&(a=!0),!a&&("undefined"!==typeof p[A].label&&null!==p[A].label&&0<p[A].label.toString().length)&&(a=
!0);if(0!==t){a=a||"undefined"!==typeof m.indexLabel&&null!==m.indexLabel&&0<m.indexLabel.toString().length;var B="inside"!==m.indexLabelPlacement&&a?0.75*Math.min(e.width,e.height)/2:0.92*Math.min(e.width,e.height)/2;m.radius&&(B=Qa(m.radius,B));var E="undefined"!==typeof m.innerRadius&&null!==m.innerRadius?Qa(m.innerRadius,B):0.7*B;m.radius=B;"doughnut"===m.type&&(m.innerRadius=E);var M=Math.min(E/B,(B-1)/B);this.pieDoughnutClickHandler=function(a){h.isAnimating||!y(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);k()}))};c();l();l();l();l();this.disableToolTip=!0;this._animator.animate(0,this.animatedRender?this.animationDuration:0,function(a){var b=h.plotArea.ctx;b.clearRect(e.x1,e.y1,e.width,e.height);b.fillStyle=h.backgroundColor;b.fillRect(e.x1,e.y1,e.width,e.height);a=q[0].startAngle+2*Math.PI*a;for(b=0;b<p.length;b++){var c=0===b?q[b].startAngle:d,d=c+(q[b].endAngle-
q[b].startAngle),f=!1;d>a&&(d=a,f=!0);var g=p[b].color?p[b].color:m._colorSet[b%m._colorSet.length];d>c&&Ha(h.plotArea.ctx,q[b].center,q[b].radius,g,m.type,c,d,m.fillOpacity,q[b].percentInnerRadius);if(f)break}k()},function(){h.disableToolTip=!1;h._animator.animate(0,h.animatedRender?500:0,function(a){b(a);k()})})}}};A.prototype.animationRequestId=null;A.prototype.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||
window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}();A.prototype.cancelRequestAnimFrame=window.cancelAnimationFrame||window.webkitCancelRequestAnimationFrame||window.mozCancelRequestAnimationFrame||window.oCancelRequestAnimationFrame||window.msCancelRequestAnimationFrame||clearTimeout;A.prototype.set=function(a,c,b){b="undefined"===typeof b?!0:b;"options"===a?(this.options=c,b&&this.render()):A.base.set.call(this,a,c,b)};A.prototype.exportChart=
function(a){a="undefined"===typeof a?{}:a;var c=a.format?a.format:"png",b=a.fileName?a.fileName:this.exportFileName;if(a.toDataURL)return this.canvas.toDataURL("image/"+c);Ca(this.canvas,c,b)};A.prototype.print=function(){var a=this.exportChart({toDataURL:!0}),c=document.createElement("iframe");c.setAttribute("class","canvasjs-chart-print-frame");c.setAttribute("style","position:absolute; width:100%; border: 0px; margin: 0px 0px 0px 0px; padding 0px 0px 0px 0px;");c.style.height=this.height+"px";
this._canvasJSContainer.appendChild(c);var b=this,d=c.contentWindow||c.contentDocument.document||c.contentDocument;d.document.open();d.document.write('<!DOCTYPE HTML>\n<html><body style="margin: 0px 0px 0px 0px; padding: 0px 0px 0px 0px;"><img src="'+a+'"/><body/></html>');d.document.close();setTimeout(function(){d.focus();d.print();setTimeout(function(){b._canvasJSContainer.removeChild(c)},1E3)},500)};la.prototype.registerSpace=function(a,c){"top"===a?this._topOccupied+=c.height:"bottom"===a?this._bottomOccupied+=
c.height:"left"===a?this._leftOccupied+=c.width:"right"===a&&(this._rightOccupied+=c.width)};la.prototype.unRegisterSpace=function(a,c){"top"===a?this._topOccupied-=c.height:"bottom"===a?this._bottomOccupied-=c.height:"left"===a?this._leftOccupied-=c.width:"right"===a&&(this._rightOccupied-=c.width)};la.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}};la.prototype.reset=function(){this._rightOccupied=this._leftOccupied=this._bottomOccupied=this._topOccupied=this._padding};T(V,L);V.prototype.render=function(a){if(0!==this.fontSize){a&&this.ctx.save();var c=this.ctx.font;this.ctx.textBaseline=this.textBaseline;var b=0;this._isDirty&&this.measureText(this.ctx);this.ctx.translate(this.x,this.y+b);"middle"===this.textBaseline&&(b=-this._lineHeight/2);this.ctx.font=
this._getFontString();this.ctx.rotate(Math.PI/180*this.angle);var d=0,f=this.padding,g=null;this.ctx.roundRect||Ja(this.ctx);(0<this.borderThickness&&this.borderColor||this.backgroundColor)&&this.ctx.roundRect(0,b,this.width,this.height,this.cornerRadius,this.borderThickness,this.backgroundColor,this.borderColor);this.ctx.fillStyle=this.fontColor;for(b=0;b<this._wrappedText.lines.length;b++)g=this._wrappedText.lines[b],"right"===this.horizontalAlign?d=this.width-g.width-this.padding:"left"===this.horizontalAlign?
d=this.padding:"center"===this.horizontalAlign&&(d=(this.width-2*this.padding)/2-g.width/2+this.padding),this.ctx.fillText(g.text,d,f),f+=g.height;this.ctx.font=c;a&&this.ctx.restore()}};V.prototype.setText=function(a){this.text=a;this._isDirty=!0;this._wrappedText=null};V.prototype.measureText=function(){this._lineHeight=La(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}};V.prototype._getLineWithWidth=function(a,c,b){a=String(a);if(!a)return{text:"",width:0};var d=b=0,f=a.length-1,g=Infinity;for(this.ctx.font=this._getFontString();d<=f;){var g=Math.floor((d+f)/2),l=a.substr(0,g+1);b=this.ctx.measureText(l).width;if(b<c)d=g+1;else if(b>c)f=g-1;else break}b>c&&1<l.length&&(l=l.substr(0,l.length-1),b=this.ctx.measureText(l).width);c=!0;if(l.length===a.length||" "===a[l.length])c=!1;c&&(a=l.split(" "),1<a.length&&a.pop(),l=a.join(" "),b=this.ctx.measureText(l).width);
return{text:l,width:b}};V.prototype._wrapText=function(){var a=new String(ma(String(this.text))),c=[],b=this.ctx.font,d=0,f=0;this.ctx.font=this._getFontString();if(0===this.frontSize)f=d=0;else for(;0<a.length;){var g=this.maxHeight-2*this.padding,l=this._getLineWithWidth(a,this.maxWidth-2*this.padding,!1);l.height=this._lineHeight;c.push(l);var k=f,f=Math.max(f,l.width),d=d+l.height,a=ma(a.slice(l.text.length,a.length));g&&d>g&&(l=c.pop(),d-=l.height,f=k)}this._wrappedText={lines:c,width:f,height:d};
this.width=f+2*this.padding;this.height=d+2*this.padding;this.ctx.font=b};V.prototype._getFontString=function(){var a;a=""+(this.fontStyle?this.fontStyle+" ":"");a+=this.fontWeight?this.fontWeight+" ":"";a+=this.fontSize?this.fontSize+"px ":"";var c=this.fontFamily?this.fontFamily+"":"";!t&&c&&(c=c.split(",")[0],"'"!==c[0]&&'"'!==c[0]&&(c="'"+c+"'"));return a+=c};T(oa,L);oa.prototype.render=function(){if(this.text){var a=this.dockInsidePlotArea?this.chart.plotArea:this.chart,c=a.layoutManager.getFreeSpace(),
b=c.x1,d=c.y1,f=0,g=0,l=this.chart._menuButton&&this.chart.exportEnabled&&"top"===this.verticalAlign?22:0,k,h;"top"===this.verticalAlign||"bottom"===this.verticalAlign?(null===this.maxWidth&&(this.maxWidth=c.width-4-l*("center"===this.horizontalAlign?2:1)),g=0.5*c.height-this.margin-2,f=0):"center"===this.verticalAlign&&("left"===this.horizontalAlign||"right"===this.horizontalAlign?(null===this.maxWidth&&(this.maxWidth=c.height-4),g=0.5*c.width-this.margin-2):"center"===this.horizontalAlign&&(null===
this.maxWidth&&(this.maxWidth=c.width-4),g=0.5*c.height-4));this.wrap||(g=Math.min(g,Math.max(1.5*this.fontSize,this.fontSize+2.5*this.padding)));var g=new V(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:g,
cornerRadius:this.cornerRadius,text:this.text,padding:this.padding,textBaseline:"top"}),m=g.measureText();"top"===this.verticalAlign||"bottom"===this.verticalAlign?("top"===this.verticalAlign?(d=c.y1+2,h="top"):"bottom"===this.verticalAlign&&(d=c.y2-2-m.height,h="bottom"),"left"===this.horizontalAlign?b=c.x1+2:"center"===this.horizontalAlign?b=c.x1+c.width/2-m.width/2:"right"===this.horizontalAlign&&(b=c.x2-2-m.width-l),k=this.horizontalAlign,this.width=m.width,this.height=m.height):"center"===this.verticalAlign&&
("left"===this.horizontalAlign?(b=c.x1+2,d=c.y2-2-(this.maxWidth/2-m.width/2),f=-90,h="left",this.width=m.height,this.height=m.width):"right"===this.horizontalAlign?(b=c.x2-2,d=c.y1+2+(this.maxWidth/2-m.width/2),f=90,h="right",this.width=m.height,this.height=m.width):"center"===this.horizontalAlign&&(d=a.y1+(a.height/2-m.height/2),b=a.x1+(a.width/2-m.width/2),h="center",this.width=m.width,this.height=m.height),k="center");g.x=b;g.y=d;g.angle=f;g.horizontalAlign=k;g.render(!0);a.layoutManager.registerSpace(h,
{width:this.width+("left"===h||"right"===h?this.margin+2:0),height:this.height+("top"===h||"bottom"===h?this.margin+2:0)});this.bounds={x1:b,y1:d,x2:b+this.width,y2:d+this.height};this.ctx.textBaseline="top"}};T(xa,L);xa.prototype.render=oa.prototype.render;T(ya,L);ya.prototype.render=function(){var a=this.dockInsidePlotArea?this.chart.plotArea:this.chart,c=a.layoutManager.getFreeSpace(),b=null,d=0,f=0,g=0,l=0,k=this.markerMargin=this.chart.options.legend&&!y(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:c.width,l=this.maxHeight=null!==this.maxHeight?this.maxHeight:0.5*c.height):"center"===this.verticalAlign&&(this.orientation="vertical",b=this.horizontalAlign,g=this.maxWidth=null!==this.maxWidth?this.maxWidth:0.5*c.width,l=this.maxHeight=null!==this.maxHeight?
this.maxHeight:c.height);for(var p=0;p<this.dataSeries.length;p++){var n=this.dataSeries[p];if(n.dataPoints&&n.dataPoints.length)if("pie"!==n.type&&"doughnut"!==n.type&&"funnel"!==n.type){var e=n.legendMarkerType=n.legendMarkerType?n.legendMarkerType:"line"!==n.type&&"stepLine"!==n.type&&"spline"!==n.type&&"scatter"!==n.type&&"bubble"!==n.type||!n.markerType?ca.getDefaultLegendMarker(n.type):n.markerType,q=n.legendText?n.legendText:this.itemTextFormatter?this.itemTextFormatter({chart:this.chart,legend:this.options,
dataSeries:n,dataPoint:null}):n.name,r=n.legendMarkerColor=n.legendMarkerColor?n.legendMarkerColor:n.markerColor?n.markerColor:n._colorSet[0],s=n.markerSize||"line"!==n.type&&"stepLine"!==n.type&&"spline"!==n.type?0.75*this.lineHeight:0,x=n.legendMarkerBorderColor?n.legendMarkerBorderColor:n.markerBorderColor,u=n.legendMarkerBorderThickness?n.legendMarkerBorderThickness:n.markerBorderThickness?Math.max(1,Math.round(0.2*s)):0,q=this.chart.replaceKeywordsWithValue(q,n.dataPoints[0],n,p),e={markerType:e,
markerColor:r,text:q,textBlock:null,chartType:n.type,markerSize:s,lineColor:n._colorSet[0],dataSeriesIndex:n.index,dataPointIndex:null,markerBorderColor:x,markerBorderThickness:u};h.push(e)}else for(var v=0;v<n.dataPoints.length;v++){var t=n.dataPoints[v],e=t.legendMarkerType?t.legendMarkerType:n.legendMarkerType?n.legendMarkerType:ca.getDefaultLegendMarker(n.type),q=t.legendText?t.legendText:n.legendText?n.legendText:this.itemTextFormatter?this.itemTextFormatter({chart:this.chart,legend:this.options,
dataSeries:n,dataPoint:t}):t.name?t.name:"DataPoint: "+(v+1),r=t.legendMarkerColor?t.legendMarkerColor:n.legendMarkerColor?n.legendMarkerColor:t.color?t.color:n.color?n.color:n._colorSet[v%n._colorSet.length],s=0.75*this.lineHeight,x=t.legendMarkerBorderColor?t.legendMarkerBorderColor:n.legendMarkerBorderColor?n.legendMarkerBorderColor:t.markerBorderColor?t.markerBorderColor:n.markerBorderColor,u=t.legendMarkerBorderThickness?t.legendMarkerBorderThickness:n.legendMarkerBorderThickness?n.legendMarkerBorderThickness:
t.markerBorderThickness||n.markerBorderThickness?Math.max(1,Math.round(0.2*s)):0,q=this.chart.replaceKeywordsWithValue(q,t,n,v),e={markerType:e,markerColor:r,text:q,textBlock:null,chartType:n.type,markerSize:s,dataSeriesIndex:p,dataPointIndex:v,markerBorderColor:x,markerBorderThickness:u};(t.showInLegend||n.showInLegend&&!1!==t.showInLegend)&&h.push(e)}}!0===this.reversed&&h.reverse();if(0<h.length){n=null;r=v=q=t=0;q=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;s=0===s?0.75*this.lineHeight:s;q-=s+k;for(p=0;p<h.length;p++){e=h[p];if("line"===e.chartType||"spline"===e.chartType||"stepLine"===e.chartType)q-=2*0.1*this.lineHeight;if(!(0>=l||"undefined"===typeof l||0>=q||"undefined"===typeof q)){if("horizontal"===this.orientation){e.textBlock=new V(this.ctx,{x:0,y:0,maxWidth:q,maxHeight:this.itemWrap?l:this.lineHeight,angle:0,text:e.text,
horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"});e.textBlock.measureText();null!==this.itemWidth&&(e.textBlock.width=this.itemWidth-(s+k+("line"===e.chartType||"spline"===e.chartType||"stepLine"===e.chartType?2*0.1*this.lineHeight:0)));if(!n||n.width+Math.round(e.textBlock.width+s+k+(0===n.width?0:this.horizontalSpacing)+("line"===e.chartType||"spline"===e.chartType||"stepLine"===
e.chartType?2*0.1*this.lineHeight:0))>g)n={items:[],width:0},m.push(n),this.height+=v,v=0;v=Math.max(v,e.textBlock.height)}else e.textBlock=new V(this.ctx,{x:0,y:0,maxWidth:q,maxHeight:!0===this.itemWrap?l:1.5*this.fontSize,angle:0,text:e.text,horizontalAlign:"left",fontSize:this.fontSize,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontColor:this.fontColor,fontStyle:this.fontStyle,textBaseline:"middle"}),e.textBlock.measureText(),null!==this.itemWidth&&(e.textBlock.width=this.itemWidth-
(s+k+("line"===e.chartType||"spline"===e.chartType||"stepLine"===e.chartType?2*0.1*this.lineHeight:0))),this.height<l-this.lineHeight?(n={items:[],width:0},m.push(n)):(n=m[t],t=(t+1)%m.length),this.height+=e.textBlock.height;e.textBlock.x=n.width;e.textBlock.y=0;n.width+=Math.round(e.textBlock.width+s+k+(0===n.width?0:this.horizontalSpacing)+("line"===e.chartType||"spline"===e.chartType||"stepLine"===e.chartType?2*0.1*this.lineHeight:0));n.items.push(e);this.width=Math.max(n.width,this.width);r=e.textBlock.width+
(s+k+("line"===e.chartType||"spline"===e.chartType||"stepLine"===e.chartType?2*0.1*this.lineHeight:0))}}this.itemWidth=r;this.height=!1===this.itemWrap?m.length*this.lineHeight:this.height+v;this.height=Math.min(l,this.height);this.width=Math.min(g,this.width)}"top"===this.verticalAlign?(f="left"===this.horizontalAlign?c.x1:"right"===this.horizontalAlign?c.x2-this.width:c.x1+c.width/2-this.width/2,d=c.y1):"center"===this.verticalAlign?(f="left"===this.horizontalAlign?c.x1:"right"===this.horizontalAlign?
c.x2-this.width:c.x1+c.width/2-this.width/2,d=c.y1+c.height/2-this.height/2):"bottom"===this.verticalAlign&&(f="left"===this.horizontalAlign?c.x1:"right"===this.horizontalAlign?c.x2-this.width:c.x1+c.width/2-this.width/2,d=c.y2-this.height);this.items=h;for(p=0;p<this.items.length;p++)e=h[p],e.id=++this.chart._eventManager.lastObjectId,this.chart._eventManager.objectMap[e.id]={id:e.id,objectType:"legendItem",legendItemIndex:p,dataSeriesIndex:e.dataSeriesIndex,dataPointIndex:e.dataPointIndex};(0<this.borderThickness&&
this.borderColor||this.backgroundColor)&&this.ctx.roundRect(f,d,this.width,this.height,this.cornerRadius,this.borderThickness,this.backgroundColor,this.borderColor);for(p=c=0;p<m.length;p++){n=m[p];for(t=v=0;t<n.items.length;t++){e=n.items[t];r=e.textBlock.x+f+(0===t?0.2*s:this.horizontalSpacing);x=d+c;q=r;this.chart.data[e.dataSeriesIndex].visible||(this.ctx.globalAlpha=0.5);this.ctx.save();this.ctx.beginPath();this.ctx.rect(f,d,g,Math.max(l-l%this.lineHeight,0));this.ctx.clip();if("line"===e.chartType||
"stepLine"===e.chartType||"spline"===e.chartType)this.ctx.strokeStyle=e.lineColor,this.ctx.lineWidth=Math.ceil(this.lineHeight/8),this.ctx.beginPath(),this.ctx.moveTo(r-0.1*this.lineHeight,x+this.lineHeight/2),this.ctx.lineTo(r+0.85*this.lineHeight,x+this.lineHeight/2),this.ctx.stroke(),q-=0.1*this.lineHeight;O.drawMarker(r+s/2,x+this.lineHeight/2,this.ctx,e.markerType,e.markerSize,e.markerColor,e.markerBorderColor,e.markerBorderThickness);e.textBlock.x=r+k+s;if("line"===e.chartType||"stepLine"===
e.chartType||"spline"===e.chartType)e.textBlock.x+=0.1*this.lineHeight;e.textBlock.y=Math.round(x+this.lineHeight/2);e.textBlock.render(!0);this.ctx.restore();v=0<t?Math.max(v,e.textBlock.height):e.textBlock.height;this.chart.data[e.dataSeriesIndex].visible||(this.ctx.globalAlpha=1);r=D(e.id);this.ghostCtx.fillStyle=r;this.ghostCtx.beginPath();this.ghostCtx.fillRect(q,e.textBlock.y-this.lineHeight/2,e.textBlock.x+e.textBlock.width-q,e.textBlock.height);e.x1=this.chart._eventManager.objectMap[e.id].x1=
q;e.y1=this.chart._eventManager.objectMap[e.id].y1=e.textBlock.y-this.lineHeight/2;e.x2=this.chart._eventManager.objectMap[e.id].x2=e.textBlock.x+e.textBlock.width;e.y2=this.chart._eventManager.objectMap[e.id].y2=e.textBlock.y+e.textBlock.height-this.lineHeight/2}c+=v}0<h.length&&a.layoutManager.registerSpace(b,{width:this.width+2+2,height:this.height+5+5});this.bounds={x1:f,y1:d,x2:f+this.width,y2:d+this.height}};T(Da,L);Da.prototype.render=function(){var a=this.chart.layoutManager.getFreeSpace();
this.ctx.fillStyle="red";this.ctx.fillRect(a.x1,a.y1,a.x2,a.y2)};T(ca,L);ca.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)return"normal";
if("bar"===a||"stackedBar"===a||"stackedBar100"===a||"rangeBar"===a)return"xySwapped";if("pie"===a||"doughnut"===a||"funnel"===a)return"none";window.console.log("Unknown Chart Type: "+a);return null};ca.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)return"square";if("line"===a||"stepLine"===a||"spline"===a||"pie"===a||"doughnut"===a||"funnel"===a)return"circle";if("area"===a||"splineArea"===a||"stackedArea"===a||"stackedArea100"===a)return"triangle";window.console.log("Unknown Chart Type: "+a);return null};ca.prototype.getDataPointAtX=function(a,c){if(!this.dataPoints||0===this.dataPoints.length)return null;var b={dataPoint:null,distance:Infinity,index:NaN},d=null,f=0,g=0,l=1,k=Infinity,h=0,m=0,p=0;"none"!==
this.chart.plotInfo.axisPlacement&&(this.axisX.logarithmic?(p=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),p=1<p?Math.min(Math.max((this.dataPoints.length-1)/p*Math.log(a/this.dataPoints[0].x)>>0,0),this.dataPoints.length):0):(p=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,p=0<p?Math.min(Math.max((this.dataPoints.length-1)/p*(a-this.dataPoints[0].x)>>0,0),this.dataPoints.length):0));for(;;){g=0<l?p+f:p-f;if(0<=g&&g<this.dataPoints.length){var d=this.dataPoints[g],
n=this.axisX.logarithmic?d.x>a?d.x/a:a/d.x:Math.abs(d.x-a);n<b.distance&&(b.dataPoint=d,b.distance=n,b.index=g);d=n;d<=k?k=d:0<l?h++:m++;if(1E3<h&&1E3<m)break}else if(0>p-f&&p+f>=this.dataPoints.length)break;-1===l?(f++,l=1):l=-1}return c||b.dataPoint.x!==a?c&&null!==b.dataPoint?b:null:b};ca.prototype.getDataPointAtXY=function(a,c,b){if(!this.dataPoints||0===this.dataPoints.length||a<this.chart.plotArea.x1||a>this.chart.plotArea.x2||c<this.chart.plotArea.y1||c>this.chart.plotArea.y2)return null;b=
b||!1;var d=[],f=0,g=0,l=1,k=!1,h=Infinity,m=0,p=0,n=0;"none"!==this.chart.plotInfo.axisPlacement&&(n=(this.chart.axisX[0]?this.chart.axisX[0]:this.chart.axisX2[0]).getXValueAt({x:a,y:c}),this.axisX.logarithmic?(g=Math.log(this.dataPoints[this.dataPoints.length-1].x/this.dataPoints[0].x),n=1<g?Math.min(Math.max((this.dataPoints.length-1)/g*Math.log(n/this.dataPoints[0].x)>>0,0),this.dataPoints.length):0):(g=this.dataPoints[this.dataPoints.length-1].x-this.dataPoints[0].x,n=0<g?Math.min(Math.max((this.dataPoints.length-
1)/g*(n-this.dataPoints[0].x)>>0,0),this.dataPoints.length):0));for(;;){g=0<l?n+f:n-f;if(0<=g&&g<this.dataPoints.length){var e=this.chart._eventManager.objectMap[this.dataPointIds[g]],q=this.dataPoints[g],r=null;if(e){switch(this.type){case "column":case "stackedColumn":case "stackedColumn100":case "bar":case "stackedBar":case "stackedBar100":case "rangeColumn":case "rangeBar":a>=e.x1&&(a<=e.x2&&c>=e.y1&&c<=e.y2)&&(d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(e.x1-
a),Math.abs(e.x2-a),Math.abs(e.y1-c),Math.abs(e.y2-c))}),k=!0);break;case "line":case "stepLine":case "spline":case "area":case "stepArea":case "stackedArea":case "stackedArea100":case "splineArea":case "scatter":var s=K("markerSize",q,this)||4,t=b?20:s,r=Math.sqrt(Math.pow(e.x1-a,2)+Math.pow(e.y1-c,2));r<=t&&d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:r});g=Math.abs(e.x1-a);g<=h?h=g:0<l?m++:p++;r<=s/2&&(k=!0);break;case "rangeArea":case "rangeSplineArea":s=K("markerSize",q,this)||
4;t=b?20:s;r=Math.min(Math.sqrt(Math.pow(e.x1-a,2)+Math.pow(e.y1-c,2)),Math.sqrt(Math.pow(e.x1-a,2)+Math.pow(e.y2-c,2)));r<=t&&d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:r});g=Math.abs(e.x1-a);g<=h?h=g:0<l?m++:p++;r<=s/2&&(k=!0);break;case "bubble":s=e.size;r=Math.sqrt(Math.pow(e.x1-a,2)+Math.pow(e.y1-c,2));r<=s/2&&(d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:r}),k=!0);break;case "pie":case "doughnut":s=e.center;t="doughnut"===this.type?e.percentInnerRadius*e.radius:
0;r=Math.sqrt(Math.pow(s.x-a,2)+Math.pow(s.y-c,2));r<e.radius&&r>t&&(r=Math.atan2(c-s.y,a-s.x),0>r&&(r+=2*Math.PI),r=Number(((180*(r/Math.PI)%360+360)%360).toFixed(12)),s=Number(((180*(e.startAngle/Math.PI)%360+360)%360).toFixed(12)),t=Number(((180*(e.endAngle/Math.PI)%360+360)%360).toFixed(12)),0===t&&1<e.endAngle&&(t=360),s>=t&&0!==q.y&&(t+=360,r<s&&(r+=360)),r>s&&r<t&&(d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:0}),k=!0));break;case "candlestick":if(a>=e.x1-e.borderThickness/
2&&a<=e.x2+e.borderThickness/2&&c>=e.y2-e.borderThickness/2&&c<=e.y3+e.borderThickness/2||Math.abs(e.x2-a+e.x1-a)<e.borderThickness&&c>=e.y1&&c<=e.y4)d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(e.x1-a),Math.abs(e.x2-a),Math.abs(e.y2-c),Math.abs(e.y3-c))}),k=!0;break;case "ohlc":if(Math.abs(e.x2-a+e.x1-a)<e.borderThickness&&c>=e.y2&&c<=e.y3||a>=e.x1&&a<=(e.x2+e.x1)/2&&c>=e.y1-e.borderThickness/2&&c<=e.y1+e.borderThickness/2||a>=(e.x1+e.x2)/2&&a<=e.x2&&c>=e.y4-e.borderThickness/
2&&c<=e.y4+e.borderThickness/2)d.push({dataPoint:q,dataPointIndex:g,dataSeries:this,distance:Math.min(Math.abs(e.x1-a),Math.abs(e.x2-a),Math.abs(e.y2-c),Math.abs(e.y3-c))}),k=!0}if(k||1E3<m&&1E3<p)break}}else if(0>n-f&&n+f>=this.dataPoints.length)break;-1===l?(f++,l=1):l=-1}a=null;for(c=0;c<d.length;c++)a?d[c].distance<=a.distance&&(a=d[c]):a=d[c];return a};ca.prototype.getMarkerProperties=function(a,c,b,d){var f=this.dataPoints;return{x:c,y:b,ctx:d,type:f[a].markerType?f[a].markerType:this.markerType,
size:f[a].markerSize?f[a].markerSize:this.markerSize,color:f[a].markerColor?f[a].markerColor:this.markerColor?this.markerColor:f[a].color?f[a].color:this.color?this.color:this._colorSet[a%this._colorSet.length],borderColor:f[a].markerBorderColor?f[a].markerBorderColor:this.markerBorderColor?this.markerBorderColor:null,borderThickness:f[a].markerBorderThickness?f[a].markerBorderThickness:this.markerBorderThickness?this.markerBorderThickness:null}};T(B,L);B.prototype.createExtraLabelsForLog=function(a){a=
(a||0)+1;if(!(5<a)){var c=this.logLabelValues[0]||this.intervalStartPosition;if(Math.log(this.range)/Math.log(c/this.viewportMinimum)<this.noTicks-1){for(var b=B.getNiceNumber((c-this.viewportMinimum)/Math.min(Math.max(2,this.noTicks-this.logLabelValues.length),3),!0),d=Math.ceil(this.viewportMinimum/b)*b;d<c;d+=b)d<this.viewportMinimum||this.logLabelValues.push(d);this.logLabelValues.sort(Aa);this.createExtraLabelsForLog(a)}}};B.prototype.createLabels=function(){var a,c,b=0,d=0,f,g=0,l=0,d=0,k=this.interval,
h=0,m,p=0.6*this.chart.height,b=!1;if(this.dataSeries&&0<this.dataSeries.length)for(d=0;d<this.dataSeries.length;d++)"dateTime"===this.dataSeries[d].xValueType&&(b=!0);if("axisX"===this.type&&b&&!this.logarithmic)for(this.intervalStartPosition=this.getLabelStartPoint(new Date(this.viewportMinimum),this.intervalType,this.interval),f=Ia(new Date(this.viewportMaximum),this.interval,this.intervalType),b=this.intervalStartPosition;b<f;Ia(b,k,this.intervalType))a=b.getTime(),a=this.labelFormatter?this.labelFormatter({chart:this.chart,
axis:this.options,value:b,label:this.labels[b]?this.labels[b]:null}):"axisX"===this.type&&this.labels[a]?this.labels[a]:Fa(b,this.valueFormatString,this.chart._cultureInfo),a=new V(this.ctx,{x:0,y:0,maxWidth:g,backgroundColor:this.labelBackgroundColor,borderColor:this.labelBorderColor,borderThickness:this.labelBorderThickness,cornerRadius:this.labelCornerRadius,maxHeight:l,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:b.getTime(),textBlock:a,effectiveHeight:null});else{f=this.viewportMaximum;if(this.labels){a=Math.ceil(k);for(var k=Math.ceil(this.intervalStartPosition),n=!1,b=k;b<this.viewportMaximum;b+=a)if(this.labels[b])n=!0;else{n=!1;break}n&&(this.interval=a,this.intervalStartPosition=k)}if(this.logarithmic&&!this.equidistantInterval){this.logLabelValues||(this.logLabelValues=
[],this.createExtraLabelsForLog());for(var e=0;e<this.logLabelValues.length;e++)b=this.logLabelValues[e],b<this.viewportMinimum||(a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:b,label:this.labels[b]?this.labels[b]:null}):"axisX"===this.type&&this.labels[b]?this.labels[b]:fa(b,this.valueFormatString,this.chart._cultureInfo),a=new V(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:l,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"}),this._labels.push({position:b,textBlock:a,effectiveHeight:null}))}for(b=this.intervalStartPosition;b<=f;b=parseFloat((this.logarithmic&&this.equidistantInterval?b*Math.pow(this.logarithmBase,
this.interval):b+this.interval).toFixed(12)))a=this.labelFormatter?this.labelFormatter({chart:this.chart,axis:this.options,value:b,label:this.labels[b]?this.labels[b]:null}):"axisX"===this.type&&this.labels[b]?this.labels[b]:fa(b,this.valueFormatString,this.chart._cultureInfo),a=new V(this.ctx,{x:0,y:0,maxWidth:g,maxHeight:l,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:b,textBlock:a,effectiveHeight:null})}if("bottom"===this._position||"top"===this._position)h=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))*G[this.intervalType+"Duration"]*this.interval,g="undefined"===typeof this.options.labelMaxWidth?0.5*this.chart.width>>0:this.options.labelMaxWidth,this.chart.panEnabled||(l="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)h=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))*G[this.intervalType+
"Duration"]*this.interval,this.chart.panEnabled||(g="undefined"===typeof this.options.labelMaxWidth?0.3*this.chart.width>>0:this.options.labelMaxWidth),l="undefined"===typeof this.options.labelWrap||this.labelWrap?0.3*this.chart.height>>0:1.5*this.labelFontSize;for(d=0;d<this._labels.length;d++){a=this._labels[d].textBlock;a.maxWidth=g;a.maxHeight=l;var q=a.measureText();m=q.height}f=[];n=k=0;if(this.labelAutoFit||this.options.labelAutoFit)if(y(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*h>>0,n=0,!this.chart.panEnabled&&1<=this._labels.length){this.sessionVariables.labelFontSize=this.labelFontSize;this.sessionVariables.labelMaxWidth=g;this.sessionVariables.labelMaxHeight=l;this.sessionVariables.labelAngle=this.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b<this._labels.length;b++){a=
this._labels[b].textBlock;for(var r,s=a.text.split(" "),d=0;d<s.length;d++)e=s[d],this.ctx.font=a.fontStyle+" "+a.fontWeight+" "+a.fontSize+"px "+a.fontFamily,e=this.ctx.measureText(e),e.width>n&&(r=b,n=e.width)}b=0;for(b=this.intervalStartPosition<this.viewportMinimum?1:0;b<this._labels.length;b++)if(a=this._labels[b].textBlock,q=a.measureText(),b<this._labels.length-1&&(e=b+1,c=this._labels[e].textBlock,c=c.measureText()),f.push(a.height),this.sessionVariables.labelMaxHeight=Math.max.apply(Math,
f),Math.cos(Math.PI/180*Math.abs(this.labelAngle)),Math.sin(Math.PI/180*Math.abs(this.labelAngle)),d=g*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(l-a.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)),y(this.options.labelAngle)&&isNaN(this.options.labelAngle)&&0!==this.options.labelAngle)if(this.sessionVariables.labelMaxHeight=0===this.labelAngle?l:Math.min((d-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)),d),s=(p-(m+a.fontSize/2)*
Math.cos(Math.PI/180*Math.abs(-25)))/Math.sin(Math.PI/180*Math.abs(-25)),!y(this.options.labelWrap))this.labelWrap?y(this.options.labelMaxWidth)?(this.sessionVariables.labelMaxWidth=Math.min(Math.max(g,n),s),this.sessionVariables.labelWrap=this.labelWrap,q.width+c.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):y(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxHeight=l,this.sessionVariables.labelMaxWidth=g,q.width+c.width>>0>2*g&&(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=s)):(this.sessionVariables.labelAngle=this.sessionVariables.labelMaxWidth>g?-25:this.sessionVariables.labelAngle,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=
l,this.sessionVariables.labelWrap=this.labelWrap);else{if(y(this.options.labelWrap))if(!y(this.options.labelMaxWidth))this.options.labelMaxWidth<g?(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=d):(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth,this.sessionVariables.labelMaxHeight=l);else if(!y(c))if(d=q.width+c.width>>0,e=this.labelFontSize,n<g)d-2*g>k&&(k=d-2*g,d>=2*g&&d<2.2*g?(this.sessionVariables.labelMaxWidth=
g,y(this.options.labelFontSize)&&12<e&&(e=Math.floor(12/13*e),a.measureText()),this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?e:this.options.labelFontSize,this.sessionVariables.labelAngle=this.labelAngle):d>=2.2*g&&d<2.8*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=s,this.sessionVariables.labelFontSize=e):d>=2.8*g&&d<3.2*g?(this.sessionVariables.labelMaxWidth=Math.max(g,n),this.sessionVariables.labelWrap=!0,y(this.options.labelFontSize)&&12<this.labelFontSize&&
(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?e:this.options.labelFontSize,this.sessionVariables.labelAngle=this.labelAngle):d>=3.2*g&&d<3.6*g?(this.sessionVariables.labelAngle=-25,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=s,this.sessionVariables.labelFontSize=this.labelFontSize):d>3.6*g&&d<5*g?(y(this.options.labelFontSize)&&12<e&&(e=Math.floor(12/13*e),a.measureText()),this.sessionVariables.labelFontSize=
y(this.options.labelFontSize)?e:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=s):d>5*g&&(this.sessionVariables.labelWrap=!0,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelFontSize=e,this.sessionVariables.labelMaxHeight=l,this.sessionVariables.labelAngle=this.labelAngle));else if(r===b&&(0===r&&n+this._labels[r+1].textBlock.measureText().width-2*g>k||r===this._labels.length-1&&n+this._labels[r-
1].textBlock.measureText().width-2*g>k||0<r&&r<this._labels.length-1&&n+this._labels[r+1].textBlock.measureText().width-2*g>k&&n+this._labels[r-1].textBlock.measureText().width-2*g>k))k=0===r?n+this._labels[r+1].textBlock.measureText().width-2*g:n+this._labels[r-1].textBlock.measureText().width-2*g,this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?e:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelAngle=-25,this.sessionVariables.labelMaxWidth=
s;else if(0===k)for(this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?e:this.options.labelFontSize,this.sessionVariables.labelWrap=!0,d=0;d<this._labels.length;d++)a=this._labels[d].textBlock,a.maxWidth=this.sessionVariables.labelMaxWidth=Math.min(Math.max(g,n),s),q=a.measureText(),d<this._labels.length-1&&(e=d+1,c=this._labels[e].textBlock,c.maxWidth=this.sessionVariables.labelMaxWidth=Math.min(Math.max(g,n),s),c=c.measureText(),q.width+c.width>>0>2*g&&(this.sessionVariables.labelAngle=
-25))}else(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxHeight=0===this.labelAngle?l:Math.min((d-g*Math.cos(Math.PI/180*Math.abs(this.labelAngle)))/Math.sin(Math.PI/180*Math.abs(this.labelAngle)),d),s=0!=this.labelAngle?(p-(m+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=l=this.labelWrap?(p-s*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/Math.cos(Math.PI/180*
Math.abs(this.labelAngle)):1.5*this.labelFontSize,y(this.options.labelWrap))?y(this.options.labelWrap)&&(this.labelWrap&&!y(this.options.labelMaxWidth)?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:s,this.sessionVariables.labelMaxHeight=l):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=s,this.sessionVariables.labelMaxHeight=d<0.9*h?0.9*h:d,this.sessionVariables.labelWrap=
this.labelWrap)):(this.options.labelWrap?(this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:s):(y(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:s,this.sessionVariables.labelWrap=this.labelWrap),this.sessionVariables.labelMaxHeight=l);for(d=0;d<this._labels.length;d++)a=this._labels[d].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=y(this.options.labelMaxWidth)?this.sessionVariables.labelMaxWidth:this.options.labelMaxWidth,a.fontSize=this.labelFontSize=y(this.options.labelFontSize)?this.sessionVariables.labelFontSize:
this.options.labelFontSize,a.angle=this.labelAngle=y(this.options.labelAngle)?this.sessionVariables.labelAngle:this.labelAngle,a.wrap=this.labelWrap=y(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=y(this.options.labelMaxWidth)?0.3*this.chart.width>>0:this.options.labelMaxWidth,l="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=l;this.sessionVariables.labelAngle=y(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle;this.sessionVariables.labelWrap=this.labelWrap;for(b=0;b<this._labels.length;b++)(a=this._labels[b].textBlock,q=a.measureText(),b<this._labels.length-1&&(e=b+1,c=this._labels[e].textBlock,
c=c.measureText()),f.push(a.height),this.sessionVariables.labelMaxHeight=Math.max.apply(Math,f),d=g*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(l-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)),y(this.options.labelAngle)&&isNaN(this.options.labelAngle)&&0!==this.options.labelAngle)?y(this.options.labelWrap)?y(this.options.labelWrap)&&(y(this.options.labelMaxWidth)?y(c)||(h=q.height+c.height>>
0,h-2*l>n&&(n=h-2*l,h>=2*l&&h<2.4*l?(y(this.options.labelFontSize)&&12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelMaxHeight=l,this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize):h>=2.4*l&&h<2.8*l?(this.sessionVariables.labelMaxHeight=d,this.sessionVariables.labelFontSize=this.labelFontSize,this.sessionVariables.labelWrap=!0):h>=2.8*l&&h<3.2*l?(this.sessionVariables.labelMaxHeight=
l,this.sessionVariables.labelWrap=!0,y(this.options.labelFontSize)&&12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelAngle=y(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):h>=3.2*l&&h<3.6*l?(this.sessionVariables.labelMaxHeight=d,this.sessionVariables.labelWrap=!0,this.sessionVariables.labelFontSize=
this.labelFontSize):h>3.6*l&&h<10*l?(y(this.options.labelFontSize)&&12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelMaxHeight=l,this.sessionVariables.labelAngle=y(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle):h>10*l&&h<50*l&&(y(this.options.labelFontSize)&&
12<this.labelFontSize&&(this.labelFontSize=Math.floor(12/13*this.labelFontSize),a.measureText()),this.sessionVariables.labelFontSize=y(this.options.labelFontSize)?this.labelFontSize:this.options.labelFontSize,this.sessionVariables.labelMaxHeight=l,this.sessionVariables.labelMaxWidth=g,this.sessionVariables.labelAngle=y(this.sessionVariables.labelAngle)?0:this.sessionVariables.labelAngle))):(this.sessionVariables.labelMaxHeight=l,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=l):(this.sessionVariables.labelAngle=this.labelAngle,this.sessionVariables.labelMaxWidth=0===this.labelAngle?g:Math.min((d-l*Math.sin(Math.PI/180*Math.abs(this.labelAngle)))/
Math.cos(Math.PI/180*Math.abs(this.labelAngle)),l),y(this.options.labelWrap))?y(this.options.labelWrap)&&(this.labelWrap&&!y(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=d):(this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:g,this.sessionVariables.labelMaxHeight=
0===this.labelAngle?l:d,y(this.options.labelMaxWidth)&&(this.sessionVariables.labelAngle=this.labelAngle))):this.options.labelWrap?(this.sessionVariables.labelMaxHeight=0===this.labelAngle?l:d,this.sessionVariables.labelWrap=this.labelWrap,this.sessionVariables.labelMaxWidth=g):(this.sessionVariables.labelMaxHeight=l,y(this.options.labelMaxWidth),this.sessionVariables.labelMaxWidth=this.options.labelMaxWidth?this.options.labelMaxWidth:this.sessionVariables.labelMaxWidth,this.sessionVariables.labelWrap=
this.labelWrap);for(d=0;d<this._labels.length;d++)a=this._labels[d].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=y(this.options.labelMaxWidth)?
this.sessionVariables.labelMaxWidth:this.options.labelMaxWidth,a.fontSize=this.labelFontSize=y(this.options.labelFontSize)?this.sessionVariables.labelFontSize:this.options.labelFontSize,a.angle=this.labelAngle=y(this.options.labelAngle)?this.sessionVariables.labelAngle:this.labelAngle,a.wrap=this.labelWrap=y(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],
x;if("outside"===g.labelPlacement){l=this.sessionVariables.labelMaxWidth;if("bottom"===this._position||"top"===this._position)x="undefined"===typeof 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)x="undefined"===typeof g.options.labelWrap?this.sessionVariables.labelMaxHeight:g.labelWrap?0.8*this.chart.width>>0:1.5*this.labelFontSize;c=y(g.options.labelBackgroundColor)?"#EEEEEE":
g.options.labelBackgroundColor}else l="bottom"===this._position||"top"===this._position?0.9*this.chart.width>>0:0.9*this.chart.height>>0,x="undefined"===typeof 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,c=y(g.options.labelBackgroundColor)?y(g.startValue)&&0!==g.startValue?t?"transparent":null:"#EEEEEE":g.options.labelBackgroundColor;a=new V(this.ctx,{x:0,y:0,backgroundColor:c,borderColor:g.labelBorderColor,
borderThickness:g.labelBorderThickness,cornerRadius:g.labelCornerRadius,maxWidth:g.options.labelMaxWidth?g.options.labelMaxWidth:l,maxHeight:x,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.options.labelFontSize:this.labelFontSize:g.labelFontSize,fontFamily:"outside"===g.labelPlacement?g.options.labelFontFamily?g.options.labelFontFamily:this.labelFontFamily:
g.labelFontFamily,fontWeight:"outside"===g.labelPlacement?g.options.fontWeight?g.options.fontWeight:this.fontWeight:g.fontWeight,fontColor:g.options.labelFontColor||g.color,fontStyle:"outside"===g.labelPlacement?g.options.fontStyle?g.options.fontStyle:this.fontWeight:g.fontStyle,textBaseline:"middle"});this._stripLineLabels.push({position:g.value,textBlock:a,effectiveHeight:null,stripLine:g})}};B.prototype.createLabelsAndCalculateWidth=function(){var a=0,c=0;this._labels=[];this._stripLineLabels=
[];if("left"===this._position||"right"===this._position){this.createLabels();for(c=0;c<this._labels.length;c++){var b=this._labels[c].textBlock,d=b.measureText(),f=0,f=0===this.labelAngle?d.width:d.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(d.height-b.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle));a<f&&(a=f);this._labels[c].effectiveWidth=f}for(c=0;c<this._stripLineLabels.length;c++)"outside"===this._stripLineLabels[c].stripLine.labelPlacement&&(this._stripLineLabels[c].stripLine.value>
this.viewportMinimum&&this._stripLineLabels[c].stripLine.value<this.viewportMaximum)&&(b=this._stripLineLabels[c].textBlock,d=b.measureText(),f=0===this.labelAngle?d.width:d.width*Math.cos(Math.PI/180*Math.abs(this.labelAngle))+(d.height-b.fontSize/2)*Math.sin(Math.PI/180*Math.abs(this.labelAngle)),a<f&&(a=f),this._stripLineLabels[c].effectiveWidth=f)}return(this.title?this._titleTextBlock.measureText().height+2:0)+a+this.tickLength+5};B.prototype.createLabelsAndCalculateHeight=function(){var a=0;
this._labels=[];this._stripLineLabels=[];var c,b=0;this.createLabels();if("bottom"===this._position||"top"===this._position){for(b=0;b<this._labels.length;b++){c=this._labels[b].textBlock;var d=c.measureText(),f=0,f=0===this.labelAngle?d.height:d.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(d.height-c.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle));a<f&&(a=f);this._labels[b].effectiveHeight=f}for(b=0;b<this._stripLineLabels.length;b++)"outside"===this._stripLineLabels[b].stripLine.labelPlacement&&
(c=this._stripLineLabels[b].textBlock,d=c.measureText(),f=0===this.labelAngle?d.height:d.width*Math.sin(Math.PI/180*Math.abs(this.labelAngle))+(d.height-c.fontSize/2)*Math.cos(Math.PI/180*Math.abs(this.labelAngle)),a<f&&(a=f),this._stripLineLabels[b].effectiveHeight=f)}return(this.title?this._titleTextBlock.measureText().height+2:0)+a+this.tickLength+5};B.setLayoutAndRender=function(a,c,b,d,f,g){var l,k,h,m,p=a[0]?a[0].chart:c[0].chart,n=p.ctx;if(a&&0<a.length)for(var e=0;e<a.length;e++)a[e]&&a[e].calculateAxisParameters();
if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].calculateAxisParameters();if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].calculateAxisParameters();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].calculateAxisParameters();var q=0,r=0,s=0,t=0,u=0,v=0,z=0,w,A,B=k=0,D,E,F,G;D=E=F=G=!1;if(a&&0<a.length)for(e=0;e<a.length;e++)a[e]&&a[e].title&&(a[e]._titleTextBlock=new V(a[e].ctx,{text:a[e].title,horizontalAlign:"center",fontSize:a[e].titleFontSize,fontFamily:a[e].titleFontFamily,fontWeight:a[e].titleFontWeight,
fontColor:a[e].titleFontColor,fontStyle:a[e].titleFontStyle,borderColor:a[e].titleBorderColor,borderThickness:a[e].titleBorderThickness,backgroundColor:a[e].titleBackgroundColor,cornerRadius:a[e].titleCornerRadius,textBaseline:"top"}));if(c&&0<c.length)for(e=0;e<c.length;e++)c[e]&&c[e].title&&(c[e]._titleTextBlock=new V(c[e].ctx,{text:c[e].title,horizontalAlign:"center",fontSize:c[e].titleFontSize,fontFamily:c[e].titleFontFamily,fontWeight:c[e].titleFontWeight,fontColor:c[e].titleFontColor,fontStyle:c[e].titleFontStyle,
borderColor:c[e].titleBorderColor,borderThickness:c[e].titleBorderThickness,backgroundColor:c[e].titleBackgroundColor,cornerRadius:c[e].titleCornerRadius,textBaseline:"top"}));if(b&&0<b.length)for(e=0;e<b.length;e++)b[e]&&b[e].title&&(b[e]._titleTextBlock=new V(b[e].ctx,{text:b[e].title,horizontalAlign:"center",fontSize:b[e].titleFontSize,fontFamily:b[e].titleFontFamily,fontWeight:b[e].titleFontWeight,fontColor:b[e].titleFontColor,fontStyle:b[e].titleFontStyle,borderColor:b[e].titleBorderColor,borderThickness:b[e].titleBorderThickness,
backgroundColor:b[e].titleBackgroundColor,cornerRadius:b[e].titleCornerRadius,textBaseline:"top"}));if(d&&0<d.length)for(e=0;e<d.length;e++)d[e]&&d[e].title&&(d[e]._titleTextBlock=new V(d[e].ctx,{text:d[e].title,horizontalAlign:"center",fontSize:d[e].titleFontSize,fontFamily:d[e].titleFontFamily,fontWeight:d[e].titleFontWeight,fontColor:d[e].titleFontColor,fontStyle:d[e].titleFontStyle,borderColor:d[e].titleBorderColor,borderThickness:d[e].titleBorderThickness,backgroundColor:d[e].titleBackgroundColor,
cornerRadius:d[e].titleCornerRadius,textBaseline:"top"}));if("normal"===f){var t=[],u=[],v=[],z=[],H=[],I=[],L=[],J=[];if(a&&0<a.length)for(e=0;e<a.length;e++)a[e]&&a[e].title&&(a[e]._titleTextBlock.maxWidth=a[e].titleMaxWidth||g.width,a[e]._titleTextBlock.maxHeight=a[e].titleWrap?0.8*g.height:1.5*a[e].titleFontSize,a[e]._titleTextBlock.angle=0);if(c&&0<c.length)for(e=0;e<c[e].length;e++)c[e]&&c[e].title&&(c[e]._titleTextBlock.maxWidth=c[e].titleMaxWidth||g.width,c[e]._titleTextBlock.maxHeight=c[e].titleWrap?
0.8*g.height:1.5*c[e].titleFontSize,c[e]._titleTextBlock.angle=0);if(b&&0<b.length)for(e=0;e<b.length;e++)b[e]&&b[e].title&&(b[e]._titleTextBlock.maxWidth=b[e].titleMaxWidth||g.height,b[e]._titleTextBlock.maxHeight=b[e].titleWrap?0.8*g.width:1.5*b[e].titleFontSize,b[e]._titleTextBlock.angle=-90);if(d&&0<d.length)for(e=0;e<d.length;e++)d[e]&&d[e].title&&(d[e]._titleTextBlock.maxWidth=d[e].titleMaxWidth||g.height,d[e]._titleTextBlock.maxHeight=d[e].titleWrap?0.8*g.width:1.5*d[e].titleFontSize,d[e]._titleTextBlock.angle=
90);for(;4>q;){var N=0,Q=0,O=0,P=0,K=f=0,C=0,R=0,Y=0,U=0,T=0,S=0;if(b&&0<b.length)for(v=[],e=T=0;e<b.length;e++)v.push(Math.ceil(b[e]?b[e].createLabelsAndCalculateWidth():0)),T+=v[e],C+=b[e]?b[e].margin:0;else v.push(Math.ceil(b[0]?b[0].createLabelsAndCalculateWidth():0));L.push(v);if(d&&0<d.length)for(z=[],e=S=0;e<d.length;e++)z.push(Math.ceil(d[e]?d[e].createLabelsAndCalculateWidth():0)),S+=z[e],R+=d[e]?d[e].margin:0;else z.push(Math.ceil(d[0]?d[0].createLabelsAndCalculateWidth():0));J.push(z);
l=Math.round(g.x1+T+C);h=Math.round(g.x2-S-R>p.width-10?p.width-10:g.x2-S-R);if(a&&0<a.length)for(t=[],e=Y=0;e<a.length;e++)a[e]&&(a[e].lineCoordinates={}),a[e].lineCoordinates.width=Math.abs(h-l),a[e].title&&(a[e]._titleTextBlock.maxWidth=0<a[e].titleMaxWidth&&a[e].titleMaxWidth<a[e].lineCoordinates.width?a[e].titleMaxWidth:a[e].lineCoordinates.width),t.push(Math.ceil(a[e]?a[e].createLabelsAndCalculateHeight():0)),Y+=t[e],f+=a[e]?a[e].margin:0;else t.push(Math.ceil(a[0]?a[0].createLabelsAndCalculateHeight():
0));H.push(t);if(c&&0<c.length)for(u=[],e=U=0;e<c.length;e++)c[e]&&(c[e].lineCoordinates={}),c[e].lineCoordinates.width=Math.abs(h-l),c[e].title&&(c[e]._titleTextBlock.maxWidth=0<c[e].titleMaxWidth&&c[e].titleMaxWidth<c[e].lineCoordinates.width?c[e].titleMaxWidth:c[e].lineCoordinates.width),u.push(Math.ceil(c[e]?c[e].createLabelsAndCalculateHeight():0)),U+=u[e],K+=c[e]?c[e].margin:0;else u.push(Math.ceil(c[0]?c[0].createLabelsAndCalculateHeight():0));I.push(u);if(a&&0<a.length)for(e=0;e<a.length;e++)a[e]&&
(a[e].lineCoordinates.x1=l,h=Math.round(g.x2-S-R>p.width-10?p.width-10:g.x2-S-R),a[e]._labels&&1<a[e]._labels.length&&(k=m=0,m=a[e]._labels[1],k="dateTime"===a[e].chart.plotInfo.axisXValueType?a[e]._labels[a[e]._labels.length-2]:a[e]._labels[a[e]._labels.length-1],r=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),s=k.textBlock.width*Math.cos(Math.PI/180*Math.abs(k.textBlock.angle))+(k.textBlock.height-
k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(k.textBlock.angle))),a[e]&&(a[e].labelAutoFit&&!y(w)&&!y(A))&&(k=0,0<a[e].labelAngle?A+s>h&&(k+=0<a[e].labelAngle?A+s-h-S:0):0>a[e].labelAngle?w-r<l&&w-r<a[e].viewportMinimum&&(B=l-(C+a[e].tickLength+v+w-r+a[e].labelFontSize/2)):0===a[e].labelAngle&&(A+s>h&&(k=A+s/2-h-S),w-r<l&&w-r<a[e].viewportMinimum&&(B=l-C-a[e].tickLength-v-w+r/2)),a[e].viewportMaximum===a[e].maximum&&a[e].viewportMinimum===a[e].minimum&&0<a[e].labelAngle&&0<k?h-=k:a[e].viewportMaximum===
a[e].maximum&&a[e].viewportMinimum===a[e].minimum&&0>a[e].labelAngle&&0<B?l+=B:a[e].viewportMaximum===a[e].maximum&&a[e].viewportMinimum===a[e].minimum&&0===a[e].labelAngle&&(0<B&&(l+=B),0<k&&(h-=k))),p.panEnabled?Y=p.sessionVariables.axisX.height:p.sessionVariables.axisX.height=Y,k=Math.round(g.y2-Y-f+N),m=Math.round(g.y2),a[e].lineCoordinates.x2=h,a[e].lineCoordinates.width=h-l,a[e].lineCoordinates.y1=k,a[e].lineCoordinates.y2=k,a[e].bounds={x1:l,y1:k,x2:h,y2:m-(Y+f-t[e]-N),width:h-l,height:m-k}),
N+=t[e]+a[e].margin;if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].lineCoordinates.x1=Math.round(g.x1+T+C),c[e].lineCoordinates.x2=Math.round(g.x2-S-R>p.width-10?p.width-10:g.x2-S-R),c[e].lineCoordinates.width=Math.abs(h-l),c[e]._labels&&1<c[e]._labels.length&&(m=c[e]._labels[1],k="dateTime"===c[e].chart.plotInfo.axisXValueType?c[e]._labels[c[e]._labels.length-2]:c[e]._labels[c[e]._labels.length-1],r=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-k.textBlock.fontSize/
2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),s=k.textBlock.width*Math.cos(Math.PI/180*Math.abs(k.textBlock.angle))+(k.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(k.textBlock.angle))),p.panEnabled?U=p.sessionVariables.axisX2.height:p.sessionVariables.axisX2.height=U,k=Math.round(g.y1),m=Math.round(g.y2+c[e].margin),c[e].lineCoordinates.y1=k+U+K-Q,c[e].lineCoordinates.y2=k,c[e].bounds={x1:l,y1:k+(U+K-u[e]-Q),x2:h,y2:m,width:h-l,height:m-k},Q+=u[e]+c[e].margin;if(b&&
0<b.length)for(e=0;e<b.length;e++)C=10,b[e]&&(l=Math.round(a[0]?a[0].lineCoordinates.x1:c[0].lineCoordinates.x1),C=b[e]._labels&&0<b[e]._labels.length?b[e]._labels[b[e]._labels.length-1].textBlock.height/2:10,k=Math.round(g.y1+U+K<Math.max(C,10)?Math.max(C,10):g.y1+U+K),h=Math.round(a[0]?a[0].lineCoordinates.x1:c[0].lineCoordinates.x1),C=0<a.length?0:b[e]._labels&&0<b[e]._labels.length?b[e]._labels[0].textBlock.height/2:10,m=Math.round(g.y2-Y-f-C),b[e].lineCoordinates={x1:h-O,y1:k,x2:h-O,y2:m,height:Math.abs(m-
k)},b[e].bounds={x1:l-(v[e]+O),y1:k,x2:h,y2:m,width:h-l,height:m-k},b[e].title&&(b[e]._titleTextBlock.maxWidth=0<b[e].titleMaxWidth&&b[e].titleMaxWidth<b[e].lineCoordinates.height?b[e].titleMaxWidth:b[e].lineCoordinates.height),O+=v[e]+b[e].margin);if(d&&0<d.length)for(e=0;e<d.length;e++)d[e]&&(l=Math.round(a[0]?a[0].lineCoordinates.x2:c[0].lineCoordinates.x2),h=Math.round(l),C=d[e]._labels&&0<d[e]._labels.length?d[e]._labels[d[e]._labels.length-1].textBlock.height/2:0,k=Math.round(g.y1+U+K<Math.max(C,
10)?Math.max(C,10):g.y1+U+K),C=0<a.length?0:d[e]._labels&&0<d[e]._labels.length?d[e]._labels[0].textBlock.height/2:0,m=Math.round(g.y2-(Y+f+C)),d[e].lineCoordinates={x1:l+P,y1:k,x2:l+P,y2:m,height:Math.abs(m-k)},d[e].bounds={x1:l,y1:k,x2:h+(z[e]+P),y2:m,width:h-l,height:m-k},d[e].title&&(d[e]._titleTextBlock.maxWidth=0<d[e].titleMaxWidth&&d[e].titleMaxWidth<d[e].lineCoordinates.height?d[e].titleMaxWidth:d[e].lineCoordinates.height),P+=z[e]+d[e].margin);if(a&&0<a.length)for(e=0;e<a.length;e++)a[e]&&
(a[e].calculateValueToPixelConversionParameters(),a[e]._labels&&1<a[e]._labels.length&&(w=(a[e].logarithmic?Math.log(a[e]._labels[1].position/a[e].viewportMinimum)/a[e].conversionParameters.lnLogarithmBase:a[e]._labels[1].position-a[e].viewportMinimum)*Math.abs(a[e].conversionParameters.pixelPerUnit)+a[e].lineCoordinates.x1,A="dateTime"===a[e].chart.plotInfo.axisXValueType?(a[e].logarithmic?Math.log(a[e]._labels[a[e]._labels.length-2].position/a[e].viewportMinimum)/a[e].conversionParameters.lnLogarithmBase:
a[e]._labels[a[e]._labels.length-2].position-a[e].viewportMinimum)*Math.abs(a[e].conversionParameters.pixelPerUnit)+a[e].lineCoordinates.x1:(a[e].logarithmic?Math.log(a[e]._labels[a[e]._labels.length-1].position/a[e].viewportMinimum)/a[e].conversionParameters.lnLogarithmBase:a[e]._labels[a[e]._labels.length-1].position-a[e].viewportMinimum)*Math.abs(a[e].conversionParameters.pixelPerUnit)+a[e].lineCoordinates.x1));if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].calculateValueToPixelConversionParameters(),
c[e]._labels&&1<c[e]._labels.length&&(w=(c[e].logarithmic?Math.log(c[e]._labels[1].position/c[e].viewportMinimum)/c[e].conversionParameters.lnLogarithmBase:c[e]._labels[1].position-c[e].viewportMinimum)*Math.abs(c[e].conversionParameters.pixelPerUnit)+c[e].lineCoordinates.x1,A="dateTime"===c[e].chart.plotInfo.axisXValueType?(c[e].logarithmic?Math.log(c[e]._labels[c[e]._labels.length-2].position/c[e].viewportMinimum)/c[e].conversionParameters.lnLogarithmBase:c[e]._labels[c[e]._labels.length-2].position-
c[e].viewportMinimum)*Math.abs(c[e].conversionParameters.pixelPerUnit)+c[e].lineCoordinates.x1:(c[e].logarithmic?Math.log(c[e]._labels[c[e]._labels.length-1].position/c[e].viewportMinimum)/c[e].conversionParameters.lnLogarithmBase:c[e]._labels[c[e]._labels.length-1].position-c[e].viewportMinimum)*Math.abs(c[e].conversionParameters.pixelPerUnit)+c[e].lineCoordinates.x1);if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].calculateValueToPixelConversionParameters();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].calculateValueToPixelConversionParameters();
if(0<q){if(a&&0<a.length)for(e=0;e<a.length;e++)D=H[q-1][e]===H[q][e]?!0:!1;else D=!0;if(c&&0<c.length)for(e=0;e<c.length;e++)E=I[q-1][e]===I[q][e]?!0:!1;else E=!0;if(b&&0<b.length)for(e=0;e<b.length;e++)F=L[q-1][e]===L[q][e]?!0:!1;else F=!0;if(d&&0<d.length)for(e=0;e<d.length;e++)G=J[q-1][e]===J[q][e]?!0:!1;else G=!0}if(D&&E&&F&&G)break;q++}n.save();n.beginPath();a[0]&&n.rect(5,a[0].bounds.y1,a[0].chart.width-10,a[0].bounds.height);c[0]&&n.rect(5,c[c.length-1].bounds.y1,c[0].chart.width-10,c[0].bounds.height);
n.clip();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderLabelsTicksAndTitle();if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderLabelsTicksAndTitle();n.restore();if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderLabelsTicksAndTitle();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderLabelsTicksAndTitle()}else{w=[];A=[];B=[];r=[];s=[];H=[];I=[];L=[];if(a&&0<a.length)for(e=0;e<a.length;e++)a[e]&&a[e].title&&(a[e]._titleTextBlock.maxWidth=a[e].titleMaxWidth||g.width,a[e]._titleTextBlock.maxHeight=
a[e].titleWrap?0.8*g.height:1.5*a[e].titleFontSize,a[e]._titleTextBlock.angle=-90);if(c&&0<c.length)for(e=0;e<c.length;e++)c[e]&&c[e].title&&(c[e]._titleTextBlock.maxWidth=c[e].titleMaxWidth||g.width,c[e]._titleTextBlock.maxHeight=c[e].titleWrap?0.8*g.height:1.5*c[e].titleFontSize,c[e]._titleTextBlock.angle=90);if(b&&0<b.length)for(e=0;e<b.length;e++)b[e]&&b[e].title&&(b[e]._titleTextBlock.maxWidth=b[e].titleMaxWidth||g.width,b[e]._titleTextBlock.maxHeight=b[e].titleWrap?0.8*g.height:1.5*b[e].titleFontSize,
b[e]._titleTextBlock.angle=0);if(d&&0<d.length)for(e=0;e<d.length;e++)d[e]&&d[e].title&&(d[e]._titleTextBlock.maxWidth=d[e].titleMaxWidth||g.width,d[e]._titleTextBlock.maxHeight=d[e].titleWrap?0.8*g.height:1.5*d[e].titleFontSize,d[e]._titleTextBlock.angle=0);for(;4>q;){U=Y=T=P=R=C=K=f=O=J=Q=N=0;if(a&&0<a.length)for(B=[],e=Y=0;e<a.length;e++)B.push(Math.ceil(a[e]?a[e].createLabelsAndCalculateWidth():0)),Y+=B[e],f+=a[e]?a[e].margin:0;else B.push(Math.ceil(a[0]?a[0].createLabelsAndCalculateWidth():0));
I.push(B);if(c&&0<c.length)for(r=[],e=U=0;e<c.length;e++)r.push(Math.ceil(c[e]?c[e].createLabelsAndCalculateWidth():0)),U+=r[e],K+=c[e]?c[e].margin:0;else r.push(Math.ceil(c[0]?c[0].createLabelsAndCalculateWidth():0));L.push(r);if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].lineCoordinates={},l=Math.round(g.x1+Y+f),h=Math.round(g.x2-U-K>p.width-10?p.width-10:g.x2-U-K),b[e].labelAutoFit&&!y(t)&&(0<!a.length&&(l=0>b[e].labelAngle?Math.max(l,t):0===b[e].labelAngle?Math.max(l,t/2):l),0<!c.length&&(h=0<
b[e].labelAngle?h-u/2:0===b[e].labelAngle?h-u/2:h)),b[e].lineCoordinates.x1=l,b[e].lineCoordinates.x2=h,b[e].lineCoordinates.width=Math.abs(h-l),b[e].title&&(b[e]._titleTextBlock.maxWidth=0<b[e].titleMaxWidth&&b[e].titleMaxWidth<b[e].lineCoordinates.width?b[e].titleMaxWidth:b[e].lineCoordinates.width);if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].lineCoordinates={},l=Math.round(g.x1+Y+f),h=Math.round(g.x2-U-K>d[e].chart.width-10?d[e].chart.width-10:g.x2-U-K),d[e]&&d[e].labelAutoFit&&!y(v)&&(0<!a.length&&
(l=0<d[e].labelAngle?Math.max(l,v):0===d[e].labelAngle?Math.max(l,v/2):l),0<!c.length&&(h-=z/2)),d[e].lineCoordinates.x1=l,d[e].lineCoordinates.x2=h,d[e].lineCoordinates.width=Math.abs(h-l),d[e].title&&(d[e]._titleTextBlock.maxWidth=0<d[e].titleMaxWidth&&d[e].titleMaxWidth<d[e].lineCoordinates.width?d[e].titleMaxWidth:d[e].lineCoordinates.width);if(b&&0<b.length)for(w=[],e=P=0;e<b.length;e++)w.push(Math.ceil(b[e]?b[e].createLabelsAndCalculateHeight():0)),P+=w[e]+b[e].margin,C+=b[e].margin;else w.push(Math.ceil(b[0]?
b[0].createLabelsAndCalculateHeight():0));s.push(w);if(d&&0<d.length)for(A=[],e=T=0;e<d.length;e++)A.push(Math.ceil(d[e]?d[e].createLabelsAndCalculateHeight():0)),T+=A[e],R+=d[e].margin;else A.push(Math.ceil(d[0]?d[0].createLabelsAndCalculateHeight():0));H.push(A);if(b&&0<b.length)for(e=0;e<b.length;e++)0<b[e]._labels.length&&(m=b[e]._labels[0],k=b[e]._labels[b[e]._labels.length-1],t=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/
180*Math.abs(m.textBlock.angle)),u=k.textBlock.width*Math.cos(Math.PI/180*Math.abs(k.textBlock.angle))+(k.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(k.textBlock.angle)));if(d&&0<d.length)for(e=0;e<d.length;e++)d[e]&&0<d[e]._labels.length&&(m=d[e]._labels[0],k=d[e]._labels[d[e]._labels.length-1],v=m.textBlock.width*Math.cos(Math.PI/180*Math.abs(m.textBlock.angle))+(m.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(m.textBlock.angle)),z=k.textBlock.width*
Math.cos(Math.PI/180*Math.abs(k.textBlock.angle))+(k.textBlock.height-k.textBlock.fontSize/2)*Math.sin(Math.PI/180*Math.abs(k.textBlock.angle)));if(p.panEnabled)for(e=0;e<b.length;e++)w[e]=p.sessionVariables.axisY.height;else for(e=0;e<b.length;e++)p.sessionVariables.axisY.height=w[e];if(b&&0<b.length)for(e=b.length-1;0<=e;e--)k=Math.round(g.y2),m=Math.round(g.y2>b[e].chart.height-10?b[e].chart.height-10:g.y2),b[e].lineCoordinates.y1=k-(w[e]+b[e].margin+N),b[e].lineCoordinates.y2=k-(w[e]+b[e].margin+
N),b[e].bounds={x1:l,y1:k-(w[e]+N+b[e].margin),x2:h,y2:m-(N+b[e].margin),width:h-l,height:w[e]},b[e].title&&(b[e]._titleTextBlock.maxWidth=0<b[e].titleMaxWidth&&b[e].titleMaxWidth<b[e].lineCoordinates.width?b[e].titleMaxWidth:b[e].lineCoordinates.width),N+=w[e]+b[e].margin;if(d&&0<d.length)for(e=d.length-1;0<=e;e--)d[e]&&(k=Math.round(g.y1),m=Math.round(g.y1+(A[e]+d[e].margin+Q)),d[e].lineCoordinates.y1=m,d[e].lineCoordinates.y2=m,d[e].bounds={x1:l,y1:k+(d[e].margin+Q),x2:h,y2:m,width:h-l,height:T},
d[e].title&&(d[e]._titleTextBlock.maxWidth=0<d[e].titleMaxWidth&&d[e].titleMaxWidth<d[e].lineCoordinates.width?d[e].titleMaxWidth:d[e].lineCoordinates.width),Q+=A[e]+d[e].margin);if(a&&0<a.length)for(e=0;e<a.length;e++){C=a[e]._labels&&0<a[e]._labels.length?a[e]._labels[0].textBlock.fontSize/2:0;l=Math.round(g.x1+f);k=d&&0<d.length?Math.round(d[0]?d[0].lineCoordinates.y2:g.y1<Math.max(C,10)?Math.max(C,10):g.y1):g.y1<Math.max(C,10)?Math.max(C,10):g.y1;h=Math.round(g.x1+Y+f);m=b&&0<b.length?Math.round(b[0]?
b[0].lineCoordinates.y1:g.y2-P>p.height-Math.max(C,10)?p.height-Math.max(C,10):g.y2-P):g.y2>p.height-Math.max(C,10)?p.height-Math.max(C,10):g.y2;if(b&&0<b.length)for(C=0;C<b.length;C++)b[C]&&b[C].labelAutoFit&&(h=0>b[C].labelAngle?Math.max(h,t):0===b[C].labelAngle?Math.max(h,t/2):h,l=0>b[C].labelAngle||0===b[C].labelAngle?h-Y:l);if(d&&0<d.length)for(C=0;C<d.length;C++)d[C]&&d[C].labelAutoFit&&(h=d[C].lineCoordinates.x1,l=h-Y);a[e].lineCoordinates={x1:h-J,y1:k,x2:h-J,y2:m,height:Math.abs(m-k)};a[e].bounds=
{x1:h-(B[e]+J),y1:k,x2:h,y2:m,width:h-l,height:m-k};a[e].title&&(a[e]._titleTextBlock.maxWidth=0<a[e].titleMaxWidth&&a[e].titleMaxWidth<a[e].lineCoordinates.height?a[e].titleMaxWidth:a[e].lineCoordinates.height);a[e].calculateValueToPixelConversionParameters();J+=B[e]+a[e].margin}if(c&&0<c.length)for(e=0;e<c.length;e++){C=c[e]._labels&&0<c[e]._labels.length?c[e]._labels[0].textBlock.fontSize/2:0;l=Math.round(g.x1-f);k=d&&0<d.length?Math.round(d[0]?d[0].lineCoordinates.y2:g.y1<Math.max(C,10)?Math.max(C,
10):g.y1):g.y1<Math.max(C,10)?Math.max(C,10):g.y1;h=Math.round(g.x2-U-K);m=b&&0<b.length?Math.round(b[0]?b[0].lineCoordinates.y1:g.y2-P>p.height-Math.max(C,10)?p.height-Math.max(C,10):g.y2-P):g.y2>p.height-Math.max(C,10)?p.height-Math.max(C,10):g.y2;if(b&&0<b.length)for(C=0;C<b.length;C++)b[C]&&b[C].labelAutoFit&&(h=0>b[C].labelAngle?Math.max(h,t):0===b[C].labelAngle?Math.max(h,t/2):h,l=0>b[C].labelAngle||0===b[C].labelAngle?h-U:l);if(d&&0<d.length)for(C=0;C<d.length;C++)d[C]&&d[C].labelAutoFit&&
(h=d[C].lineCoordinates.x2,l=h-U);c[e].lineCoordinates={x1:h+O,y1:k,x2:h+O,y2:m,height:Math.abs(m-k)};c[e].bounds={x1:l,y1:k,x2:h+r[e]+O,y2:m,width:h-l,height:m-k};c[e].title&&(c[e]._titleTextBlock.maxWidth=0<c[e].titleMaxWidth&&c[e].titleMaxWidth<c[e].lineCoordinates.height?c[e].titleMaxWidth:c[e].lineCoordinates.height);c[e].calculateValueToPixelConversionParameters();O+=r[e]+c[e].margin}if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].calculateValueToPixelConversionParameters();if(d&&0<d.length)for(e=
0;e<d.length;e++)d[e].calculateValueToPixelConversionParameters();if(0<q){if(a&&0<a.length)for(e=0;e<a.length;e++)D=I[q-1][e]===I[q][e]?!0:!1;else D=!0;if(c&&0<c.length)for(e=0;e<c.length;e++)E=L[q-1][e]===L[q][e]?!0:!1;else E=!0;if(b&&0<b.length)for(e=0;e<b.length;e++)F=s[q-1][e]===s[q][e]?!0:!1;else F=!0;if(d&&0<d.length)for(e=0;e<d.length;e++)G=H[q-1][e]===H[q][e]?!0:!1;else G=!0}if(D&&E&&F&&G)break;q++}if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderLabelsTicksAndTitle();if(d&&0<d.length)for(e=
0;e<d.length;e++)d[e].renderLabelsTicksAndTitle();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderLabelsTicksAndTitle();if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderLabelsTicksAndTitle()}p.preparePlotArea();g=p.plotArea;n.save();n.beginPath();n.rect(g.x1,g.y1,Math.abs(g.x2-g.x1),Math.abs(g.y2-g.y1));n.clip();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderStripLinesOfThicknessType("value");if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderStripLinesOfThicknessType("value");if(b&&0<b.length)for(e=
0;e<b.length;e++)b[e].renderStripLinesOfThicknessType("value");if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderStripLinesOfThicknessType("value");if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderInterlacedColors();if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderInterlacedColors();if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderInterlacedColors();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderInterlacedColors();n.restore();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderGrid();if(c&&
0<c.length)for(e=0;e<c.length;e++)c[e].renderGrid();if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderGrid();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderGrid();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderAxisLine();if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderAxisLine();if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderAxisLine();if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderAxisLine();if(a&&0<a.length)for(e=0;e<a.length;e++)a[e].renderStripLinesOfThicknessType("pixel");
if(c&&0<c.length)for(e=0;e<c.length;e++)c[e].renderStripLinesOfThicknessType("pixel");if(b&&0<b.length)for(e=0;e<b.length;e++)b[e].renderStripLinesOfThicknessType("pixel");if(d&&0<d.length)for(e=0;e<d.length;e++)d[e].renderStripLinesOfThicknessType("pixel")};B.prototype.renderLabelsTicksAndTitle=function(){var a=!1,c=0,b=0,d=1,f=0;0!==this.labelAngle&&360!==this.labelAngle&&(d=1.2);if("undefined"===typeof this.options.interval){if("bottom"===this._position||"top"===this._position)if(this.logarithmic&&
!this.equidistantInterval&&this.labelAutoFit){for(var c=[],d=0!==this.labelAngle&&360!==this.labelAngle?1:1.2,g,l=this.viewportMaximum,k=this.lineCoordinates.width/Math.log(this.range),h=this._labels.length-1;0<=h;h--){p=this._labels[h];if(p.position<this.viewportMinimum)break;p.position>this.viewportMaximum||!(h===this._labels.length-1||g<Math.log(l/p.position)*k/d)||(c.push(p),l=p.position,g=p.textBlock.width*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+p.textBlock.height*Math.abs(Math.sin(Math.PI/
180*this.labelAngle)))}this._labels=c}else{for(h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||(p=p.textBlock.width*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+p.textBlock.height*Math.abs(Math.sin(Math.PI/180*this.labelAngle)),c+=p);c>this.lineCoordinates.width*d&&this.labelAutoFit&&(a=!0)}if("left"===this._position||"right"===this._position)if(this.logarithmic&&!this.equidistantInterval&&this.labelAutoFit){for(var c=[],m,l=this.viewportMaximum,k=this.lineCoordinates.height/
Math.log(this.range),h=this._labels.length-1;0<=h;h--){p=this._labels[h];if(p.position<this.viewportMinimum)break;p.position>this.viewportMaximum||!(h===this._labels.length-1||m<Math.log(l/p.position)*k)||(c.push(p),l=p.position,m=p.textBlock.height*Math.abs(Math.cos(Math.PI/180*this.labelAngle))+p.textBlock.width*Math.abs(Math.sin(Math.PI/180*this.labelAngle)))}this._labels=c}else{for(h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||(p=p.textBlock.height*Math.abs(Math.cos(Math.PI/
180*this.labelAngle))+p.textBlock.width*Math.abs(Math.sin(Math.PI/180*this.labelAngle)),b+=p);b>this.lineCoordinates.height*d&&this.labelAutoFit&&(a=!0)}}if("bottom"===this._position){for(var p,h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||p.position>this.viewportMaximum||(b=this.getPixelCoordinatesOnAxis(p.position),a&&0!==f++%2&&this.labelAutoFit||(this.tickThickness&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,d=1===this.ctx.lineWidth%
2?(b.x<<0)+0.5:b.x<<0,this.ctx.beginPath(),this.ctx.moveTo(d,b.y<<0),this.ctx.lineTo(d,b.y+this.tickLength<<0),this.ctx.stroke()),0===p.textBlock.angle?(b.x-=p.textBlock.width/2,b.y+=this.tickLength+p.textBlock.fontSize/2):(b.x-=0>this.labelAngle?p.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0,b.y+=this.tickLength+Math.abs(0>this.labelAngle?p.textBlock.width*Math.sin(Math.PI/180*this.labelAngle)-5:5)),p.textBlock.x=b.x,p.textBlock.y=b.y,p.textBlock.render(!0)));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(h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||p.position>this.viewportMaximum||(b=this.getPixelCoordinatesOnAxis(p.position),a&&0!==f++%2&&this.labelAutoFit||(this.tickThickness&&
(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,d=1===this.ctx.lineWidth%2?(b.x<<0)+0.5:b.x<<0,this.ctx.beginPath(),this.ctx.moveTo(d,b.y<<0),this.ctx.lineTo(d,b.y-this.tickLength<<0),this.ctx.stroke()),0===p.textBlock.angle?(b.x-=p.textBlock.width/2,b.y-=this.tickLength+p.textBlock.height/2):(b.x+=(p.textBlock.height-this.tickLength-this.labelFontSize/2)*Math.sin(Math.PI/180*this.labelAngle)-(0<this.labelAngle?p.textBlock.width*Math.cos(Math.PI/180*this.labelAngle):0),
b.y-=this.tickLength+(p.textBlock.height/2*Math.cos(Math.PI/180*this.labelAngle)+(0<this.labelAngle?p.textBlock.width*Math.sin(Math.PI/180*this.labelAngle):0))),p.textBlock.x=b.x,p.textBlock.y=b.y,p.textBlock.render(!0)));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(h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||p.position>this.viewportMaximum||(b=this.getPixelCoordinatesOnAxis(p.position),a&&0!==f++%2&&this.labelAutoFit||(this.tickThickness&&(this.ctx.lineWidth=this.tickThickness,this.ctx.strokeStyle=this.tickColor,d=1===this.ctx.lineWidth%2?(b.y<<0)+0.5:b.y<<0,this.ctx.beginPath(),this.ctx.moveTo(b.x<<0,d),this.ctx.lineTo(b.x-this.tickLength<<0,d),this.ctx.stroke()),0===this.labelAngle?(p.textBlock.y=b.y,
p.textBlock.x=b.x-p.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5):(p.textBlock.y=b.y-p.textBlock.width*Math.sin(Math.PI/180*this.labelAngle),p.textBlock.x=0<this.labelAngle?b.x-p.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)-this.tickLength-5:b.x-p.textBlock.width*Math.cos(Math.PI/180*this.labelAngle)+(p.textBlock.height-p.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)-this.tickLength),p.textBlock.render(!0)));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(h=0;h<this._labels.length;h++)p=this._labels[h],p.position<this.viewportMinimum||p.position>this.viewportMaximum||(b=this.getPixelCoordinatesOnAxis(p.position),a&&0!==f++%2&&this.labelAutoFit||(this.tickThickness&&(this.ctx.lineWidth=this.tickThickness,
this.ctx.strokeStyle=this.tickColor,d=1===this.ctx.lineWidth%2?(b.y<<0)+0.5:b.y<<0,this.ctx.beginPath(),this.ctx.moveTo(b.x<<0,d),this.ctx.lineTo(b.x+this.tickLength<<0,d),this.ctx.stroke()),0===this.labelAngle?(p.textBlock.y=b.y,p.textBlock.x=b.x+this.tickLength+5):(p.textBlock.y=0>this.labelAngle?b.y:b.y-(p.textBlock.height-p.textBlock.fontSize/2-5)*Math.cos(Math.PI/180*this.labelAngle),p.textBlock.x=0<this.labelAngle?b.x+(p.textBlock.height-p.textBlock.fontSize/2-5)*Math.sin(Math.PI/180*this.labelAngle)+
this.tickLength:b.x+this.tickLength+5),p.textBlock.render(!0)));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))}};B.prototype.renderInterlacedColors=function(){var a=this.chart.plotArea.ctx,c,b,d=this.chart.plotArea,f=0;c=!0;if(("bottom"===this._position||"top"===this._position)&&
this.interlacedColor)for(a.fillStyle=this.interlacedColor,f=0;f<this._labels.length;f++)c?(c=this.getPixelCoordinatesOnAxis(this._labels[f].position),b=f+1>this._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[f+1].position),a.fillRect(Math.min(b.x,c.x),d.y1,Math.abs(b.x-c.x),Math.abs(d.y1-d.y2)),c=!1):c=!0;else if(("left"===this._position||"right"===this._position)&&this.interlacedColor)for(a.fillStyle=this.interlacedColor,f=0;f<this._labels.length;f++)c?
(b=this.getPixelCoordinatesOnAxis(this._labels[f].position),c=f+1>this._labels.length-1?this.getPixelCoordinatesOnAxis(this.viewportMaximum):this.getPixelCoordinatesOnAxis(this._labels[f+1].position),a.fillRect(d.x1,Math.min(b.y,c.y),Math.abs(d.x1-d.x2),Math.abs(c.y-b.y)),c=!1):c=!0;a.beginPath()};B.prototype.renderStripLinesOfThicknessType=function(a){if(this.stripLines&&0<this.stripLines.length&&a){for(var c=this,b,d=0,f=0,g=!1,l=!1,k=[],h=[],l=!1,d=0;d<this.stripLines.length;d++){var m=this.stripLines[d];
m._thicknessType===a&&("pixel"===a&&(m.value<this.viewportMinimum||m.value>this.viewportMaximum||y(m.value)||isNaN(this.range))||k.push(m))}for(d=0;d<this._stripLineLabels.length;d++)if(m=this.stripLines[d],b=this._stripLineLabels[d],!(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[d].labelMaxWidth?this.options.stripLines[d].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?y(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,y(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[d].labelMaxWidth?this.options.stripLines[d].labelMaxWidth:this.chart.plotArea.height-3,b.textBlock.measureText(),a.x-b.textBlock.height>this.chart.plotArea.x1?y(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,y(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[d].labelMaxWidth?this.options.stripLines[d].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-b.textBlock.height>this.chart.plotArea.y1?y(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:y(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[d].labelMaxWidth?this.options.stripLines[d].labelMaxWidth:this.chart.plotArea.width-3,b.textBlock.angle=0,b.textBlock.measureText(),a.y-+b.textBlock.height>this.chart.plotArea.y1?y(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:y(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(!l){l=!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(d=0;d<k.length;d++)m=k[d],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(f=
0;f<k.length;f++)m=k[f],m.showOnTop&&m.render();this.ctx.restore()},m)):m.render();for(d=0;d<h.length;d++)b=h[d],b.stripLine.showOnTop?l||(l=!0,this.chart.addEventListener("dataAnimationIterationEnd",function(){for(f=0;f<h.length;f++)b=h[f],"inside"===b.stripLine.labelPlacement&&b.stripLine.showOnTop&&(c.ctx.save(),c.ctx.beginPath(),c.ctx.rect(c.chart.plotArea.x1,c.chart.plotArea.y1,c.chart.plotArea.width,c.chart.plotArea.height),c.ctx.clip(),b.textBlock.render(!0),c.ctx.restore())},b.textBlock)):
"inside"===b.stripLine.labelPlacement&&b.textBlock.render(!0);this.ctx.restore();l=!0}if(l)for(l=!1,d=0;d<h.length;d++)b=h[d],b.stripLine.showOnTop?l||(l=!0,this.chart.addEventListener("dataAnimationIterationEnd",function(){for(f=0;f<h.length;f++)b=h[f],"outside"===b.stripLine.labelPlacement&&b.stripLine.showOnTop&&b.textBlock.render(!0)},b.textBlock)):"outside"===b.stripLine.labelPlacement&&b.textBlock.render(!0)}};B.prototype.renderGrid=function(){if(this.gridThickness&&0<this.gridThickness){var a=
this.chart.ctx;a.save();var c,b=this.chart.plotArea;a.lineWidth=this.gridThickness;a.strokeStyle=this.gridColor;a.setLineDash&&a.setLineDash(F(this.gridDashType,this.gridThickness));if("bottom"===this._position||"top"===this._position)for(d=0;d<this._labels.length;d++)this._labels[d].position<this.viewportMinimum||this._labels[d].position>this.viewportMaximum||(a.beginPath(),c=this.getPixelCoordinatesOnAxis(this._labels[d].position),c=1===a.lineWidth%2?(c.x<<0)+0.5:c.x<<0,a.moveTo(c,b.y1<<0),a.lineTo(c,
b.y2<<0),a.stroke());else if("left"===this._position||"right"===this._position)for(var d=0;d<this._labels.length;d++)this._labels[d].position<this.viewportMinimum||this._labels[d].position>this.viewportMaximum||(a.beginPath(),c=this.getPixelCoordinatesOnAxis(this._labels[d].position),c=1===a.lineWidth%2?(c.y<<0)+0.5:c.y<<0,a.moveTo(b.x1<<0,c),a.lineTo(b.x2<<0,c),a.stroke());a.restore()}};B.prototype.renderAxisLine=function(){var a=this.chart.ctx;a.save();if("bottom"===this._position||"top"===this._position){if(this.lineThickness){a.lineWidth=
this.lineThickness;a.strokeStyle=this.lineColor?this.lineColor:"black";a.setLineDash&&a.setLineDash(F(this.lineDashType,this.lineThickness));var c=1===this.lineThickness%2?(this.lineCoordinates.y1<<0)+0.5:this.lineCoordinates.y1<<0;a.beginPath();a.moveTo(this.lineCoordinates.x1,c);a.lineTo(this.lineCoordinates.x2,c);a.stroke()}}else"left"!==this._position&&"right"!==this._position||!this.lineThickness||(a.lineWidth=this.lineThickness,a.strokeStyle=this.lineColor,a.setLineDash&&a.setLineDash(F(this.lineDashType,
this.lineThickness)),c=1===this.lineThickness%2?(this.lineCoordinates.x1<<0)+0.5:this.lineCoordinates.x1<<0,a.beginPath(),a.moveTo(c,this.lineCoordinates.y1),a.lineTo(c,this.lineCoordinates.y2),a.stroke());a.restore()};B.prototype.getPixelCoordinatesOnAxis=function(a){var c={};if("bottom"===this._position||"top"===this._position)c.x=this.convertValueToPixel(a),c.y=this.lineCoordinates.y1;if("left"===this._position||"right"===this._position)c.y=this.convertValueToPixel(a),c.x=this.lineCoordinates.x2;
return c};B.prototype.convertPixelToValue=function(a){if("undefined"===typeof a)return null;var c=0,c=0,c="number"===typeof a?a:"left"===this._position||"right"===this._position?a.y:a.x;return c=this.logarithmic?Math.pow(this.logarithmBase,(c-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit)*this.viewportMinimum:this.conversionParameters.minimum+(c-this.conversionParameters.reference)/this.conversionParameters.pixelPerUnit};B.prototype.convertValueToPixel=function(a){return this.logarithmic?
this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*Math.log(a/this.conversionParameters.minimum)/this.conversionParameters.lnLogarithmBase+0.5<<0:this.conversionParameters.reference+this.conversionParameters.pixelPerUnit*(a-this.conversionParameters.minimum)+0.5<<0};B.prototype.setViewPortRange=function(a,c){this.sessionVariables.newViewportMinimum=this.viewportMinimum=Math.min(a,c);this.sessionVariables.newViewportMaximum=this.viewportMaximum=Math.max(a,c)};B.prototype.getXValueAt=
function(a){if(!a)return null;var c=null;"left"===this._position?c=this.convertPixelToValue(a.y):"bottom"===this._position&&(c=this.convertPixelToValue(a.x));return c};B.prototype.calculateValueToPixelConversionParameters=function(a){a={pixelPerUnit:null,minimum:null,reference:null};var c=this.lineCoordinates.width,b=this.lineCoordinates.height;a.minimum=this.viewportMinimum;if("bottom"===this._position||"top"===this._position)this.logarithmic?(a.lnLogarithmBase=Math.log(this.logarithmBase),a.pixelPerUnit=
(this.reversed?-1:1)*c*a.lnLogarithmBase/Math.log(Math.abs(this.range))):a.pixelPerUnit=(this.reversed?-1:1)*c/Math.abs(this.range),a.reference=this.reversed?this.lineCoordinates.x2:this.lineCoordinates.x1;if("left"===this._position||"right"===this._position)this.logarithmic?(a.lnLogarithmBase=Math.log(this.logarithmBase),a.pixelPerUnit=(this.reversed?1:-1)*b*a.lnLogarithmBase/Math.log(Math.abs(this.range))):a.pixelPerUnit=(this.reversed?1:-1)*b/Math.abs(this.range),a.reference=this.reversed?this.lineCoordinates.y1:
this.lineCoordinates.y2;this.conversionParameters=a};B.prototype.calculateAxisParameters=function(){if(this.logarithmic)this.calculateLogarithamicAxisParameters();else{var a=this.chart.layoutManager.getFreeSpace(),c=!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,d=4;"axisX"===this.type&&(d=600>this.maxWidth?8:6);var a=Math.max(d,Math.floor(this.maxWidth/a)),f,g,l,d=0;!y(this.options.viewportMinimum)&&(!y(this.options.viewportMaximum)&&this.options.viewportMinimum>=this.options.viewportMaximum)&&(this.viewportMinimum=this.viewportMaximum=null);if(y(this.options.viewportMinimum)&&!y(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(y(this.options.viewportMaximum)&&!y(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("axisX"===this.type){if(this.dataSeries&&0<this.dataSeries.length)for(f=0;f<this.dataSeries.length;f++)"dateTime"===this.dataSeries[f].xValueType&&
(b=!0);f=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin;g=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax;0===g-f&&(d="undefined"===typeof this.options.interval?0.4:this.options.interval,g+=d,f-=d);Infinity!==this.dataInfo.minDiff?l=this.dataInfo.minDiff:1<g-f?l=0.5*Math.abs(g-f):(l=1,b&&(c=!0))}else"axisY"===this.type&&(f=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,g=null!==this.viewportMaximum?this.viewportMaximum:
this.dataInfo.viewPortMax,isFinite(f)||isFinite(g)?isFinite(f)?isFinite(g)||(g=f):f=g:(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,f="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),0===f&&0===g?(g+=9,f=0):0===g-f?(d=Math.min(Math.abs(0.01*Math.abs(g)),5),g+=d,f-=d):f>g?(d=Math.min(Math.abs(0.01*Math.abs(g-f)),5),0<=g?f=g-d:g=isFinite(f)?f+d:0):(d=Math.min(Math.abs(0.01*Math.abs(g-f)),0.05),0!==g&&(g+=d),0!==f&&(f-=d)),l=Infinity!==
this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-f?0.5*Math.abs(g-f):1,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&0<f&&(f=0),this.includeZero&&(null===this.viewportMaximum||isNaN(this.viewportMaximum))&&0>g&&(g=0));d=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?g:this.viewportMaximum)-(isNaN(this.viewportMinimum)||null===this.viewportMinimum?f:this.viewportMinimum);if("axisX"===this.type&&b){this.intervalType||(d/1<=a?(this.interval=1,this.intervalType=
"millisecond"):d/2<=a?(this.interval=2,this.intervalType="millisecond"):d/5<=a?(this.interval=5,this.intervalType="millisecond"):d/10<=a?(this.interval=10,this.intervalType="millisecond"):d/20<=a?(this.interval=20,this.intervalType="millisecond"):d/50<=a?(this.interval=50,this.intervalType="millisecond"):d/100<=a?(this.interval=100,this.intervalType="millisecond"):d/200<=a?(this.interval=200,this.intervalType="millisecond"):d/250<=a?(this.interval=250,this.intervalType="millisecond"):d/300<=a?(this.interval=
300,this.intervalType="millisecond"):d/400<=a?(this.interval=400,this.intervalType="millisecond"):d/500<=a?(this.interval=500,this.intervalType="millisecond"):d/(1*G.secondDuration)<=a?(this.interval=1,this.intervalType="second"):d/(2*G.secondDuration)<=a?(this.interval=2,this.intervalType="second"):d/(5*G.secondDuration)<=a?(this.interval=5,this.intervalType="second"):d/(10*G.secondDuration)<=a?(this.interval=10,this.intervalType="second"):d/(15*G.secondDuration)<=a?(this.interval=15,this.intervalType=
"second"):d/(20*G.secondDuration)<=a?(this.interval=20,this.intervalType="second"):d/(30*G.secondDuration)<=a?(this.interval=30,this.intervalType="second"):d/(1*G.minuteDuration)<=a?(this.interval=1,this.intervalType="minute"):d/(2*G.minuteDuration)<=a?(this.interval=2,this.intervalType="minute"):d/(5*G.minuteDuration)<=a?(this.interval=5,this.intervalType="minute"):d/(10*G.minuteDuration)<=a?(this.interval=10,this.intervalType="minute"):d/(15*G.minuteDuration)<=a?(this.interval=15,this.intervalType=
"minute"):d/(20*G.minuteDuration)<=a?(this.interval=20,this.intervalType="minute"):d/(30*G.minuteDuration)<=a?(this.interval=30,this.intervalType="minute"):d/(1*G.hourDuration)<=a?(this.interval=1,this.intervalType="hour"):d/(2*G.hourDuration)<=a?(this.interval=2,this.intervalType="hour"):d/(3*G.hourDuration)<=a?(this.interval=3,this.intervalType="hour"):d/(6*G.hourDuration)<=a?(this.interval=6,this.intervalType="hour"):d/(1*G.dayDuration)<=a?(this.interval=1,this.intervalType="day"):d/(2*G.dayDuration)<=
a?(this.interval=2,this.intervalType="day"):d/(4*G.dayDuration)<=a?(this.interval=4,this.intervalType="day"):d/(1*G.weekDuration)<=a?(this.interval=1,this.intervalType="week"):d/(2*G.weekDuration)<=a?(this.interval=2,this.intervalType="week"):d/(3*G.weekDuration)<=a?(this.interval=3,this.intervalType="week"):d/(1*G.monthDuration)<=a?(this.interval=1,this.intervalType="month"):d/(2*G.monthDuration)<=a?(this.interval=2,this.intervalType="month"):d/(3*G.monthDuration)<=a?(this.interval=3,this.intervalType=
"month"):d/(6*G.monthDuration)<=a?(this.interval=6,this.intervalType="month"):(this.interval=d/(1*G.yearDuration)<=a?1:d/(2*G.yearDuration)<=a?2:d/(4*G.yearDuration)<=a?4:Math.floor(B.getNiceNumber(d/(a-1),!0)/G.yearDuration),this.intervalType="year"));if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=f-l/2;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=g+l/2;c?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";d=B.getNiceNumber(d,!1);this.interval=this.options&&0<this.options.interval?this.options.interval:B.getNiceNumber(d/(a-1),!0);if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum="axisX"===this.type?f-l/2:Math.floor(f/this.interval)*this.interval;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum="axisX"===this.type?g+l/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=B.getNiceNumber((this.viewportMaximum-this.viewportMinimum)/(a-1),!0)))}if(null===this.minimum||null===this.maximum)if("axisX"===this.type?(f=null!==this.minimum?this.minimum:this.dataInfo.min,g=null!==this.maximum?this.maximum:
this.dataInfo.max,0===g-f&&(d="undefined"===typeof this.options.interval?0.4:this.options.interval,g+=d,f-=d),l=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-f?0.5*Math.abs(g-f):1):"axisY"===this.type&&(f=null!==this.minimum?this.minimum:this.dataInfo.min,g=null!==this.maximum?this.maximum:this.dataInfo.max,isFinite(f)||isFinite(g)?0===f&&0===g?(g+=9,f=0):0===g-f?(d=Math.min(Math.abs(0.01*Math.abs(g)),5),g+=d,f-=d):f>g?(d=Math.min(Math.abs(0.01*Math.abs(g-f)),5),0<=g?f=g-d:g=isFinite(f)?
f+d:0):(d=Math.min(Math.abs(0.01*Math.abs(g-f)),0.05),0!==g&&(g+=d),0!==f&&(f-=d)):(g="undefined"===typeof this.options.interval?-Infinity:this.options.interval,f="undefined"!==typeof this.options.interval||isFinite(this.dataInfo.minDiff)?0:Infinity),l=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:1<g-f?0.5*Math.abs(g-f):1,this.includeZero&&(null===this.minimum||isNaN(this.minimum))&&0<f&&(f=0),this.includeZero&&(null===this.maximum||isNaN(this.maximum))&&0>g&&(g=0)),"axisX"===this.type&&
b){if(null===this.minimum||isNaN(this.minimum))this.minimum=f-l/2;if(null===this.maximum||isNaN(this.maximum))this.maximum=g+l/2}else this.intervalType="number",null===this.minimum&&(this.minimum="axisX"===this.type?f-l/2:Math.floor(f/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+l/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));y(this.sessionVariables.newViewportMinimum)&&(this.viewportMinimum=Math.max(this.viewportMinimum,this.minimum));y(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;if(!this.valueFormatString&&(this.valueFormatString="#,##0.##",1>this.range)){c=Math.floor(Math.abs(Math.log(this.range)/Math.LN10))+2;if(isNaN(c)||!isFinite(c))c=2;if(2<c)for(b=0;b<c-
2;b++)this.valueFormatString+="#"}}};B.prototype.calculateLogarithamicAxisParameters=function(){var a=this.chart.layoutManager.getFreeSpace(),c=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),d,f,g,l;l=1;if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum=
this.minimum;if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum=this.maximum;"axisX"===this.type?(d=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,f=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,1===f/d&&(l=Math.pow(this.logarithmBase,"undefined"===typeof this.options.interval?0.4:this.options.interval),f*=l,d/=l),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:f/d>this.logarithmBase?f/d*Math.pow(this.logarithmBase,
0.5):this.logarithmBase):"axisY"===this.type&&(d=null!==this.viewportMinimum?this.viewportMinimum:this.dataInfo.viewPortMin,f=null!==this.viewportMaximum?this.viewportMaximum:this.dataInfo.viewPortMax,0>=d&&!isFinite(f)?(f="undefined"===typeof this.options.interval?0:this.options.interval,d=1):0>=d?d=f:isFinite(f)||(f=d),1===d&&1===f?(f*=this.logarithmBase-1/this.logarithmBase,d=1):1===f/d?(l=Math.min(f*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),f*=l,d/=l):d>f?(l=Math.min(d/
f*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,5)),1<=f?d=f/l:f=d*l):(l=Math.min(f/d*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,0.04)),1!==f&&(f*=l),1!==d&&(d/=l)),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:f/d>this.logarithmBase?f/d*Math.pow(this.logarithmBase,0.5):this.logarithmBase,this.includeZero&&(null===this.viewportMinimum||isNaN(this.viewportMinimum))&&1<d&&(d=1),this.includeZero&&(null===this.viewportMaximum||isNaN(this.viewportMaximum))&&
1>f&&(f=1));l=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?f:this.viewportMaximum)/(isNaN(this.viewportMinimum)||null===this.viewportMinimum?d:this.viewportMinimum);linearRange=(isNaN(this.viewportMaximum)||null===this.viewportMaximum?f:this.viewportMaximum)-(isNaN(this.viewportMinimum)||null===this.viewportMinimum?d:this.viewportMinimum);this.intervalType="number";l=Math.pow(this.logarithmBase,B.getNiceNumber(Math.abs(Math.log(l)/c),!1));this.options&&0<this.options.interval?this.interval=
this.options.interval:(this.interval=B.getNiceExponent(Math.log(l)/c/(a-1),!0),b=B.getNiceNumber(linearRange/(a-1),!0));if(null===this.viewportMinimum||isNaN(this.viewportMinimum))this.viewportMinimum="axisX"===this.type?d/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(d)/c/this.interval));if(null===this.viewportMaximum||isNaN(this.viewportMaximum))this.viewportMaximum="axisX"===this.type?f*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(f)/c/
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=B.getNiceExponent(Math.ceil(Math.log(l)/c)/(a-1)),b=B.getNiceNumber((this.viewportMaximum-this.viewportMinimum)/(a-1),!0)));if(null===this.minimum||null===this.maximum)"axisX"===
this.type?(d=null!==this.minimum?this.minimum:this.dataInfo.min,f=null!==this.maximum?this.maximum:this.dataInfo.max,1===f/d&&(l=Math.pow(this.logarithmBase,"undefined"===typeof this.options.interval?0.4:this.options.interval),f*=l,d/=l),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:f/d>this.logarithmBase?f/d*Math.pow(this.logarithmBase,0.5):this.logarithmBase):"axisY"===this.type&&(d=null!==this.minimum?this.minimum:this.dataInfo.min,f=null!==this.maximum?this.maximum:this.dataInfo.max,
isFinite(d)||isFinite(f)?1===d&&1===f?(f*=this.logarithmBase,d/=this.logarithmBase):1===f/d?(l=Math.pow(this.logarithmBase,this.interval),f*=l,d/=l):d>f?(l=Math.min(0.01*(d/f),5),1<=f?d=f/l:f=d*l):(l=Math.min(f/d*Math.pow(this.logarithmBase,0.01),Math.pow(this.logarithmBase,0.04)),1!==f&&(f*=l),1!==d&&(d/=l)):(f="undefined"===typeof this.options.interval?0:this.options.interval,d=1),g=Infinity!==this.dataInfo.minDiff?this.dataInfo.minDiff:f/d>this.logarithmBase?f/d*Math.pow(this.logarithmBase,0.5):
this.logarithmBase,this.includeZero&&(null===this.minimum||isNaN(this.minimum))&&1<d&&(d=1),this.includeZero&&(null===this.maximum||isNaN(this.maximum))&&1>f&&(f=1)),this.intervalType="number",null===this.minimum&&(this.minimum="axisX"===this.type?d/Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.floor(Math.log(d)/c/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?f*Math.sqrt(g):Math.pow(this.logarithmBase,this.interval*Math.ceil(Math.log(f)/c/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);d=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(c*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(c=Math.floor(this.viewportMinimum/b+0.5)*b;c<this.viewportMinimum;)c+=b;this.equidistantInterval=!1;this.intervalStartPosition=c;this.interval=b}else this.options.interval||(b=Math.ceil(this.interval),this.range>this.interval&&(this.interval=b,d=Math.pow(this.logarithmBase,Math.floor(Math.log(this.viewportMinimum)/(c*this.interval)+0.2)*this.interval))),this.equidistantInterval=!0,this.intervalStartPosition=d;if(!this.valueFormatString&&
(this.valueFormatString="#,##0.##",1>this.viewportMinimum)){c=Math.floor(Math.abs(Math.log(this.viewportMinimum)/Math.LN10))+2;if(isNaN(c)||!isFinite(c))c=2;if(2<c)for(b=0;b<c-2;b++)this.valueFormatString+="#"}};B.getNiceExponent=function(a,c){var b=Math.floor(Math.log(a)/Math.LN10),d=a/Math.pow(10,b),d=0>b?1>=d?1:5>=d?5:10:Math.max(Math.floor(d),1);return Number((d*Math.pow(10,b)).toFixed(20))};B.getNiceNumber=function(a,c){var b=Math.floor(Math.log(a)/Math.LN10),d=a/Math.pow(10,b);return Number(((c?
1.5>d?1:3>d?2:7>d?5:10:1>=d?1:2>=d?2:5>=d?5:10)*Math.pow(10,b)).toFixed(20))};B.prototype.getLabelStartPoint=function(){var a=G[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};T(pa,L);pa.prototype.createUserOptions=function(a){if("undefined"!==typeof a||this.options._isPlaceholder){var c=0;this.parent.options._isPlaceholder&&this.parent.createUserOptions();this.options._isPlaceholder||(sa(this.parent.stripLines),c=this.parent.options.stripLines.indexOf(this.options));this.options="undefined"===typeof a?{}:a;this.parent.options.stripLines[c]=this.options}};pa.prototype.render=function(){this.ctx.save();
var a=this.parent.getPixelCoordinatesOnAxis(this.value),c=Math.abs("pixel"===this._thicknessType?this.thickness:this.parent.conversionParameters.pixelPerUnit*this.thickness);if(0<c){var b=null===this.opacity?1:this.opacity;this.ctx.strokeStyle=this.color;this.ctx.beginPath();var d=this.ctx.globalAlpha;this.ctx.globalAlpha=b;D(this.id);var f,g,l,k;this.ctx.lineWidth=c;this.ctx.setLineDash&&this.ctx.setLineDash(F(this.lineDashType,c));if("bottom"===this.parent._position||"top"===this.parent._position)f=
g=1===this.ctx.lineWidth%2?(a.x<<0)+0.5:a.x<<0,l=this.chart.plotArea.y1,k=this.chart.plotArea.y2,this.bounds={x1:f-c/2,y1:l,x2:g+c/2,y2:k};else if("left"===this.parent._position||"right"===this.parent._position)l=k=1===this.ctx.lineWidth%2?(a.y<<0)+0.5:a.y<<0,f=this.chart.plotArea.x1,g=this.chart.plotArea.x2,this.bounds={x1:f,y1:l-c/2,x2:g,y2:k+c/2};this.ctx.moveTo(f,l);this.ctx.lineTo(g,k);this.ctx.stroke();this.ctx.globalAlpha=d}this.ctx.restore()};T(R,L);R.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.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: "+(t?"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+=t?"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;";t||(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)}};R.prototype.mouseMoveHandler=function(a,c){this._lastUpdated&&40>(new Date).getTime()-this._lastUpdated||(this._lastUpdated=(new Date).getTime(),this._updateToolTip(a,c))};
R.prototype._updateToolTip=function(a,c,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 c){if(isNaN(this._prevX)||isNaN(this._prevY))return;a=this._prevX;c=this._prevY}else this._prevX=a,this._prevY=c;var d=null,f=null,g=[],l=0;if(this.shared&&this.enabled&&"none"!==this.chart.plotInfo.axisPlacement){if("xySwapped"===this.chart.plotInfo.axisPlacement){f=[];if(this.chart.axisX)for(var k=
0;k<this.chart.axisX.length;k++){for(var l=this.chart.axisX[k].convertPixelToValue({y:c}),h=null,d=0;d<this.chart.axisX[k].dataSeries.length;d++)(h=this.chart.axisX[k].dataSeries[d].getDataPointAtX(l,b))&&0<=h.index&&(h.dataSeries=this.chart.axisX[k].dataSeries[d],null!==h.dataPoint.y&&f.push(h));h=null}if(this.chart.axisX2)for(k=0;k<this.chart.axisX2.length;k++){l=this.chart.axisX2[k].convertPixelToValue({y:c});h=null;for(d=0;d<this.chart.axisX2[k].dataSeries.length;d++)(h=this.chart.axisX2[k].dataSeries[d].getDataPointAtX(l,
b))&&0<=h.index&&(h.dataSeries=this.chart.axisX2[k].dataSeries[d],null!==h.dataPoint.y&&f.push(h));h=null}}else{f=[];if(this.chart.axisX)for(k=0;k<this.chart.axisX.length;k++)for(l=this.chart.axisX[k].convertPixelToValue({x:a}),h=null,d=0;d<this.chart.axisX[k].dataSeries.length;d++)(h=this.chart.axisX[k].dataSeries[d].getDataPointAtX(l,b))&&0<=h.index&&(h.dataSeries=this.chart.axisX[k].dataSeries[d],null!==h.dataPoint.y&&f.push(h));if(this.chart.axisX2)for(k=0;k<this.chart.axisX2.length;k++)for(l=
this.chart.axisX2[k].convertPixelToValue({x:a}),h=null,d=0;d<this.chart.axisX2[k].dataSeries.length;d++)(h=this.chart.axisX2[k].dataSeries[d].getDataPointAtX(l,b))&&0<=h.index&&(h.dataSeries=this.chart.axisX2[k].dataSeries[d],null!==h.dataPoint.y&&f.push(h))}if(0===f.length)return;f.sort(function(a,b){return a.distance-b.distance});b=f[0];for(d=0;d<f.length;d++)f[d].dataPoint.x.valueOf()===b.dataPoint.x.valueOf()&&g.push(f[d]);f=null}else{if(h=this.chart.getDataPointAtXY(a,c,b))this.currentDataPointIndex=
h.dataPointIndex,this.currentSeriesIndex=h.dataSeries.index;else if(t)if(h=Na(a,c,this.chart._eventManager.ghostCtx),0<h&&"undefined"!==typeof this.chart._eventManager.objectMap[h]){h=this.chart._eventManager.objectMap[h];if("legendItem"===h.objectType)return;this.currentSeriesIndex=h.dataSeriesIndex;this.currentDataPointIndex=0<=h.dataPointIndex?h.dataPointIndex:-1}else this.currentDataPointIndex=-1;else this.currentDataPointIndex=-1;if(0<=this.currentSeriesIndex){f=this.chart.data[this.currentSeriesIndex];
h={};if(0<=this.currentDataPointIndex)d=f.dataPoints[this.currentDataPointIndex],h.dataSeries=f,h.dataPoint=d,h.index=this.currentDataPointIndex,h.distance=Math.abs(d.x-l);else{if(!this.enabled||"line"!==f.type&&"stepLine"!==f.type&&"spline"!==f.type&&"area"!==f.type&&"stepArea"!==f.type&&"splineArea"!==f.type&&"stackedArea"!==f.type&&"stackedArea100"!==f.type&&"rangeArea"!==f.type&&"rangeSplineArea"!==f.type&&"candlestick"!==f.type&&"ohlc"!==f.type)return;l=f.axisX.convertPixelToValue({x:a});h=f.getDataPointAtX(l,
b);h.dataSeries=f;this.currentDataPointIndex=h.index;d=h.dataPoint}if(!y(h.dataPoint.y))if(h.dataSeries.axisY)if(0<h.dataPoint.y.length){for(d=b=0;d<h.dataPoint.y.length;d++)h.dataPoint.y[d]<h.dataSeries.axisY.viewportMinimum?b--:h.dataPoint.y[d]>h.dataSeries.axisY.viewportMaximum&&b++;b<h.dataPoint.y.length&&b>-h.dataPoint.y.length&&g.push(h)}else"column"===f.type||"bar"===f.type?0>h.dataPoint.y?0>h.dataSeries.axisY.viewportMinimum&&h.dataSeries.axisY.viewportMaximum>=h.dataPoint.y&&g.push(h):h.dataSeries.axisY.viewportMinimum<=
h.dataPoint.y&&0<=h.dataSeries.axisY.viewportMaximum&&g.push(h):"bubble"===f.type?(b=this.chart._eventManager.objectMap[f.dataPointIds[h.index]].size/2,h.dataPoint.y>=h.dataSeries.axisY.viewportMinimum-b&&h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum+b&&g.push(h)):(0<=h.dataSeries.type.indexOf("100")||"stackedColumn"===f.type||"stackedBar"===f.type||h.dataPoint.y>=h.dataSeries.axisY.viewportMinimum&&h.dataPoint.y<=h.dataSeries.axisY.viewportMaximum)&&g.push(h);else g.push(h)}}if(0<g.length&&(this.highlightObjects(g),
this.enabled))if(b="",b=this.getToolTipInnerHTML({entries:g}),null!==b){this.contentDiv.innerHTML=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:t?"rgba(255,255,255,.9)":"rgb(255,255,255)",this.borderColor=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:t?"italic":"normal"}catch(m){}"pie"===g[0].dataSeries.type||"doughnut"===g[0].dataSeries.type||"funnel"===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));a+="px";c=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):c:g[0].dataSeries.axisY.convertValueToPixel(g[0].dataPoint.y);c=-c+10;0<c+this.container.clientHeight+5&&(c-=c+this.container.clientHeight+
5-0);this.container.style.left=a;this.container.style.bottom=c+"px";!this.animationEnabled||b?this.disableAnimation():this.enableAnimation()}else this.hide(!1)}};R.prototype.highlightObjects=function(a){var c=this.chart.overlaidCanvasCtx;this.chart.resetOverlayedCanvas();c.clearRect(0,0,this.chart.width,this.chart.height);c.save();var b=this.chart.plotArea,d=0;c.beginPath();c.rect(b.x1,b.y1,b.x2-b.x1,b.y2-b.y1);c.clip();for(b=0;b<a.length;b++){var f=a[b];if((f=this.chart._eventManager.objectMap[f.dataSeries.dataPointIds[f.index]])&&
f.objectType&&"dataPoint"===f.objectType){var d=this.chart.data[f.dataSeriesIndex],g=d.dataPoints[f.dataPointIndex],l=f.dataPointIndex;!1===g.highlightEnabled||!0!==d.highlightEnabled&&!0!==g.highlightEnabled||("line"===d.type||"stepLine"===d.type||"spline"===d.type||"scatter"===d.type||"area"===d.type||"stepArea"===d.type||"splineArea"===d.type||"stackedArea"===d.type||"stackedArea100"===d.type||"rangeArea"===d.type||"rangeSplineArea"===d.type?(g=d.getMarkerProperties(l,f.x1,f.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),O.drawMarkers([g]),"undefined"!==typeof f.y2&&(g=d.getMarkerProperties(l,f.x1,f.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),O.drawMarkers([g]))):"bubble"===d.type?(g=d.getMarkerProperties(l,f.x1,f.y1,this.chart.overlaidCanvasCtx),g.size=f.size,g.color=
"white",g.borderColor="white",c.globalAlpha=0.3,O.drawMarkers([g]),c.globalAlpha=1):"column"===d.type||"stackedColumn"===d.type||"stackedColumn100"===d.type||"bar"===d.type||"rangeBar"===d.type||"stackedBar"===d.type||"stackedBar100"===d.type||"rangeColumn"===d.type?N(c,f.x1,f.y1,f.x2,f.y2,"white",0,null,!1,!1,!1,!1,0.3):"pie"===d.type||"doughnut"===d.type?Ha(c,f.center,f.radius,"white",d.type,f.startAngle,f.endAngle,0.3,f.percentInnerRadius):"candlestick"===d.type?(c.globalAlpha=1,c.strokeStyle=
f.color,c.lineWidth=2*f.borderThickness,d=0===c.lineWidth%2?0:0.5,c.beginPath(),c.moveTo(f.x3-d,Math.min(f.y2,f.y3)),c.lineTo(f.x3-d,Math.min(f.y1,f.y4)),c.stroke(),c.beginPath(),c.moveTo(f.x3-d,Math.max(f.y1,f.y4)),c.lineTo(f.x3-d,Math.max(f.y2,f.y3)),c.stroke(),N(c,f.x1,Math.min(f.y1,f.y4),f.x2,Math.max(f.y1,f.y4),"transparent",2*f.borderThickness,f.color,!1,!1,!1,!1),c.globalAlpha=1):"ohlc"===d.type&&(c.globalAlpha=1,c.strokeStyle=f.color,c.lineWidth=2*f.borderThickness,d=0===c.lineWidth%2?0:0.5,
c.beginPath(),c.moveTo(f.x3-d,f.y2),c.lineTo(f.x3-d,f.y3),c.stroke(),c.beginPath(),c.moveTo(f.x3,f.y1),c.lineTo(f.x1,f.y1),c.stroke(),c.beginPath(),c.moveTo(f.x3,f.y4),c.lineTo(f.x2,f.y4),c.stroke(),c.globalAlpha=1))}}c.restore();c.globalAlpha=1;c.beginPath()};R.prototype.getToolTipInnerHTML=function(a){a=a.entries;for(var c=null,b=null,d=null,f=0,g="",l=!0,k=0;k<a.length;k++)if(a[k].dataSeries.toolTipContent||a[k].dataPoint.toolTipContent){l=!1;break}if(l&&(this.content&&"function"===typeof this.content||
this.contentFormatter))a={chart:this.chart,toolTip:this.options,entries:a},c=this.contentFormatter?this.contentFormatter(a):this.content(a);else if(this.shared&&"none"!==this.chart.plotInfo.axisPlacement){for(var h=null,m="",k=0;k<a.length;k++)if(b=a[k].dataSeries,d=a[k].dataPoint,f=a[k].index,g="",0===k&&(l&&!this.content)&&(this.chart.axisX&&0<this.chart.axisX.length?m+="undefined"!==typeof this.chart.axisX[0].labels[d.x]?this.chart.axisX[0].labels[d.x]:"{x}":this.chart.axisX2&&0<this.chart.axisX2.length&&
(m+="undefined"!==typeof this.chart.axisX2[0].labels[d.x]?this.chart.axisX2[0].labels[d.x]:"{x}"),m+="</br>",m=this.chart.replaceKeywordsWithValue(m,d,b,f)),null!==d.toolTipContent&&("undefined"!==typeof d.toolTipContent||null!==b.options.toolTipContent)){if("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)this.chart.axisX&&1<this.chart.axisX.length&&(g+=h!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=d.toolTipContent?d.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}",h=b.axisXIndex;else if("bubble"===b.type)this.chart.axisX&&
1<this.chart.axisX.length&&(g+=h!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=d.toolTipContent?d.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}";else if("rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||"rangeSplineArea"===b.type)this.chart.axisX&&1<this.chart.axisX.length&&
(g+=h!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":""),g+=d.toolTipContent?d.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]}";else if("candlestick"===b.type||"ohlc"===b.type)this.chart.axisX&&1<this.chart.axisX.length&&(g+=h!=b.axisXIndex?b.axisX.title?b.axisX.title+"<br/>":"X:{axisXIndex}<br/>":
""),g+=d.toolTipContent?d.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]}";null===c&&(c="");!0===this.reversed?(c=this.chart.replaceKeywordsWithValue(g,d,b,f)+c,k<a.length-1&&(c="</br>"+c)):(c+=this.chart.replaceKeywordsWithValue(g,
d,b,f),k<a.length-1&&(c+="</br>"))}null!==c&&(c=m+c)}else{b=a[0].dataSeries;d=a[0].dataPoint;f=a[0].index;if(null===d.toolTipContent||"undefined"===typeof d.toolTipContent&&null===b.options.toolTipContent)return null;if("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)g=d.toolTipContent?d.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(d.label?"{label}":"{x}")+":</span>&nbsp;&nbsp;{y}";else if("bubble"===b.type)g=d.toolTipContent?d.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+
"\"'>"+(d.label?"{label}":"{x}")+":</span>&nbsp;&nbsp;{y}, &nbsp;&nbsp;{z}";else if("pie"===b.type||"doughnut"===b.type||"funnel"===b.type)g=d.toolTipContent?d.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(d.name?"{name}:</span>&nbsp;&nbsp;":d.label?"{label}:</span>&nbsp;&nbsp;":"</span>")+"{y}";else if("rangeColumn"===b.type||"rangeBar"===b.type||"rangeArea"===b.type||
"rangeSplineArea"===b.type)g=d.toolTipContent?d.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?"":"'color:{color};'")+"\"'>"+(d.label?"{label}":"{x}")+" :</span>&nbsp;&nbsp;{y[0]}, &nbsp;{y[1]}";else if("candlestick"===b.type||"ohlc"===b.type)g=d.toolTipContent?d.toolTipContent:b.toolTipContent?b.toolTipContent:this.content&&"function"!==typeof this.content?this.content:"<span style='\""+(this.options.fontColor?
"":"'color:{color};'")+"\"'>"+(d.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]}";null===c&&(c="");c+=this.chart.replaceKeywordsWithValue(g,d,b,f)}return c};R.prototype.enableAnimation=function(){this.container.style.WebkitTransition||(this.container.style.WebkitTransition="left .2s ease-out, bottom .2s ease-out",this.container.style.MozTransition="left .2s ease-out, bottom .2s ease-out",
this.container.style.MsTransition="left .2s ease-out, bottom .2s ease-out",this.container.style.transition="left .2s ease-out, bottom .2s ease-out")};R.prototype.disableAnimation=function(){this.container.style.WebkitTransition&&(this.container.style.WebkitTransition="",this.container.style.MozTransition="",this.container.style.MsTransition="",this.container.style.transition="")};R.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())};R.prototype.show=function(a,c,b){this._updateToolTip(a,c,"undefined"===typeof b?!1:b)};A.prototype.getPercentAndTotal=function(a,c){var b=null,d=null,f=null;if(0<=a.type.indexOf("stacked"))d=0,b=c.x.getTime?c.x.getTime():c.x,b in a.plotUnit.yTotals&&(d=a.plotUnit.yTotals[b],f=isNaN(c.y)?0:0===d?0:100*(c.y/d));else if("pie"===a.type||"doughnut"===a.type){for(i=d=0;i<a.dataPoints.length;i++)isNaN(a.dataPoints[i].y)||(d+=
a.dataPoints[i].y);f=isNaN(c.y)?0:100*(c.y/d)}return{percent:f,total:d}};A.prototype.replaceKeywordsWithValue=function(a,c,b,d,f){var g=this;f="undefined"===typeof f?0:f;if((0<=b.type.indexOf("stacked")||"pie"===b.type||"doughnut"===b.type)&&(0<=a.indexOf("#percent")||0<=a.indexOf("#total"))){var l="#percent",k="#total",h=this.getPercentAndTotal(b,c),k=isNaN(h.total)?k:h.total,l=isNaN(h.percent)?l:h.percent;do{h="";if(b.percentFormatString)h=b.percentFormatString;else{var h="#,##0.",m=Math.max(Math.ceil(Math.log(1/
Math.abs(l))/Math.LN10),2);if(isNaN(m)||!isFinite(m))m=2;for(var p=0;p<m;p++)h+="#";b.percentFormatString=h}a=a.replace("#percent",fa(l,h,g._cultureInfo));a=a.replace("#total",fa(k,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=ma(a.slice(1,a.length-1));a=a.replace("#index",
f);var e=null;try{var h=a.match(/(.*?)\s*\[\s*(.*?)\s*\]/);h&&0<h.length&&(e=ma(h[2]),a=ma(h[1]))}catch(k){}h=null;if("color"===a)return c.color?c.color:b.color?b.color:b._colorSet[d%b._colorSet.length];if(c.hasOwnProperty(a))h=c;else if(b.hasOwnProperty(a))h=b;else return"";h=h[a];null!==e&&(h=h[e]);if("x"===a)if("dateTime"===g.plotInfo.axisXValueType||"dateTime"===b.xValueType||c.x&&c.x.getTime){if(g.plotInfo.plotTypes[0].plotUnits[0].axisX&&!g.plotInfo.plotTypes[0].plotUnits[0].axisX.logarithmic)return Fa(h,
c.xValueFormatString?c.xValueFormatString:b.xValueFormatString?b.xValueFormatString:b.xValueFormatString=g.axisX&&g.axisX.autoValueFormatString?g.axisX.autoValueFormatString:"DD MMM YY",g._cultureInfo)}else return fa(h,c.xValueFormatString?c.xValueFormatString:b.xValueFormatString?b.xValueFormatString:b.xValueFormatString="#,##0.########",g._cultureInfo);else return"y"===a?fa(h,c.yValueFormatString?c.yValueFormatString:b.yValueFormatString?b.yValueFormatString:b.yValueFormatString="#,##0.########",
g._cultureInfo):"z"===a?fa(h,c.zValueFormatString?c.zValueFormatString:b.zValueFormatString?b.zValueFormatString:b.zValueFormatString="#,##0.########",g._cultureInfo):h})};na.prototype.reset=function(){this.lastObjectId=0;this.objectMap=[];this.rectangularRegionEventSubscriptions=[];this.previousDataPointEventObject=null;this.eventObjects=[];t&&(this.ghostCtx.clearRect(0,0,this.chart.width,this.chart.height),this.ghostCtx.beginPath())};na.prototype.getNewObjectTrackingId=function(){return++this.lastObjectId};
na.prototype.mouseEventHandler=function(a){if("mousemove"===a.type||"click"===a.type){var c=[],b=za(a),d=null;if((d=this.chart.getObjectAtXY(b.x,b.y,!1))&&"undefined"!==typeof this.objectMap[d])if(d=this.objectMap[d],"dataPoint"===d.objectType){var f=this.chart.data[d.dataSeriesIndex],g=f.dataPoints[d.dataPointIndex],l=d.dataPointIndex;d.eventParameter={x:b.x,y:b.y,dataPoint:g,dataSeries:f.options,dataPointIndex:l,dataSeriesIndex:f.index,chart:this.chart};d.eventContext={context:g,userContext:g,mouseover:"mouseover",
mousemove:"mousemove",mouseout:"mouseout",click:"click"};c.push(d);d=this.objectMap[f.id];d.eventParameter={x:b.x,y:b.y,dataPoint:g,dataSeries:f.options,dataPointIndex:l,dataSeriesIndex:f.index,chart:this.chart};d.eventContext={context:f,userContext:f.options,mouseover:"mouseover",mousemove:"mousemove",mouseout:"mouseout",click:"click"};c.push(this.objectMap[f.id])}else"legendItem"===d.objectType&&(f=this.chart.data[d.dataSeriesIndex],g=null!==d.dataPointIndex?f.dataPoints[d.dataPointIndex]:null,
d.eventParameter={x:b.x,y:b.y,dataSeries:f.options,dataPoint:g,dataPointIndex:d.dataPointIndex,dataSeriesIndex:d.dataSeriesIndex,chart:this.chart},d.eventContext={context:this.chart.legend,userContext:this.chart.legend.options,mouseover:"itemmouseover",mousemove:"itemmousemove",mouseout:"itemmouseout",click:"itemclick"},c.push(d));f=[];for(b=0;b<this.mouseoveredObjectMaps.length;b++){g=!0;for(d=0;d<c.length;d++)if(c[d].id===this.mouseoveredObjectMaps[b].id){g=!1;break}g?this.fireEvent(this.mouseoveredObjectMaps[b],
"mouseout",a):f.push(this.mouseoveredObjectMaps[b])}this.mouseoveredObjectMaps=f;for(b=0;b<c.length;b++){f=!1;for(d=0;d<this.mouseoveredObjectMaps.length;d++)if(c[b].id===this.mouseoveredObjectMaps[d].id){f=!0;break}f||(this.fireEvent(c[b],"mouseover",a),this.mouseoveredObjectMaps.push(c[b]));"click"===a.type?this.fireEvent(c[b],"click",a):"mousemove"===a.type&&this.fireEvent(c[b],"mousemove",a)}}};na.prototype.fireEvent=function(a,c,b){if(a&&c){var d=a.eventParameter,f=a.eventContext,g=a.eventContext.userContext;
g&&(f&&g[f[c]])&&g[f[c]].call(g,d);"mouseout"!==c?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"===c&&("dataPoint"===a.objectType&&this.chart.pieDoughnutClickHandler)&&this.chart.pieDoughnutClickHandler.call(this.chart.data[a.dataSeriesIndex],d)}};T(qa,L);Ea.prototype.animate=function(a,c,b,d,f){var g=this;this.chart.isAnimating=!0;f=f||E.easing.linear;b&&this.animations.push({startTime:(new Date).getTime()+
(a?a:0),duration:c,animationCallback:b,onComplete:d});for(a=[];0<this.animations.length;)if(c=this.animations.shift(),b=(new Date).getTime(),d=0,c.startTime<=b&&(d=f(Math.min(b-c.startTime,c.duration),0,1,c.duration),d=Math.min(d,1),isNaN(d)||!isFinite(d))&&(d=1),1>d&&a.push(c),c.animationCallback(d),1<=d&&c.onComplete)c.onComplete();this.animations=a;0<this.animations.length?this.animationRequestId=this.chart.requestAnimFrame.call(window,function(){g.animate.call(g)}):this.chart.isAnimating=!1};
Ea.prototype.cancelAllAnimations=function(){this.animations=[];this.animationRequestId&&this.chart.cancelRequestAnimFrame.call(window,this.animationRequestId);this.animationRequestId=null;this.chart.isAnimating=!1};var E={yScaleAnimation:function(a,c){if(0!==a){var b=c.dest,d=c.source.canvas,f=c.animationBase;b.drawImage(d,0,0,d.width,d.height,0,f-f*a,b.canvas.width/Q,a*b.canvas.height/Q)}},xScaleAnimation:function(a,c){if(0!==a){var b=c.dest,d=c.source.canvas,f=c.animationBase;b.drawImage(d,0,0,
d.width,d.height,f-f*a,0,a*b.canvas.width/Q,b.canvas.height/Q)}},xClipAnimation:function(a,c){if(0!==a){var b=c.dest,d=c.source.canvas;b.save();0<a&&b.drawImage(d,0,0,d.width*a,d.height,0,0,d.width*a/Q,d.height/Q);b.restore()}},fadeInAnimation:function(a,c){if(0!==a){var b=c.dest,d=c.source.canvas;b.save();b.globalAlpha=a;b.drawImage(d,0,0,d.width,d.height,0,0,b.canvas.width/Q,b.canvas.height/Q);b.restore()}},easing:{linear:function(a,c,b,d){return b*a/d+c},easeOutQuad:function(a,c,b,d){return-b*
(a/=d)*(a-2)+c},easeOutQuart:function(a,c,b,d){return-b*((a=a/d-1)*a*a*a-1)+c},easeInQuad:function(a,c,b,d){return b*(a/=d)*a+c},easeInQuart:function(a,c,b,d){return b*(a/=d)*a*a*a+c}}},O={drawMarker:function(a,c,b,d,f,g,l,k){if(b){var h=1;b.fillStyle=g?g:"#000000";b.strokeStyle=l?l:"#000000";b.lineWidth=k?k:0;"circle"===d?(b.moveTo(a,c),b.beginPath(),b.arc(a,c,f/2,0,2*Math.PI,!1),g&&b.fill(),k&&(l?b.stroke():(h=b.globalAlpha,b.globalAlpha=0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=h))):
"square"===d?(b.beginPath(),b.rect(a-f/2,c-f/2,f,f),g&&b.fill(),k&&(l?b.stroke():(h=b.globalAlpha,b.globalAlpha=0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=h))):"triangle"===d?(b.beginPath(),b.moveTo(a-f/2,c+f/2),b.lineTo(a+f/2,c+f/2),b.lineTo(a,c-f/2),b.closePath(),g&&b.fill(),k&&(l?b.stroke():(h=b.globalAlpha,b.globalAlpha=0.15,b.strokeStyle="black",b.stroke(),b.globalAlpha=h)),b.beginPath()):"cross"===d&&(b.strokeStyle=g,b.lineWidth=f/4,b.beginPath(),b.moveTo(a-f/2,c-f/2),b.lineTo(a+f/
2,c+f/2),b.stroke(),b.moveTo(a+f/2,c-f/2),b.lineTo(a-f/2,c+f/2),b.stroke())}},drawMarkers:function(a){for(var c=0;c<a.length;c++){var b=a[c];O.drawMarker(b.x,b.y,b.ctx,b.type,b.size,b.color,b.borderColor,b.borderThickness)}}},Ra={Chart:A,addColorSet:function(a,c){ja[a]=c},addCultureInfo:function(a,c){ra[a]=c},formatNumber:function(a,c,b){b=b||"en";if(ra[b])return fa(a,c||"#,##0.##",new qa(b));throw"Unknown Culture Name";},formatDate:function(a,c,b){b=b||"en";if(ra[b])return Fa(a,c||"DD MMM YYYY",
new qa(b));throw"Unknown Culture Name";}};Ra.Chart.version="v1.9.10 GA";window.CanvasJS=Ra})();
/*
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}();

View File

@@ -1,8 +1,9 @@
<!DOCTYPE html>
<html>
</head>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="../yjs-dist.js"></script>
<script src="./index.js"></script>
</head>
<body contenteditable="true">

View File

@@ -1,29 +1,21 @@
/* global Y */
window.onload = function () {
window.domBinding = new Y.DomBinding(window.yXmlType, document.body)
}
let y = new Y('htmleditor', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
// url: 'http://127.0.0.1:1234',
url: 'http://192.168.178.81:1234',
room: 'html-editor-example6'
},
share: {
xml: 'XmlFragment()' // y.share.xml is of type Y.Xml with tagname "p"
}
}).then(function (y) {
window.yXml = y
// Bind children of XmlFragment to the document.body
window.yXml.share.xml.bindToDom(document.body)
})
window.y = y
window.yXmlType = y.define('xml', Y.XmlFragment)
window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
captureTimeout: 500
})
document.onkeydown = function interceptUndoRedo (e) {
if (e.keyCode === 90 && (e.metaKey || e.ctrlKey)) {
if (!e.shiftKey) {
window.undoManager.undo()
} else {
window.undoManager.redo()
}
e.preventDefault()
}
}

View File

@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="codeMirrorContainer"></div>
<script src="../bower_components/codemirror/lib/codemirror.js"></script>
<script src="../bower_components/codemirror/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../bower_components/codemirror/lib/codemirror.css">
<style>
.CodeMirror {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
</style>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src='../../../y-indexeddb/y-indexeddb.js'></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,19 +0,0 @@
/* global Y, CodeMirror */
const persistence = new Y.IndexedDB()
const connector = {
connector: {
name: 'websockets-client',
room: 'codemirror-example'
}
}
const y = new Y('codemirror-example', connector, persistence)
window.yCodeMirror = y
var editor = CodeMirror(document.querySelector('#codeMirrorContainer'), {
mode: 'javascript',
lineNumbers: true
})
y.define('codemirror', Y.Text).bindCodeMirror(editor)

View File

@@ -10,7 +10,7 @@
.one {
grid-column: 1 ;
}
.two {
.two {
grid-column: 2;
}
.three {
@@ -49,7 +49,10 @@
</div>
</div>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-text/dist/y-text.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,38 +1,64 @@
/* global Y */
function bindYjsInstance (y, suffix) {
y.define('textarea', Y.Text).bind(document.getElementById('textarea' + suffix))
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'Textarea-example',
url: 'https://yjs-v13.herokuapp.com/'
},
share: {
textarea: 'Text'
}
}).then(function (y) {
window.y1 = y
y.share.textarea.bind(document.getElementById('textarea1'))
})
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'Textarea-example',
url: 'https://yjs-v13-second.herokuapp.com/'
},
share: {
textarea: 'Text'
}
}).then(function (y) {
window.y2 = y
y.share.textarea.bind(document.getElementById('textarea2'))
y.connector.socket.on('connection', function () {
document.getElementById('container' + suffix).removeAttribute('disconnected')
document.getElementById('container2').removeAttribute('disconnected')
})
y.connector.socket.on('disconnect', function () {
document.getElementById('container' + suffix).setAttribute('disconnected', true)
document.getElementById('container2').setAttribute('disconnected', true)
})
}
})
let y1 = new Y('infinite-example', {
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'Textarea-example',
url: 'https://yjs-v13-third.herokuapp.com/'
},
share: {
textarea: 'Text'
}
}).then(function (y) {
window.y3 = y
y.share.textarea.bind(document.getElementById('textarea3'))
y.connector.socket.on('connection', function () {
document.getElementById('container3').removeAttribute('disconnected')
})
y.connector.socket.on('disconnect', function () {
document.getElementById('container3').setAttribute('disconnected', true)
})
})
window.y1 = y1
bindYjsInstance(y1, '1')
let y2 = new Y('infinite-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.y2 = y2
bindYjsInstance(y2, '2')
let y3 = new Y('infinite-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.y3 = y3
bindYjsInstance(y1, '3')

View File

@@ -17,7 +17,9 @@
</g>
</svg>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../../../y-map/dist/y-map.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="../bower_components/d3/d3.js"></script>
<script src="./index.js"></script>
</body>

View File

@@ -1,67 +1,74 @@
/* @flow */
/* global Y, d3 */
let y = new Y('jigsaw-example', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
room: 'Puzzle-example',
url: 'http://localhost:1234'
},
share: {
piece1: 'Map',
piece2: 'Map',
piece3: 'Map',
piece4: 'Map'
}
})
}).then(function (y) {
window.yJigsaw = y
var origin // mouse start position - translation of piece
var drag = d3.behavior.drag()
.on('dragstart', function (params) {
// get the translation of the element
var translation = d3
.select(this)
.attr('transform')
.slice(10, -1)
.split(',')
.map(Number)
// mouse coordinates
var mouse = d3.mouse(this.parentNode)
origin = {
x: mouse[0] - translation[0],
y: mouse[1] - translation[1]
}
})
.on('drag', function () {
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart
var y = mouse[1] - origin.y
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')')
})
.on('dragend', function (piece, i) {
// save the current translation of the puzzle piece
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x
var y = mouse[1] - origin.y
piece.set('translation', {x: x, y: y})
})
let jigsaw = y.define('jigsaw', Y.Map)
window.yJigsaw = y
var data = [y.share.piece1, y.share.piece2, y.share.piece3, y.share.piece4]
var pieces = d3.select(document.querySelector('#puzzle-example')).selectAll('path').data(data)
var origin // mouse start position - translation of piece
var drag = d3.behavior.drag()
.on('dragstart', function (params) {
// get the translation of the element
var translation = d3
.select(this)
.attr('transform')
.slice(10, -1)
.split(',')
.map(Number)
// mouse coordinates
var mouse = d3.mouse(this.parentNode)
origin = {
x: mouse[0] - translation[0],
y: mouse[1] - translation[1]
}
})
.on('drag', function () {
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart
var y = mouse[1] - origin.y
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')')
})
.on('dragend', function (piece, i) {
// save the current translation of the puzzle piece
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x
var y = mouse[1] - origin.y
jigsaw.set(piece, {x: x, y: y})
})
pieces
.classed('draggable', true)
.attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0}
return 'translate(' + translation.x + ',' + translation.y + ')'
}).call(drag)
var data = ['piece1', 'piece2', 'piece3', 'piece4']
var pieces = d3.select(document.querySelector('#puzzle-example')).selectAll('path').data(data)
pieces
.classed('draggable', true)
.attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0}
return 'translate(' + translation.x + ',' + translation.y + ')'
}).call(drag)
data.forEach(function (piece) {
jigsaw.observe(function () {
// whenever a property of a piece changes, update the translation of the pieces
pieces
.transition()
.attr('transform', function (piece) {
var translation = piece.get(piece)
if (translation == null || typeof translation.x !== 'number' || typeof translation.y !== 'number') {
translation = { x: 0, y: 0 }
}
return 'translate(' + translation.x + ',' + translation.y + ')'
})
data.forEach(function (piece) {
piece.observe(function () {
// whenever a property of a piece changes, update the translation of the pieces
pieces
.transition()
.attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0}
return 'translate(' + translation.x + ',' + translation.y + ')'
})
})
})
})

View File

@@ -13,8 +13,11 @@
width: 100%;
}
</style>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/yjs/y.js"></script>
<script src="../bower_components/y-array/y-array.js"></script>
<script src="../bower_components/y-text/y-text.js"></script>
<script src="../bower_components/y-websockets-client/y-websockets-client.js"></script>
<script src="../bower_components/y-memory/y-memory.js"></script>
<script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
<script src="./index.js"></script>
</body>

View File

@@ -2,21 +2,29 @@
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' } })
let y = new Y('monaco-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
require(['vs/editor/editor.main'], function () {
window.yMonaco = y
// Initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'monaco-example'
},
sourceDir: '/bower_components',
share: {
monaco: 'Text' // y.share.monaco is of type Y.Text
}
}).then(function (y) {
window.yMonaco = y
// Create Monaco editor
var editor = monaco.editor.create(document.getElementById('monacoContainer'), {
language: 'javascript'
// Create Monaco editor
var editor = monaco.editor.create(document.getElementById('monacoContainer'), {
language: 'javascript'
})
// Bind to y.share.monaco
y.share.monaco.bindMonaco(editor)
})
// Bind to y.share.monaco
y.define('monaco', Y.Text).bindMonaco(editor)
})

File diff suppressed because it is too large Load Diff

View File

@@ -9,15 +9,12 @@
"author": "Kevin Jahns",
"license": "MIT",
"dependencies": {
"monaco-editor": "^0.8.3",
"rollup": "^0.52.3"
"monaco-editor": "^0.8.3"
},
"devDependencies": {
"standard": "^10.0.2"
},
"standard": {
"ignore": [
"bower_components"
]
"ignore": ["bower_components"]
}
}

View File

@@ -1,21 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<!-- Main quill library -->
<script src="../../node_modules/quill/dist/quill.min.js"></script>
<link href="../../node_modules/quill/dist/quill.snow.css" rel="stylesheet">
<!-- Quill cursors module -->
<script src="../../node_modules/quill-cursors/dist/quill-cursors.min.js"></script>
<link href="../../node_modules/quill-cursors/dist/quill-cursors.css" rel="stylesheet">
<!-- Yjs Library and connector -->
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
</head>
<body>
<div id="quill-container">
<div id="quill">
</div>
</div>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,78 +0,0 @@
/* global Y, Quill, QuillCursors */
Quill.register('modules/cursors', QuillCursors)
let y = new Y('quill-0', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
let users = y.define('users', Y.Array)
let myUserInfo = new Y.Map()
myUserInfo.set('name', 'dada')
myUserInfo.set('color', 'red')
users.push([myUserInfo])
let quill = new Quill('#quill-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block'],
[{ color: [] }, { background: [] }], // Snow theme fills in values
[{ script: 'sub' }, { script: 'super' }],
['link', 'image'],
['link', 'code-block'],
[{ list: 'ordered' }, { list: 'bullet' }]
],
cursors: {
hideDelay: 500
}
},
placeholder: 'Compose an epic...',
theme: 'snow' // or 'bubble'
})
let cursors = quill.getModule('cursors')
function drawCursors () {
cursors.clearCursors()
users.map((user, userId) => {
if (user !== myUserInfo) {
let relativeRange = user.get('range')
let lastUpdated = new Date(user.get('last updated'))
if (lastUpdated != null && new Date() - lastUpdated < 20000 && relativeRange != null) {
let start = Y.utils.fromRelativePosition(y, relativeRange.start).offset
let end = Y.utils.fromRelativePosition(y, relativeRange.end).offset
let range = { index: start, length: end - start }
cursors.setCursor(userId + '', range, user.get('name'), user.get('color'))
}
}
})
}
users.observeDeep(drawCursors)
drawCursors()
quill.on('selection-change', function (range) {
if (range != null) {
myUserInfo.set('range', {
start: Y.utils.getRelativePosition(yText, range.index),
end: Y.utils.getRelativePosition(yText, range.index + range.length)
})
} else {
myUserInfo.delete('range')
}
myUserInfo.set('last updated', new Date().toString())
})
let yText = y.define('quill', Y.Text)
let quillBinding = new Y.QuillBinding(yText, quill)
window.quillBinding = quillBinding
window.yText = yText
window.y = y
window.quill = quill
window.users = users
window.cursors = cursors

View File

@@ -1,18 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<!-- Main Quill library -->
<script src="../../node_modules/quill/dist/quill.min.js"></script>
<link href="../../node_modules/quill/dist/quill.snow.css" rel="stylesheet">
<!-- Yjs Library and connector -->
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<!-- quill does not include dist files! We are using the hosted version instead -->
<!--link rel="stylesheet" href="../bower_components/quill/dist/quill.snow.css" /-->
<link href="https://cdn.quilljs.com/1.0.4/quill.snow.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.2.0/styles/monokai-sublime.min.css" rel="stylesheet">
<style>
#quill-container {
border: 1px solid gray;
box-shadow: 0px 0px 10px gray;
}
</style>
</head>
<body>
<div id="quill-container">
<div id="quill">
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.5.1/katex.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.2.0/highlight.min.js" type="text/javascript"></script>
<script src="https://cdn.quilljs.com/1.0.4/quill.js"></script>
<!-- quill does not include dist files! We are using the hosted version instead (see above)
<script src="../bower_components/quill/dist/quill.js"></script>
-->
<script src="../../y.js"></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-richtext/dist/y-richtext.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,33 +1,40 @@
/* global Y, Quill */
let y = new Y('quill-cursors-0', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
let quill = new Quill('#quill-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block'],
[{ color: [] }, { background: [] }], // Snow theme fills in values
[{ script: 'sub' }, { script: 'super' }],
['link', 'image'],
['link', 'code-block'],
[{ list: 'ordered' }, { list: 'bullet' }]
]
room: 'richtext-example-quill-1.0-test',
url: 'http://localhost:1234'
},
placeholder: 'Compose an epic...',
theme: 'snow' // or 'bubble'
sourceDir: '/bower_components',
share: {
richtext: 'Richtext' // y.share.richtext is of type Y.Richtext
}
}).then(function (y) {
window.yQuill = y
// create quill element
window.quill = new Quill('#quill', {
modules: {
formula: true,
syntax: true,
toolbar: [
[{ size: ['small', false, 'large', 'huge'] }],
['bold', 'italic', 'underline'],
[{ color: [] }, { background: [] }], // Snow theme fills in values
[{ script: 'sub' }, { script: 'super' }],
['link', 'image'],
['link', 'code-block'],
[{ list: 'ordered' }]
]
},
theme: 'snow'
})
// bind quill to richtext type
y.share.richtext.bind(window.quill)
})
let yText = y.define('quill', Y.Text)
let quillBinding = new Y.QuillBinding(yText, quill)
window.quillBinding = quillBinding
window.yText = yText
window.y = y
window.quill = quill

View File

@@ -4,12 +4,10 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
input: 'yjs-dist.mjs',
name: 'Y',
output: {
file: 'yjs-dist.js',
format: 'umd'
},
entry: 'yjs-dist.esm',
dest: 'yjs-dist.js',
moduleName: 'Y',
format: 'umd',
plugins: [
nodeResolve({
main: true,
@@ -18,7 +16,7 @@ export default {
}),
commonjs()
],
sourcemap: true,
sourceMap: true,
banner: `
/**
* ${pkg.name} - ${pkg.description}

View File

@@ -1,9 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<textarea style="width:80%;" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<textarea style="width:80%;" rows=40 id="textfield" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../../../y-array/y-array.js"></script>
<script src="../../../y-text/y-text.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -1,15 +1,23 @@
/* global Y */
let y = new Y('textarea-example', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
room: 'Textarea-example2',
// url: '//localhost:1234',
url: 'https://yjs-v13.herokuapp.com/'
},
share: {
textarea: 'Text'
},
timeout: 5000 // reject if no connection was established within 5 seconds
}).then(function (y) {
window.yTextarea = y
// bind the textarea to a shared text element
y.share.textarea.bind(document.getElementById('textfield'))
})
window.yTextarea = y
// bind the textarea to a shared text element
let type = y.define('textarea', Y.Text)
let textarea = document.querySelector('textarea')
window.binding = new Y.TextareaBinding(type, textarea)

View File

@@ -1,10 +1,9 @@
<!DOCTYPE html>
<html>
</head>
<!-- jquery is not required for YXml. It is just here for convenience, and to test batch operations. -->
<!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../yjs-dist.js"></script>
<script src="./index.js"></script>
</head>
<body>
@@ -24,16 +23,14 @@
</div>
<script>
/* global $ */
var commands = document.querySelectorAll('.command')
Array.prototype.forEach.call(commands, function (command) {
var execute = function () {
// eslint-disable-next-line no-eval
eval(command.querySelector('input').value)
var commands = document.querySelectorAll(".command");
Array.prototype.forEach.call(document.querySelectorAll('.command'), function (command) {
var execute = function(){
eval(command.querySelector("input").value);
}
command.querySelector('button').onclick = execute
$(command.querySelector('input')).keyup(function (e) {
if (e.keyCode === 13) {
command.querySelector("button").onclick = execute
$(command.querySelector("input")).keyup(function (e) {
if (e.keyCode == 13) {
execute()
}
})

View File

@@ -1,13 +1,23 @@
/* global Y */
let y = new Y('xml-example', {
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
// url: 'http://127.0.0.1:1234',
url: 'http://192.168.178.81:1234',
room: 'Xml-example'
},
sourceDir: '/bower_components',
share: {
xml: 'Xml("p")' // y.share.xml is of type Y.Xml with tagname "p"
}
}).then(function (y) {
window.yXml = y
// bind xml type to a dom, and put it in body
window.sharedDom = y.share.xml.getDom()
document.body.appendChild(window.sharedDom)
})
window.yXml = y
// bind xml type to a dom, and put it in body
window.sharedDom = y.define('xml', Y.XmlElement).toDom()
document.body.appendChild(window.sharedDom)

12
examples/yjs-dist.esm Normal file
View File

@@ -0,0 +1,12 @@
import Y from '../src/y.js'
import yArray from '../../y-array/src/y-array.js'
import yIndexedDB from '../../y-indexeddb/src/y-indexeddb.js'
import yMap from '../../y-map/src/y-map.js'
import yText from '../../y-text/src/Text.js'
import yXml from '../../y-xml/src/y-xml.js'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
Y.extend(yArray, yIndexedDB, yMap, yText, yXml, yWebsocketsClient)
export default Y

View File

@@ -1,9 +0,0 @@
import Y from '../src/Y.js'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.js'
Y.extend(yWebsocketsClient)
extendYIndexedDBPersistence(Y)
export default Y

2386
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "yjs",
"version": "13.0.0-54",
"version": "13.0.0-18",
"description": "A framework for real-time p2p shared editing on any data",
"main": "./y.node.js",
"browser": "./y.js",
@@ -9,18 +9,13 @@
"test": "npm run lint",
"debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
"lint": "standard",
"docs": "esdoc",
"serve-docs": "npm run docs && serve ./docs/",
"dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
"watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'",
"postversion": "npm run dist",
"postpublish": "tag-dist-files --overwrite-existing-tag"
},
"files": [
"y.*",
"src/*",
".esdoc.json",
"docs/*"
"y.*"
],
"standard": {
"ignore": [
@@ -57,10 +52,6 @@
"chance": "^1.0.9",
"concurrently": "^3.4.0",
"cutest": "^0.1.9",
"esdoc": "^1.0.4",
"esdoc-standard-plugin": "^1.0.0",
"quill": "^1.3.5",
"quill-cursors": "^1.0.2",
"rollup-plugin-babel": "^2.7.1",
"rollup-plugin-commonjs": "^8.0.2",
"rollup-plugin-inject": "^2.0.0",
@@ -73,6 +64,7 @@
"tag-dist-files": "^0.1.6"
},
"dependencies": {
"debug": "^2.6.8"
"debug": "^2.6.8",
"utf-8": "^1.0.0"
}
}

View File

@@ -5,13 +5,9 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
input: 'src/Y.dist.js',
name: 'Y',
sourcemap: true,
output: {
file: 'y.js',
format: 'umd'
},
entry: 'src/y.js',
moduleName: 'Y',
format: 'umd',
plugins: [
nodeResolve({
main: true,
@@ -21,9 +17,6 @@ export default {
commonjs(),
babel(),
uglify({
mangle: {
except: ['YMap', 'Y', 'YArray', 'YText', 'YXmlHook', 'YXmlFragment', 'YXmlElement', 'YXmlEvent', 'YXmlText', 'YEvent', 'YArrayEvent', 'YMapEvent', 'Type', 'Delete', 'ItemJSON', 'ItemString', 'Item']
},
output: {
comments: function (node, comment) {
var text = comment.value
@@ -36,6 +29,8 @@ export default {
}
})
],
dest: 'y.js',
sourceMap: true,
banner: `
/**
* ${pkg.name} - ${pkg.description}

View File

@@ -3,13 +3,9 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json')
export default {
input: 'src/Y.dist.js',
nameame: 'Y',
sourcemap: true,
output: {
file: 'y.node.js',
format: 'cjs'
},
entry: 'src/y.js',
moduleName: 'Y',
format: 'umd',
plugins: [
nodeResolve({
main: true,
@@ -18,6 +14,8 @@ export default {
}),
commonjs()
],
dest: 'y.node.js',
sourceMap: true,
banner: `
/**
* ${pkg.name} - ${pkg.description}

View File

@@ -3,20 +3,18 @@ import commonjs from 'rollup-plugin-commonjs'
import multiEntry from 'rollup-plugin-multi-entry'
export default {
input: 'test/index.js',
name: 'y-tests',
sourcemap: true,
output: {
file: 'y.test.js',
format: 'umd'
},
entry: 'test/*.js',
moduleName: 'y-tests',
format: 'umd',
plugins: [
multiEntry(),
nodeResolve({
main: true,
module: true,
browser: true
}),
commonjs()
]
commonjs(),
multiEntry()
],
dest: 'y.test.js',
sourceMap: true
}

View File

@@ -1,47 +0,0 @@
import { createMutualExclude } from '../Util/mutualExclude.js'
/**
* Abstract class for bindings.
*
* A binding handles data binding from a Yjs type to a data object. For example,
* you can bind a Quill editor instance to a YText instance with the `QuillBinding` class.
*
* It is expected that a concrete implementation accepts two parameters
* (type and binding target).
*
* @example
* const quill = new Quill(document.createElement('div'))
* const type = y.define('quill', Y.Text)
* const binding = new Y.QuillBinding(quill, type)
*
*/
export default class Binding {
/**
* @param {YType} type Yjs type.
* @param {any} target Binding Target.
*/
constructor (type, target) {
/**
* The Yjs type that is bound to `target`
* @type {YType}
*/
this.type = type
/**
* The target that `type` is bound to.
* @type {*}
*/
this.target = target
/**
* @private
*/
this._mutualExclude = createMutualExclude()
}
/**
* Remove all data observers (both from the type and the target).
*/
destroy () {
this.type = null
this.target = null
}
}

View File

@@ -1,151 +0,0 @@
/* global MutationObserver */
import Binding from '../Binding.js'
import { createAssociation, removeAssociation } from './util.js'
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js'
import { defaultFilter, applyFilterOnType } from './filter.js'
import typeObserver from './typeObserver.js'
import domObserver from './domObserver.js'
/**
* A binding that binds the children of a YXmlFragment to a DOM element.
*
* This binding is automatically destroyed when its parent is deleted.
*
* @example
* const div = document.createElement('div')
* const type = y.define('xml', Y.XmlFragment)
* const binding = new Y.QuillBinding(type, div)
*
*/
export default class DomBinding extends Binding {
/**
* @param {YXmlFragment} type The bind source. This is the ultimate source of
* truth.
* @param {Element} target The bind target. Mirrors the target.
* @param {Object} [opts] Optional configurations
* @param {FilterFunction} [opts.filter=defaultFilter] The filter function to use.
*/
constructor (type, target, opts = {}) {
// Binding handles textType as this.type and domTextarea as this.target
super(type, target)
this.opts = opts
opts.document = opts.document || document
opts.hooks = opts.hooks || {}
/**
* Maps each DOM element to the type that it is associated with.
* @type {Map}
*/
this.domToType = new Map()
/**
* Maps each YXml type to the DOM element that it is associated with.
* @type {Map}
*/
this.typeToDom = new Map()
/**
* Defines which DOM attributes and elements to filter out.
* Also filters remote changes.
* @type {FilterFunction}
*/
this.filter = opts.filter || defaultFilter
// set initial value
target.innerHTML = ''
type.forEach(child => {
target.insertBefore(child.toDom(opts.document, opts.hooks, this), null)
})
this._typeObserver = typeObserver.bind(this)
this._domObserver = (mutations) => {
domObserver.call(this, mutations, opts.document)
}
type.observeDeep(this._typeObserver)
this._mutationObserver = new MutationObserver(this._domObserver)
this._mutationObserver.observe(target, {
childList: true,
attributes: true,
characterData: true,
subtree: true
})
const y = type._y
// Force flush dom changes before Type changes are applied (they might
// modify the dom)
this._beforeTransactionHandler = (y, transaction, remote) => {
this._domObserver(this._mutationObserver.takeRecords())
beforeTransactionSelectionFixer(y, this, transaction, remote)
}
y.on('beforeTransaction', this._beforeTransactionHandler)
this._afterTransactionHandler = (y, transaction, remote) => {
afterTransactionSelectionFixer(y, this, transaction, remote)
// remove associations
// TODO: this could be done more efficiently
// e.g. Always delete using the following approach, or removeAssociation
// in dom/type-observer..
transaction.deletedStructs.forEach(type => {
const dom = this.typeToDom.get(type)
if (dom !== undefined) {
removeAssociation(this, dom, type)
}
})
}
y.on('afterTransaction', this._afterTransactionHandler)
// Before calling observers, apply dom filter to all changed and new types.
this._beforeObserverCallsHandler = (y, transaction) => {
// Apply dom filter to new and changed types
transaction.changedTypes.forEach((subs, type) => {
// Only check attributes. New types are filtered below.
if ((subs.size > 1 || (subs.size === 1 && subs.has(null) === false))) {
applyFilterOnType(y, this, type)
}
})
transaction.newTypes.forEach(type => {
applyFilterOnType(y, this, type)
})
}
y.on('beforeObserverCalls', this._beforeObserverCallsHandler)
createAssociation(this, target, type)
}
/**
* Enables the smart scrolling functionality for a Dom Binding.
* This is useful when YXml is bound to a shared editor. When activated,
* the viewport will be changed to accommodate remote changes.
*
* @param {Element} scrollElement The node that is
*/
enableSmartScrolling (scrollElement) {
// @TODO: implement smart scrolling
}
/**
* NOTE: currently does not apply filter to existing elements!
* @param {FilterFunction} filter The filter function to use from now on.
*/
setFilter (filter) {
this.filter = filter
// TODO: apply filter to all elements
}
/**
* Remove all properties that are handled by this class.
*/
destroy () {
this.domToType = null
this.typeToDom = null
this.type.unobserve(this._typeObserver)
this._mutationObserver.disconnect()
const y = this.type._y
y.off('beforeTransaction', this._beforeTransactionHandler)
y.off('beforeObserverCalls', this._beforeObserverCallsHandler)
y.off('afterObserverCalls', this._afterObserverCallsHandler)
y.off('afterTransaction', this._afterTransactionHandler)
super.destroy()
}
}
/**
* A filter defines which elements and attributes to share.
* Return null if the node should be filtered. Otherwise return the Map of
* accepted attributes.
*
* @typedef {function(nodeName: String, attrs: Map): Map|null} FilterFunction
*/

View File

@@ -1,136 +0,0 @@
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import {
iterateUntilUndeleted,
removeAssociation,
insertNodeHelper } from './util.js'
import diff from '../../Util/simpleDiff.js'
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
/**
* 1. Check if any of the nodes was deleted
* 2. Iterate over the children.
* 2.1 If a node exists that is not yet bound to a type, insert a new node
* 2.2 If _contents.length < dom.childNodes.length, fill the
* rest of _content with childNodes
* 2.3 If a node was moved, delete it and
* recreate a new yxml element that is bound to that node.
* You can detect that a node was moved because expectedId
* !== actualId in the list
* @private
*/
function applyChangesFromDom (binding, dom, yxml, _document) {
if (yxml == null || yxml === false || yxml.constructor === YXmlHook) {
return
}
const y = yxml._y
const knownChildren = new Set()
for (let i = dom.childNodes.length - 1; i >= 0; i--) {
const type = binding.domToType.get(dom.childNodes[i])
if (type !== undefined && type !== false) {
knownChildren.add(type)
}
}
// 1. Check if any of the nodes was deleted
yxml.forEach(function (childType) {
if (knownChildren.has(childType) === false) {
childType._delete(y)
removeAssociation(binding, binding.typeToDom.get(childType), childType)
}
})
// 2. iterate
const childNodes = dom.childNodes
const len = childNodes.length
let prevExpectedType = null
let expectedType = iterateUntilUndeleted(yxml._start)
for (let domCnt = 0; domCnt < len; domCnt++) {
const childNode = childNodes[domCnt]
const childType = binding.domToType.get(childNode)
if (childType !== undefined) {
if (childType === false) {
// should be ignored or is going to be deleted
continue
}
if (expectedType !== null) {
if (expectedType !== childType) {
// 2.3 Not expected node
if (childType._parent !== yxml) {
// child was moved from another parent
// childType is going to be deleted by its previous parent
removeAssociation(binding, childNode, childType)
} else {
// child was moved to a different position.
childType._delete(y)
removeAssociation(binding, childNode, childType)
}
prevExpectedType = insertNodeHelper(yxml, prevExpectedType, childNode, _document, binding)
} else {
// Found expected node. Continue.
prevExpectedType = expectedType
expectedType = iterateUntilUndeleted(expectedType._right)
}
} else {
// 2.2 Fill _content with child nodes
prevExpectedType = insertNodeHelper(yxml, prevExpectedType, childNode, _document, binding)
}
} else {
// 2.1 A new node was found
prevExpectedType = insertNodeHelper(yxml, prevExpectedType, childNode, _document, binding)
}
}
}
/**
* @private
*/
export default function domObserver (mutations, _document) {
this._mutualExclude(() => {
this.type._y.transact(() => {
let diffChildren = new Set()
mutations.forEach(mutation => {
const dom = mutation.target
const yxml = this.domToType.get(dom)
if (yxml === false || yxml === undefined || yxml.constructor === YXmlHook) {
// dom element is filtered
return
}
switch (mutation.type) {
case 'characterData':
var change = diff(yxml.toString(), dom.nodeValue)
yxml.delete(change.pos, change.remove)
yxml.insert(change.pos, change.insert)
break
case 'attributes':
if (yxml.constructor === YXmlFragment) {
break
}
let name = mutation.attributeName
let val = dom.getAttribute(name)
// check if filter accepts attribute
let attributes = new Map()
attributes.set(name, val)
if (yxml.constructor !== YXmlFragment && this.filter(dom.nodeName, attributes).size > 0) {
if (yxml.getAttribute(name) !== val) {
if (val == null) {
yxml.removeAttribute(name)
} else {
yxml.setAttribute(name, val)
}
}
}
break
case 'childList':
diffChildren.add(mutation.target)
break
}
})
for (let dom of diffChildren) {
if (dom.yOnChildrenChanged !== undefined) {
dom.yOnChildrenChanged()
}
const yxml = this.domToType.get(dom)
applyChangesFromDom(this, dom, yxml, _document)
}
})
})
}

View File

@@ -1,59 +0,0 @@
import { YXmlText, YXmlElement, YXmlHook } from '../../Types/YXml/YXml.js'
import { createAssociation, domsToTypes } from './util.js'
import { filterDomAttributes, defaultFilter } from './filter.js'
/**
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
*
* @param {Element|TextNode} element The DOM Element
* @param {?Document} _document Optional. Provide the global document object
* @param {Hooks} [hooks = {}] Optional. Set of Yjs Hooks
* @param {Filter} [filter=defaultFilter] Optional. Dom element filter
* @param {?DomBinding} binding Warning: This property is for internal use only!
* @return {YXmlElement | YXmlText}
*/
export default function domToType (element, _document = document, hooks = {}, filter = defaultFilter, binding) {
let type
switch (element.nodeType) {
case _document.ELEMENT_NODE:
let hookName = null
let hook
// configure `hookName !== undefined` if element is a hook.
if (element.hasAttribute('data-yjs-hook')) {
hookName = element.getAttribute('data-yjs-hook')
hook = hooks[hookName]
if (hook === undefined) {
console.error(`Unknown hook "${hookName}". Deleting yjsHook dataset property.`)
delete element.removeAttribute('data-yjs-hook')
hookName = null
}
}
if (hookName === null) {
// Not a hook
const attrs = filterDomAttributes(element, filter)
if (attrs === null) {
type = false
} else {
type = new YXmlElement(element.nodeName)
attrs.forEach((val, key) => {
type.setAttribute(key, val)
})
type.insert(0, domsToTypes(element.childNodes, document, hooks, filter, binding))
}
} else {
// Is a hook
type = new YXmlHook(hookName)
hook.fillType(element, type)
}
break
case _document.TEXT_NODE:
type = new YXmlText()
type.insert(0, element.nodeValue)
break
default:
throw new Error('Can\'t transform this node type to a YXml type!')
}
createAssociation(binding, element, type)
return type
}

View File

@@ -1,60 +0,0 @@
import isParentOf from '../../Util/isParentOf.js'
/**
* Default filter method (does nothing).
*
* @param {String} nodeName The nodeName of the element
* @param {Map} attrs Map of key-value pairs that are attributes of the node.
* @return {Map | null} The allowed attributes or null, if the element should be
* filtered.
*/
export function defaultFilter (nodeName, attrs) {
// TODO: implement basic filter that filters out dangerous properties!
return attrs
}
/**
*
*/
export function filterDomAttributes (dom, filter) {
const attrs = new Map()
for (let i = dom.attributes.length - 1; i >= 0; i--) {
const attr = dom.attributes[i]
attrs.set(attr.name, attr.value)
}
return filter(dom.nodeName, attrs)
}
/**
* Applies a filter on a type.
*
* @param {Y} y The Yjs instance.
* @param {DomBinding} binding The DOM binding instance that has the dom filter.
* @param {YXmlElement | YXmlFragment } type The type to apply the filter to.
*
* @private
*/
export function applyFilterOnType (y, binding, type) {
if (isParentOf(binding.type, type)) {
const nodeName = type.nodeName
let attributes = new Map()
if (type.getAttributes !== undefined) {
let attrs = type.getAttributes()
for (let key in attrs) {
attributes.set(key, attrs[key])
}
}
const filteredAttributes = binding.filter(nodeName, new Map(attributes))
if (filteredAttributes === null) {
type._delete(y)
} else {
// iterate original attributes
attributes.forEach((value, key) => {
// delete all attributes that are not in filteredAttributes
if (filteredAttributes.has(key) === false) {
type.removeAttribute(key)
}
})
}
}
}

View File

@@ -1,84 +0,0 @@
/* globals getSelection */
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
let browserSelection = null
let relativeSelection = null
/**
* @private
*/
export let beforeTransactionSelectionFixer
if (typeof getSelection !== 'undefined') {
beforeTransactionSelectionFixer = function _beforeTransactionSelectionFixer (y, domBinding, transaction, remote) {
if (!remote) {
return
}
relativeSelection = { from: null, to: null, fromY: null, toY: null }
browserSelection = getSelection()
const anchorNode = browserSelection.anchorNode
const anchorNodeType = domBinding.domToType.get(anchorNode)
if (anchorNode !== null && anchorNodeType !== undefined) {
relativeSelection.from = getRelativePosition(anchorNodeType, browserSelection.anchorOffset)
relativeSelection.fromY = anchorNodeType._y
}
const focusNode = browserSelection.focusNode
const focusNodeType = domBinding.domToType.get(focusNode)
if (focusNode !== null && focusNodeType !== undefined) {
relativeSelection.to = getRelativePosition(focusNodeType, browserSelection.focusOffset)
relativeSelection.toY = focusNodeType._y
}
}
} else {
beforeTransactionSelectionFixer = function _fakeBeforeTransactionSelectionFixer () {}
}
/**
* @private
*/
export function afterTransactionSelectionFixer (y, domBinding, transaction, remote) {
if (relativeSelection === null || !remote) {
return
}
const to = relativeSelection.to
const from = relativeSelection.from
const fromY = relativeSelection.fromY
const toY = relativeSelection.toY
let shouldUpdate = false
let anchorNode = browserSelection.anchorNode
let anchorOffset = browserSelection.anchorOffset
let focusNode = browserSelection.focusNode
let focusOffset = browserSelection.focusOffset
if (from !== null) {
let sel = fromRelativePosition(fromY, from)
if (sel !== null) {
let node = domBinding.typeToDom.get(sel.type)
let offset = sel.offset
if (node !== anchorNode || offset !== anchorOffset) {
anchorNode = node
anchorOffset = offset
shouldUpdate = true
}
}
}
if (to !== null) {
let sel = fromRelativePosition(toY, to)
if (sel !== null) {
let node = domBinding.typeToDom.get(sel.type)
let offset = sel.offset
if (node !== focusNode || offset !== focusOffset) {
focusNode = node
focusOffset = offset
shouldUpdate = true
}
}
}
if (shouldUpdate) {
browserSelection.setBaseAndExtent(
anchorNode,
anchorOffset,
focusNode,
focusOffset
)
}
}

View File

@@ -1,63 +0,0 @@
import YXmlText from '../../Types/YXml/YXmlText.js'
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import { removeDomChildrenUntilElementFound } from './util.js'
/**
* @private
*/
export default function typeObserver (events) {
this._mutualExclude(() => {
events.forEach(event => {
const yxml = event.target
const dom = this.typeToDom.get(yxml)
if (dom !== undefined && dom !== false) {
if (yxml.constructor === YXmlText) {
dom.nodeValue = yxml.toString()
// TODO: use hasOwnProperty instead of === undefined check
} else if (event.attributesChanged !== undefined) {
// update attributes
event.attributesChanged.forEach(attributeName => {
const value = yxml.getAttribute(attributeName)
if (value === undefined) {
dom.removeAttribute(attributeName)
} else {
dom.setAttribute(attributeName, value)
}
})
/*
* TODO: instead of hard-checking the types, it would be best to
* specify the type's features. E.g.
* - _yxmlHasAttributes
* - _yxmlHasChildren
* Furthermore, the features shouldn't be encoded in the types,
* only in the attributes (above)
*/
if (event.childListChanged && yxml.constructor !== YXmlHook) {
let currentChild = dom.firstChild
yxml.forEach(childType => {
const childNode = this.typeToDom.get(childType)
switch (childNode) {
case undefined:
// Does not exist. Create it.
const node = childType.toDom(this.opts.document, this.opts.hooks, this)
dom.insertBefore(node, currentChild)
break
case false:
// nop
break
default:
// Is already attached to the dom.
// Find it and remove all dom nodes in-between.
removeDomChildrenUntilElementFound(dom, currentChild, childNode)
currentChild = childNode.nextSibling
break
}
})
removeDomChildrenUntilElementFound(dom, currentChild, null)
}
}
}
})
})
}

View File

@@ -1,124 +0,0 @@
import domToType from './domToType.js'
/**
* Iterates items until an undeleted item is found.
*
* @private
*/
export function iterateUntilUndeleted (item) {
while (item !== null && item._deleted) {
item = item._right
}
return item
}
/**
* Removes an association (the information that a DOM element belongs to a
* type).
*
* @param {DomBinding} domBinding The binding object
* @param {Element} dom The dom that is to be associated with type
* @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
*
*/
export function removeAssociation (domBinding, dom, type) {
domBinding.domToType.delete(dom)
domBinding.typeToDom.delete(type)
}
/**
* Creates an association (the information that a DOM element belongs to a
* type).
*
* @param {DomBinding} domBinding The binding object
* @param {Element} dom The dom that is to be associated with type
* @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
*
*/
export function createAssociation (domBinding, dom, type) {
if (domBinding !== undefined) {
domBinding.domToType.set(dom, type)
domBinding.typeToDom.set(type, dom)
}
}
/**
* If oldDom is associated with a type, associate newDom with the type and
* forget about oldDom. If oldDom is not associated with any type, nothing happens.
*
* @param {DomBinding} domBinding The binding object
* @param {Element} oldDom The existing dom
* @param {Element} newDom The new dom object
*/
export function switchAssociation (domBinding, oldDom, newDom) {
if (domBinding !== undefined) {
const type = domBinding.domToType.get(oldDom)
if (type !== undefined) {
removeAssociation(domBinding, oldDom, type)
createAssociation(domBinding, newDom, type)
}
}
}
/**
* Insert Dom Elements after one of the children of this YXmlFragment.
* The Dom elements will be bound to a new YXmlElement and inserted at the
* specified position.
*
* @param {YXmlElement} type The type in which to insert DOM elements.
* @param {YXmlElement|null} prev The reference node. New YxmlElements are
* inserted after this node. Set null to insert at
* the beginning.
* @param {Array<Element>} doms The Dom elements to insert.
* @param {?Document} _document Optional. Provide the global document object.
* @param {DomBinding} binding The dom binding
* @return {Array<YXmlElement>} The YxmlElements that are inserted.
*
* @private
*/
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
const types = domsToTypes(doms, _document, binding.opts.hooks, binding.filter, binding)
return type.insertAfter(prev, types)
}
export function domsToTypes (doms, _document, hooks, filter, binding) {
const types = []
for (let dom of doms) {
const t = domToType(dom, _document, hooks, filter, binding)
if (t !== false) {
types.push(t)
}
}
return types
}
/**
* @private
*/
export function insertNodeHelper (yxml, prevExpectedNode, child, _document, binding) {
let insertedNodes = insertDomElementsAfter(yxml, prevExpectedNode, [child], _document, binding)
if (insertedNodes.length > 0) {
return insertedNodes[0]
} else {
return prevExpectedNode
}
}
/**
* Remove children until `elem` is found.
*
* @param {Element} parent The parent of `elem` and `currentChild`.
* @param {Element} currentChild Start removing elements with `currentChild`. If
* `currentChild` is `elem` it won't be removed.
* @param {Element|null} elem The elemnt to look for.
*
* @private
*/
export function removeDomChildrenUntilElementFound (parent, currentChild, elem) {
while (currentChild !== elem) {
const del = currentChild
currentChild = currentChild.nextSibling
parent.removeChild(del)
}
}

View File

@@ -1,53 +0,0 @@
import Binding from '../Binding.js'
function typeObserver (event) {
const quill = this.target
// Force flush Quill changes.
quill.update('yjs')
this._mutualExclude(function () {
// Apply computed delta.
quill.updateContents(event.delta, 'yjs')
// Force flush Quill changes. Ignore applied changes.
quill.update('yjs')
})
}
function quillObserver (delta) {
this._mutualExclude(() => {
this.type.applyDelta(delta.ops)
})
}
/**
* A Binding that binds a YText type to a Quill editor.
*
* @example
* const quill = new Quill(document.createElement('div'))
* const type = y.define('quill', Y.Text)
* const binding = new Y.QuillBinding(quill, type)
* // Now modifications on the DOM will be reflected in the Type, and the other
* // way around!
*/
export default class QuillBinding extends Binding {
/**
* @param {YText} textType
* @param {Quill} quill
*/
constructor (textType, quill) {
// Binding handles textType as this.type and quill as this.target.
super(textType, quill)
// Set initial value.
quill.setContents(textType.toDelta(), 'yjs')
// Observers are handled by this class.
this._typeObserver = typeObserver.bind(this)
this._quillObserver = quillObserver.bind(this)
textType.observe(this._typeObserver)
quill.on('text-change', this._quillObserver)
}
destroy () {
// Remove everything that is handled by this class.
this.type.unobserve(this._typeObserver)
this.target.off('text-change', this._quillObserver)
super.destroy()
}
}

View File

@@ -1,56 +0,0 @@
import Binding from '../Binding.js'
import simpleDiff from '../../Util/simpleDiff.js'
import { getRelativePosition, fromRelativePosition } from '../../Util/relativePosition.js'
function typeObserver () {
this._mutualExclude(() => {
const textarea = this.target
const textType = this.type
const relativeStart = getRelativePosition(textType, textarea.selectionStart)
const relativeEnd = getRelativePosition(textType, textarea.selectionEnd)
textarea.value = textType.toString()
const start = fromRelativePosition(textType._y, relativeStart)
const end = fromRelativePosition(textType._y, relativeEnd)
textarea.setSelectionRange(start, end)
})
}
function domObserver () {
this._mutualExclude(() => {
let diff = simpleDiff(this.type.toString(), this.target.value)
this.type.delete(diff.pos, diff.remove)
this.type.insert(diff.pos, diff.insert)
})
}
/**
* A binding that binds a YText to a dom textarea.
*
* This binding is automatically destroyed when its parent is deleted.
*
* @example
* const textare = document.createElement('textarea')
* const type = y.define('textarea', Y.Text)
* const binding = new Y.QuillBinding(type, textarea)
*
*/
export default class TextareaBinding extends Binding {
constructor (textType, domTextarea) {
// Binding handles textType as this.type and domTextarea as this.target
super(textType, domTextarea)
// set initial value
domTextarea.value = textType.toString()
// Observers are handled by this class
this._typeObserver = typeObserver.bind(this)
this._domObserver = domObserver.bind(this)
textType.observe(this._typeObserver)
domTextarea.addEventListener('input', this._domObserver)
}
destroy () {
// Remove everything that is handled by this class
this.type.unobserve(this._typeObserver)
this.target.unobserve(this._domObserver)
super.destroy()
}
}

View File

@@ -1,297 +1,294 @@
import BinaryEncoder from './Util/Binary/Encoder.js'
import BinaryDecoder from './Util/Binary/Decoder.js'
import { BinaryEncoder, BinaryDecoder } from './Encoding.js'
import { sendSyncStep1, computeMessageSyncStep1, computeMessageSyncStep2, computeMessageUpdate } from './MessageHandler.js'
import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.js'
import { readSyncStep2 } from './MessageHandler/syncStep2.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import debug from 'debug'
// TODO: rename Connector
export default class AbstractConnector {
constructor (y, opts) {
this.y = y
this.opts = opts
if (opts.role == null || opts.role === 'master') {
this.role = 'master'
} else if (opts.role === 'slave') {
this.role = 'slave'
} else {
throw new Error("Role must be either 'master' or 'slave'!")
export default function extendConnector (Y/* :any */) {
class AbstractConnector {
/*
opts contains the following information:
role : String Role of this client ("master" or "slave")
*/
constructor (y, opts) {
this.y = y
if (opts == null) {
opts = {}
}
this.opts = opts
// Prefer to receive untransformed operations. This does only work if
// this client receives operations from only one other client.
// In particular, this does not work with y-webrtc.
// It will work with y-websockets-client
this.preferUntransformed = opts.preferUntransformed || false
if (opts.role == null || opts.role === 'master') {
this.role = 'master'
} else if (opts.role === 'slave') {
this.role = 'slave'
} else {
throw new Error("Role must be either 'master' or 'slave'!")
}
this.log = Y.debug('y:connector')
this.logMessage = Y.debug('y:connector-message')
this.y.db.forwardAppliedOperations = opts.forwardAppliedOperations || false
this.role = opts.role
this.connections = new Map()
this.isSynced = false
this.userEventListeners = []
this.whenSyncedListeners = []
this.currentSyncTarget = null
this.debug = opts.debug === true
this.broadcastOpBuffer = []
this.protocolVersion = 11
this.authInfo = opts.auth || null
this.checkAuth = opts.checkAuth || function () { return Promise.resolve('write') } // default is everyone has write access
if (opts.generateUserId !== false) {
this.setUserId(Y.utils.generateUserId())
}
}
this.log = debug('y:connector')
this.logMessage = debug('y:connector-message')
this._forwardAppliedStructs = opts.forwardAppliedOperations || false // TODO: rename
this.role = opts.role
this.connections = new Map()
this.isSynced = false
this.userEventListeners = []
this.whenSyncedListeners = []
this.currentSyncTarget = null
this.debug = opts.debug === true
this.broadcastBuffer = new BinaryEncoder()
this.broadcastBufferSize = 0
this.protocolVersion = 11
this.authInfo = opts.auth || null
this.checkAuth = opts.checkAuth || function () { return Promise.resolve('write') } // default is everyone has write access
if (opts.maxBufferLength == null) {
this.maxBufferLength = -1
} else {
this.maxBufferLength = opts.maxBufferLength
reconnect () {
this.log('reconnecting..')
return this.y.db.startGarbageCollector()
}
}
reconnect () {
this.log('reconnecting..')
}
disconnect () {
this.log('discronnecting..')
this.connections = new Map()
this.isSynced = false
this.currentSyncTarget = null
this.whenSyncedListeners = []
this.y.db.stopGarbageCollector()
return this.y.db.whenTransactionsFinished()
}
disconnect () {
this.log('discronnecting..')
this.connections = new Map()
this.isSynced = false
this.currentSyncTarget = null
this.whenSyncedListeners = []
return Promise.resolve()
}
repair () {
this.log('Repairing the state of Yjs. This can happen if messages get lost, and Yjs detects that something is wrong. If this happens often, please report an issue here: https://github.com/y-js/yjs/issues')
this.isSynced = false
this.connections.forEach((user, userId) => {
user.isSynced = false
this._syncWithUser(userId)
})
}
onUserEvent (f) {
this.userEventListeners.push(f)
}
setUserId (userId) {
if (this.userId == null) {
if (!Number.isInteger(userId)) {
let err = new Error('UserId must be an integer!')
this.y.emit('error', err)
throw err
}
this.log('Set userId to "%s"', userId)
this.userId = userId
return this.y.db.setUserId(userId)
} else {
return null
}
}
removeUserEventListener (f) {
this.userEventListeners = this.userEventListeners.filter(g => f !== g)
}
onUserEvent (f) {
this.userEventListeners.push(f)
}
userLeft (user) {
if (this.connections.has(user)) {
this.log('%s: User left %s', this.y.userID, user)
this.connections.delete(user)
// check if isSynced event can be sent now
this._setSyncedWith(null)
removeUserEventListener (f) {
this.userEventListeners = this.userEventListeners.filter(g => f !== g)
}
userLeft (user) {
if (this.connections.has(user)) {
this.log('%s: User left %s', this.userId, user)
this.connections.delete(user)
// check if isSynced event can be sent now
this._setSyncedWith(null)
for (var f of this.userEventListeners) {
f({
action: 'userLeft',
user: user
})
}
}
}
userJoined (user, role, auth) {
if (role == null) {
throw new Error('You must specify the role of the joined user!')
}
if (this.connections.has(user)) {
throw new Error('This user already joined!')
}
this.log('%s: User joined %s', this.userId, user)
this.connections.set(user, {
uid: user,
isSynced: false,
role: role,
processAfterAuth: [],
auth: auth || null,
receivedSyncStep2: false
})
let defer = {}
defer.promise = new Promise(function (resolve) { defer.resolve = resolve })
this.connections.get(user).syncStep2 = defer
for (var f of this.userEventListeners) {
f({
action: 'userLeft',
user: user
action: 'userJoined',
user: user,
role: role
})
}
this._syncWithUser(user)
}
}
userJoined (user, role, auth) {
if (role == null) {
throw new Error('You must specify the role of the joined user!')
}
if (this.connections.has(user)) {
throw new Error('This user already joined!')
}
this.log('%s: User joined %s', this.y.userID, user)
this.connections.set(user, {
uid: user,
isSynced: false,
role: role,
processAfterAuth: [],
processAfterSync: [],
auth: auth || null,
receivedSyncStep2: false
})
let defer = {}
defer.promise = new Promise(function (resolve) { defer.resolve = resolve })
this.connections.get(user).syncStep2 = defer
for (var f of this.userEventListeners) {
f({
action: 'userJoined',
user: user,
role: role
})
}
this._syncWithUser(user)
}
// Execute a function _when_ we are connected.
// If not connected, wait until connected
whenSynced (f) {
if (this.isSynced) {
f()
} else {
this.whenSyncedListeners.push(f)
}
}
_syncWithUser (userID) {
if (this.role === 'slave') {
return // "The current sync has not finished or this is controlled by a master!"
}
sendSyncStep1(this, userID)
}
_fireIsSyncedListeners () {
if (!this.isSynced) {
this.isSynced = true
// It is safer to remove this!
// call whensynced listeners
for (var f of this.whenSyncedListeners) {
// Execute a function _when_ we are connected.
// If not connected, wait until connected
whenSynced (f) {
if (this.isSynced) {
f()
} else {
this.whenSyncedListeners.push(f)
}
this.whenSyncedListeners = []
this.y._setContentReady()
this.y.emit('synced')
}
}
send (uid, buffer) {
const y = this.y
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
throw new Error('Expected Message to be an ArrayBuffer or Uint8Array - don\'t use this method to send custom messages')
_syncWithUser (userid) {
if (this.role === 'slave') {
return // "The current sync has not finished or this is controlled by a master!"
}
sendSyncStep1(this, userid)
}
this.log('User%s to User%s: Send \'%y\'', y.userID, uid, buffer)
this.logMessage('User%s to User%s: Send %Y', y.userID, uid, [y, buffer])
}
broadcast (buffer) {
const y = this.y
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
throw new Error('Expected Message to be an ArrayBuffer or Uint8Array - don\'t use this method to send custom messages')
}
this.log('User%s: Broadcast \'%y\'', y.userID, buffer)
this.logMessage('User%s: Broadcast: %Y', y.userID, [y, buffer])
}
/*
Buffer operations, and broadcast them when ready.
*/
broadcastStruct (struct) {
const firstContent = this.broadcastBuffer.length === 0
if (firstContent) {
this.broadcastBuffer.writeVarString(this.y.room)
this.broadcastBuffer.writeVarString('update')
this.broadcastBufferSize = 0
this.broadcastBufferSizePos = this.broadcastBuffer.pos
this.broadcastBuffer.writeUint32(0)
}
this.broadcastBufferSize++
struct._toBinary(this.broadcastBuffer)
if (this.maxBufferLength > 0 && this.broadcastBuffer.length > this.maxBufferLength) {
// it is necessary to send the buffer now
// cache the buffer and check if server is responsive
const buffer = this.broadcastBuffer
buffer.setUint32(this.broadcastBufferSizePos, this.broadcastBufferSize)
this.broadcastBuffer = new BinaryEncoder()
this.whenRemoteResponsive().then(() => {
this.broadcast(buffer.createBuffer())
})
} else if (firstContent) {
// send the buffer when all transactions are finished
// (or buffer exceeds maxBufferLength)
setTimeout(() => {
if (this.broadcastBuffer.length > 0) {
const buffer = this.broadcastBuffer
buffer.setUint32(this.broadcastBufferSizePos, this.broadcastBufferSize)
this.broadcast(buffer.createBuffer())
this.broadcastBuffer = new BinaryEncoder()
}
}, 0)
}
}
/*
* Somehow check the responsiveness of the remote clients/server
* Default behavior:
* Wait 100ms before broadcasting the next batch of operations
*
* Only used when maxBufferLength is set
*
*/
whenRemoteResponsive () {
return new Promise(function (resolve) {
setTimeout(resolve, 100)
})
}
/*
You received a raw message, and you know that it is intended for Yjs. Then call this function.
*/
receiveMessage (sender, buffer, skipAuth) {
const y = this.y
const userID = y.userID
skipAuth = skipAuth || false
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
return Promise.reject(new Error('Expected Message to be an ArrayBuffer or Uint8Array!'))
}
if (sender === userID) {
return Promise.resolve()
}
let decoder = new BinaryDecoder(buffer)
let encoder = new BinaryEncoder()
let roomname = decoder.readVarString() // read room name
encoder.writeVarString(roomname)
let messageType = decoder.readVarString()
let senderConn = this.connections.get(sender)
this.log('User%s from User%s: Receive \'%s\'', userID, sender, messageType)
this.logMessage('User%s from User%s: Receive %Y', userID, sender, [y, buffer])
if (senderConn == null && !skipAuth) {
throw new Error('Received message from unknown peer!')
}
if (messageType === 'sync step 1' || messageType === 'sync step 2') {
let auth = decoder.readVarUint()
if (senderConn.auth == null) {
senderConn.processAfterAuth.push([messageType, senderConn, decoder, encoder, sender])
// check auth
return this.checkAuth(auth, y, sender).then(authPermissions => {
if (senderConn.auth == null) {
senderConn.auth = authPermissions
y.emit('userAuthenticated', {
user: senderConn.uid,
auth: authPermissions
})
_fireIsSyncedListeners () {
this.y.db.whenTransactionsFinished().then(() => {
if (!this.isSynced) {
this.isSynced = true
// It is safer to remove this!
// TODO: remove: this.garbageCollectAfterSync()
// call whensynced listeners
for (var f of this.whenSyncedListeners) {
f()
}
let messages = senderConn.processAfterAuth
senderConn.processAfterAuth = []
messages.forEach(m =>
this.computeMessage(m[0], m[1], m[2], m[3], m[4])
)
})
}
}
if ((skipAuth || senderConn.auth != null) && (messageType !== 'update' || senderConn.isSynced)) {
this.computeMessage(messageType, senderConn, decoder, encoder, sender, skipAuth)
} else {
senderConn.processAfterSync.push([messageType, senderConn, decoder, encoder, sender, false])
}
}
computeMessage (messageType, senderConn, decoder, encoder, sender, skipAuth) {
if (messageType === 'sync step 1' && (senderConn.auth === 'write' || senderConn.auth === 'read')) {
// cannot wait for sync step 1 to finish, because we may wait for sync step 2 in sync step 1 (->lock)
readSyncStep1(decoder, encoder, this.y, senderConn, sender)
} else {
const y = this.y
y.transact(function () {
if (messageType === 'sync step 2' && senderConn.auth === 'write') {
readSyncStep2(decoder, encoder, y, senderConn, sender)
} else if (messageType === 'update' && (skipAuth || senderConn.auth === 'write')) {
integrateRemoteStructs(y, decoder)
} else {
throw new Error('Unable to receive message')
this.whenSyncedListeners = []
}
}, true)
}
}
_setSyncedWith (user) {
if (user != null) {
const userConn = this.connections.get(user)
userConn.isSynced = true
const messages = userConn.processAfterSync
userConn.processAfterSync = []
messages.forEach(m => {
this.computeMessage(m[0], m[1], m[2], m[3], m[4])
})
}
const conns = Array.from(this.connections.values())
if (conns.length > 0 && conns.every(u => u.isSynced)) {
this._fireIsSyncedListeners()
send (uid, buffer) {
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
throw new Error('Expected Message to be an ArrayBuffer or Uint8Array - please don\'t use this method to send custom messages')
}
this.log('%s: Send \'%y\' to %s', this.userId, buffer, uid)
this.logMessage('Message: %Y', buffer)
}
broadcast (buffer) {
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
throw new Error('Expected Message to be an ArrayBuffer or Uint8Array - please don\'t use this method to send custom messages')
}
this.log('%s: Broadcast \'%y\'', this.userId, buffer)
this.logMessage('Message: %Y', buffer)
}
/*
Buffer operations, and broadcast them when ready.
*/
broadcastOps (ops) {
ops = ops.map(function (op) {
return Y.Struct[op.struct].encode(op)
})
var self = this
function broadcastOperations () {
if (self.broadcastOpBuffer.length > 0) {
let encoder = new BinaryEncoder()
encoder.writeVarString(self.opts.room)
encoder.writeVarString('update')
let ops = self.broadcastOpBuffer
self.broadcastOpBuffer = []
let length = ops.length
encoder.writeUint32(length)
for (var i = 0; i < length; i++) {
let op = ops[i]
Y.Struct[op.struct].binaryEncode(encoder, op)
}
self.broadcast(encoder.createBuffer())
}
}
if (this.broadcastOpBuffer.length === 0) {
this.broadcastOpBuffer = ops
this.y.db.whenTransactionsFinished().then(broadcastOperations)
} else {
this.broadcastOpBuffer = this.broadcastOpBuffer.concat(ops)
}
}
/*
You received a raw message, and you know that it is intended for Yjs. Then call this function.
*/
receiveMessage (sender, buffer, skipAuth) {
skipAuth = skipAuth || false
if (!(buffer instanceof ArrayBuffer || buffer instanceof Uint8Array)) {
return Promise.reject(new Error('Expected Message to be an ArrayBuffer or Uint8Array!'))
}
if (sender === this.userId) {
return Promise.resolve()
}
let decoder = new BinaryDecoder(buffer)
let encoder = new BinaryEncoder()
let roomname = decoder.readVarString() // read room name
encoder.writeVarString(roomname)
let messageType = decoder.readVarString()
let senderConn = this.connections.get(sender)
this.log('%s: Receive \'%s\' from %s', this.userId, messageType, sender)
this.logMessage('Message: %Y', buffer)
if (senderConn == null && !skipAuth) {
throw new Error('Received message from unknown peer!')
}
if (messageType === 'sync step 1' || messageType === 'sync step 2') {
let auth = decoder.readVarUint()
if (senderConn.auth == null) {
senderConn.processAfterAuth.push([messageType, senderConn, decoder, encoder, sender])
// check auth
return this.checkAuth(auth, this.y, sender).then(authPermissions => {
if (senderConn.auth == null) {
senderConn.auth = authPermissions
this.y.emit('userAuthenticated', {
user: senderConn.uid,
auth: authPermissions
})
}
let messages = senderConn.processAfterAuth
senderConn.processAfterAuth = []
return messages.reduce((p, m) =>
p.then(() => this.computeMessage(m[0], m[1], m[2], m[3], m[4]))
, Promise.resolve())
})
}
}
if (skipAuth || senderConn.auth != null) {
return this.computeMessage(messageType, senderConn, decoder, encoder, sender, skipAuth)
} else {
senderConn.processAfterAuth.push([messageType, senderConn, decoder, encoder, sender, false])
}
}
computeMessage (messageType, senderConn, decoder, encoder, sender, skipAuth) {
if (messageType === 'sync step 1' && (senderConn.auth === 'write' || senderConn.auth === 'read')) {
// cannot wait for sync step 1 to finish, because we may wait for sync step 2 in sync step 1 (->lock)
computeMessageSyncStep1(decoder, encoder, this, senderConn, sender)
return this.y.db.whenTransactionsFinished()
} else if (messageType === 'sync step 2' && senderConn.auth === 'write') {
return computeMessageSyncStep2(decoder, encoder, this, senderConn, sender)
} else if (messageType === 'update' && (skipAuth || senderConn.auth === 'write')) {
return computeMessageUpdate(decoder, encoder, this, senderConn, sender)
} else {
return Promise.reject(new Error('Unable to receive message'))
}
}
_setSyncedWith (user) {
if (user != null) {
this.connections.get(user).isSynced = true
}
let conns = Array.from(this.connections.values())
if (conns.length > 0 && conns.every(u => u.isSynced)) {
this._fireIsSyncedListeners()
}
}
}
Y.AbstractConnector = AbstractConnector
}

609
src/Database.js Normal file
View File

@@ -0,0 +1,609 @@
/* @flow */
'use strict'
export default function extendDatabase (Y /* :any */) {
/*
Partial definition of an OperationStore.
TODO: name it Database, operation store only holds operations.
A database definition must alse define the following methods:
* logTable() (optional)
- show relevant information information in a table
* requestTransaction(makeGen)
- request a transaction
* destroy()
- destroy the database
*/
class AbstractDatabase {
/* ::
y: YConfig;
forwardAppliedOperations: boolean;
listenersById: Object;
listenersByIdExecuteNow: Array<Object>;
listenersByIdRequestPending: boolean;
initializedTypes: Object;
whenUserIdSetListener: ?Function;
waitingTransactions: Array<Transaction>;
transactionInProgress: boolean;
executeOrder: Array<Object>;
gc1: Array<Struct>;
gc2: Array<Struct>;
gcTimeout: number;
gcInterval: any;
garbageCollect: Function;
executeOrder: Array<any>; // for debugging only
userId: UserId;
opClock: number;
transactionsFinished: ?{promise: Promise, resolve: any};
transact: (x: ?Generator) => any;
*/
constructor (y, opts) {
this.y = y
opts.gc = opts.gc === true
this.dbOpts = opts
var os = this
this.userId = null
var resolve_
this.userIdPromise = new Promise(function (resolve) {
resolve_ = resolve
})
this.userIdPromise.resolve = resolve_
// whether to broadcast all applied operations (insert & delete hook)
this.forwardAppliedOperations = false
// E.g. this.listenersById[id] : Array<Listener>
this.listenersById = {}
// Execute the next time a transaction is requested
this.listenersByIdExecuteNow = []
// A transaction is requested
this.listenersByIdRequestPending = false
/* To make things more clear, the following naming conventions:
* ls : we put this.listenersById on ls
* l : Array<Listener>
* id : Id (can't use as property name)
* sid : String (converted from id via JSON.stringify
so we can use it as a property name)
Always remember to first overwrite
a property before you iterate over it!
*/
// TODO: Use ES7 Weak Maps. This way types that are no longer user,
// wont be kept in memory.
this.initializedTypes = {}
this.waitingTransactions = []
this.transactionInProgress = false
this.transactionIsFlushed = false
if (typeof YConcurrencyTestingMode !== 'undefined') {
this.executeOrder = []
}
this.gc1 = [] // first stage
this.gc2 = [] // second stage -> after that, remove the op
function garbageCollect () {
return os.whenTransactionsFinished().then(function () {
if (os.gcTimeout > 0 && (os.gc1.length > 0 || os.gc2.length > 0)) {
if (!os.y.connector.isSynced) {
console.warn('gc should be empty when not synced!')
}
return new Promise((resolve) => {
os.requestTransaction(function () {
if (os.y.connector != null && os.y.connector.isSynced) {
for (var i = 0; i < os.gc2.length; i++) {
var oid = os.gc2[i]
this.garbageCollectOperation(oid)
}
os.gc2 = os.gc1
os.gc1 = []
}
// TODO: Use setInterval here instead (when garbageCollect is called several times there will be several timeouts..)
if (os.gcTimeout > 0) {
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
}
resolve()
})
})
} else {
// TODO: see above
if (os.gcTimeout > 0) {
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
}
return Promise.resolve()
}
})
}
this.garbageCollect = garbageCollect
this.startGarbageCollector()
this.repairCheckInterval = !opts.repairCheckInterval ? 6000 : opts.repairCheckInterval
this.opsReceivedTimestamp = new Date()
this.startRepairCheck()
}
startGarbageCollector () {
this.gc = this.dbOpts.gc
if (this.gc) {
this.gcTimeout = !this.dbOpts.gcTimeout ? 30000 : this.dbOpts.gcTimeout
} else {
this.gcTimeout = -1
}
if (this.gcTimeout > 0) {
this.garbageCollect()
}
}
startRepairCheck () {
var os = this
if (this.repairCheckInterval > 0) {
this.repairCheckIntervalHandler = setInterval(function repairOnMissingOperations () {
/*
Case 1. No ops have been received in a while (new Date() - os.opsReceivedTimestamp > os.repairCheckInterval)
- 1.1 os.listenersById is empty. Then the state was correct the whole time. -> Nothing to do (nor to update)
- 1.2 os.listenersById is not empty.
* Then the state was incorrect for at least {os.repairCheckInterval} seconds.
* -> Remove everything in os.listenersById and sync again (connector.repair())
Case 2. An op has been received in the last {os.repairCheckInterval } seconds.
It is not yet necessary to check for faulty behavior. Everything can still resolve itself. Wait for more messages.
If nothing was received for a while and os.listenersById is still not emty, we are in case 1.2
-> Do nothing
Baseline here is: we really only have to catch case 1.2..
*/
if (
new Date() - os.opsReceivedTimestamp > os.repairCheckInterval &&
Object.keys(os.listenersById).length > 0 // os.listenersById is not empty
) {
// haven't received operations for over {os.repairCheckInterval} seconds, resend state vector
os.listenersById = {}
os.opsReceivedTimestamp = new Date() // update so you don't send repair several times in a row
os.y.connector.repair()
}
}, this.repairCheckInterval)
}
}
stopRepairCheck () {
clearInterval(this.repairCheckIntervalHandler)
}
queueGarbageCollector (id) {
if (this.y.connector.isSynced && this.gc) {
this.gc1.push(id)
}
}
emptyGarbageCollector () {
return new Promise(resolve => {
var check = () => {
if (this.gc1.length > 0 || this.gc2.length > 0) {
this.garbageCollect().then(check)
} else {
resolve()
}
}
setTimeout(check, 0)
})
}
addToDebug () {
if (typeof YConcurrencyTestingMode !== 'undefined') {
var command /* :string */ = Array.prototype.map.call(arguments, function (s) {
if (typeof s === 'string') {
return s
} else {
return JSON.stringify(s)
}
}).join('').replace(/"/g, "'").replace(/,/g, ', ').replace(/:/g, ': ')
this.executeOrder.push(command)
}
}
getDebugData () {
console.log(this.executeOrder.join('\n'))
}
stopGarbageCollector () {
var self = this
this.gc = false
this.gcTimeout = -1
return new Promise(function (resolve) {
self.requestTransaction(function () {
var ungc /* :Array<Struct> */ = self.gc1.concat(self.gc2)
self.gc1 = []
self.gc2 = []
for (var i = 0; i < ungc.length; i++) {
var op = this.getOperation(ungc[i])
if (op != null) {
delete op.gc
this.setOperation(op)
}
}
resolve()
})
})
}
/*
Try to add to GC.
TODO: rename this function
Rulez:
* Only gc if this user is online & gc turned on
* The most left element in a list must not be gc'd.
=> There is at least one element in the list
returns true iff op was added to GC
*/
addToGarbageCollector (op, left) {
if (
op.gc == null &&
op.deleted === true &&
this.store.gc &&
this.store.y.connector.isSynced
) {
var gc = false
if (left != null && left.deleted === true) {
gc = true
} else if (op.content != null && op.content.length > 1) {
op = this.getInsertionCleanStart([op.id[0], op.id[1] + 1])
gc = true
}
if (gc) {
op.gc = true
this.setOperation(op)
this.store.queueGarbageCollector(op.id)
return true
}
}
return false
}
removeFromGarbageCollector (op) {
function filter (o) {
return !Y.utils.compareIds(o, op.id)
}
this.gc1 = this.gc1.filter(filter)
this.gc2 = this.gc2.filter(filter)
delete op.gc
}
destroyTypes () {
for (var key in this.initializedTypes) {
var type = this.initializedTypes[key]
if (type._destroy != null) {
type._destroy()
} else {
console.error('The type you included does not provide destroy functionality, it will remain in memory (updating your packages will help).')
}
}
}
destroy () {
clearTimeout(this.gcInterval)
this.gcInterval = null
this.stopRepairCheck()
}
setUserId (userId) {
if (!this.userIdPromise.inProgress) {
this.userIdPromise.inProgress = true
var self = this
self.requestTransaction(function () {
self.userId = userId
var state = this.getState(userId)
self.opClock = state.clock
self.userIdPromise.resolve(userId)
})
}
return this.userIdPromise
}
whenUserIdSet (f) {
this.userIdPromise.then(f)
}
getNextOpId (numberOfIds) {
if (numberOfIds == null) {
throw new Error('getNextOpId expects the number of created ids to create!')
} else if (this.userId == null) {
throw new Error('OperationStore not yet initialized!')
} else {
var id = [this.userId, this.opClock]
this.opClock += numberOfIds
return id
}
}
/*
Apply a list of operations.
* we save a timestamp, because we received new operations that could resolve ops in this.listenersById (see this.startRepairCheck)
* get a transaction
* check whether all Struct.*.requiredOps are in the OS
* check if it is an expected op (otherwise wait for it)
* check if was deleted, apply a delete operation after op was applied
*/
applyOperations (decoder) {
this.opsReceivedTimestamp = new Date()
let length = decoder.readUint32()
for (var i = 0; i < length; i++) {
let o = Y.Struct.binaryDecodeOperation(decoder)
if (o.id == null || o.id[0] !== this.y.connector.userId) {
var required = Y.Struct[o.struct].requiredOps(o)
if (o.requires != null) {
required = required.concat(o.requires)
}
this.whenOperationsExist(required, o)
}
}
}
/*
op is executed as soon as every operation requested is available.
Note that Transaction can (and should) buffer requests.
*/
whenOperationsExist (ids, op) {
if (ids.length > 0) {
let listener = {
op: op,
missing: ids.length
}
for (let i = 0; i < ids.length; i++) {
let id = ids[i]
let sid = JSON.stringify(id)
let l = this.listenersById[sid]
if (l == null) {
l = []
this.listenersById[sid] = l
}
l.push(listener)
}
} else {
this.listenersByIdExecuteNow.push({
op: op
})
}
if (this.listenersByIdRequestPending) {
return
}
this.listenersByIdRequestPending = true
var store = this
this.requestTransaction(function () {
var exeNow = store.listenersByIdExecuteNow
store.listenersByIdExecuteNow = []
var ls = store.listenersById
store.listenersById = {}
store.listenersByIdRequestPending = false
for (let key = 0; key < exeNow.length; key++) {
let o = exeNow[key].op
store.tryExecute.call(this, o)
}
for (var sid in ls) {
var l = ls[sid]
var id = JSON.parse(sid)
var op
if (typeof id[1] === 'string') {
op = this.getOperation(id)
} else {
op = this.getInsertion(id)
}
if (op == null) {
store.listenersById[sid] = l
} else {
for (let i = 0; i < l.length; i++) {
let listener = l[i]
let o = listener.op
if (--listener.missing === 0) {
store.tryExecute.call(this, o)
}
}
}
}
})
}
/*
Actually execute an operation, when all expected operations are available.
*/
/* :: // TODO: this belongs somehow to transaction
store: Object;
getOperation: any;
isGarbageCollected: any;
addOperation: any;
whenOperationsExist: any;
*/
tryExecute (op) {
this.store.addToDebug('this.store.tryExecute.call(this, ', JSON.stringify(op), ')')
if (op.struct === 'Delete') {
Y.Struct.Delete.execute.call(this, op)
// this is now called in Transaction.deleteOperation!
// this.store.operationAdded(this, op)
} else {
// check if this op was defined
var defined = this.getInsertion(op.id)
while (defined != null && defined.content != null) {
// check if this op has a longer content in the case it is defined
if (defined.id[1] + defined.content.length < op.id[1] + op.content.length) {
var overlapSize = defined.content.length - (op.id[1] - defined.id[1])
op.content.splice(0, overlapSize)
op.id = [op.id[0], op.id[1] + overlapSize]
op.left = Y.utils.getLastId(defined)
op.origin = op.left
defined = this.getOperation(op.id) // getOperation suffices here
} else {
break
}
}
if (defined == null) {
var opid = op.id
var isGarbageCollected = this.isGarbageCollected(opid)
if (!isGarbageCollected) {
// TODO: reduce number of get / put calls for op ..
Y.Struct[op.struct].execute.call(this, op)
this.addOperation(op)
this.store.operationAdded(this, op)
// operationAdded can change op..
op = this.getOperation(opid)
// if insertion, try to combine with left
this.tryCombineWithLeft(op)
}
}
}
}
/*
* Called by a transaction when an operation is added.
* This function is especially important for y-indexeddb, where several instances may share a single database.
* Every time an operation is created by one instance, it is send to all other instances and operationAdded is called
*
* If it's not a Delete operation:
* * Checks if another operation is executable (listenersById)
* * Update state, if possible
*
* Always:
* * Call type
*/
operationAdded (transaction, op) {
if (op.struct === 'Delete') {
var type = this.initializedTypes[JSON.stringify(op.targetParent)]
if (type != null) {
type._changed(transaction, op)
}
} else {
// increase SS
transaction.updateState(op.id[0])
var opLen = op.content != null ? op.content.length : 1
for (let i = 0; i < opLen; i++) {
// notify whenOperation listeners (by id)
var sid = JSON.stringify([op.id[0], op.id[1] + i])
var l = this.listenersById[sid]
delete this.listenersById[sid]
if (l != null) {
for (var key in l) {
var listener = l[key]
if (--listener.missing === 0) {
this.whenOperationsExist([], listener.op)
}
}
}
}
var t = this.initializedTypes[JSON.stringify(op.parent)]
// if parent is deleted, mark as gc'd and return
if (op.parent != null) {
var parentIsDeleted = transaction.isDeleted(op.parent)
if (parentIsDeleted) {
transaction.deleteList(op.id)
return
}
}
// notify parent, if it was instanciated as a custom type
if (t != null) {
let o = Y.utils.copyOperation(op)
t._changed(transaction, o)
}
if (!op.deleted) {
// Delete if DS says this is actually deleted
var len = op.content != null ? op.content.length : 1
var startId = op.id // You must not use op.id in the following loop, because op will change when deleted
// TODO: !! console.log('TODO: change this before commiting')
for (let i = 0; i < len; i++) {
var id = [startId[0], startId[1] + i]
var opIsDeleted = transaction.isDeleted(id)
if (opIsDeleted) {
var delop = {
struct: 'Delete',
target: id
}
this.tryExecute.call(transaction, delop)
}
}
}
}
}
whenTransactionsFinished () {
if (this.transactionInProgress) {
if (this.transactionsFinished == null) {
var resolve_
var promise = new Promise(function (resolve) {
resolve_ = resolve
})
this.transactionsFinished = {
resolve: resolve_,
promise: promise
}
}
return this.transactionsFinished.promise
} else {
return Promise.resolve()
}
}
// Check if there is another transaction request.
// * the last transaction is always a flush :)
getNextRequest () {
if (this.waitingTransactions.length === 0) {
if (this.transactionIsFlushed) {
this.transactionInProgress = false
this.transactionIsFlushed = false
if (this.transactionsFinished != null) {
this.transactionsFinished.resolve()
this.transactionsFinished = null
}
return null
} else {
this.transactionIsFlushed = true
return function () {
this.flush()
}
}
} else {
this.transactionIsFlushed = false
return this.waitingTransactions.shift()
}
}
requestTransaction (makeGen/* :any */, callImmediately) {
this.waitingTransactions.push(makeGen)
if (!this.transactionInProgress) {
this.transactionInProgress = true
setTimeout(() => {
this.transact(this.getNextRequest())
}, 0)
}
}
/*
Get a created/initialized type.
*/
getType (id) {
return this.initializedTypes[JSON.stringify(id)]
}
/*
Init type. This is called when a remote operation is retrieved, and transformed to a type
TODO: delete type from store.initializedTypes[id] when corresponding id was deleted!
*/
initType (id, args) {
var sid = JSON.stringify(id)
var t = this.store.initializedTypes[sid]
if (t == null) {
var op/* :MapStruct | ListStruct */ = this.getOperation(id)
if (op != null) {
t = Y[op.type].typeDefinition.initType.call(this, this.store, op, args)
this.store.initializedTypes[sid] = t
}
}
return t
}
/*
Create type. This is called when the local user creates a type (which is a synchronous action)
*/
createType (typedefinition, id) {
var structname = typedefinition[0].struct
id = id || this.getNextOpId(1)
var op = Y.Struct[structname].create(id, typedefinition[1])
op.type = typedefinition[0].name
this.requestTransaction(function () {
if (op.id[0] === 0xFFFFFF) {
this.setOperation(op)
} else {
this.applyCreatedOperations([op])
}
})
var t = Y[op.type].typeDefinition.createType(this, op, typedefinition[1])
this.initializedTypes[JSON.stringify(op.id)] = t
return t
}
}
Y.AbstractDatabase = AbstractDatabase
}

152
src/Encoding.js Normal file
View File

@@ -0,0 +1,152 @@
import utf8 from 'utf-8'
const bits7 = 0b1111111
const bits8 = 0b11111111
export class BinaryEncoder {
constructor () {
this.data = []
}
get pos () {
return this.data.length
}
createBuffer () {
return Uint8Array.from(this.data).buffer
}
writeUint8 (num) {
this.data.push(num & bits8)
}
setUint8 (pos, num) {
this.data[pos] = num & bits8
}
writeUint16 (num) {
this.data.push(num & bits8, (num >>> 8) & bits8)
}
setUint16 (pos, num) {
this.data[pos] = num & bits8
this.data[pos + 1] = (num >>> 8) & bits8
}
writeUint32 (num) {
for (let i = 0; i < 4; i++) {
this.data.push(num & bits8)
num >>>= 8
}
}
setUint32 (pos, num) {
for (let i = 0; i < 4; i++) {
this.data[pos + i] = num & bits8
num >>>= 8
}
}
writeVarUint (num) {
while (num >= 0b10000000) {
this.data.push(0b10000000 | (bits7 & num))
num >>>= 7
}
this.data.push(bits7 & num)
}
writeVarString (str) {
let bytes = utf8.setBytesFromString(str)
let len = bytes.length
this.writeVarUint(len)
for (let i = 0; i < len; i++) {
this.data.push(bytes[i])
}
}
writeOpID (id) {
let user = id[0]
this.writeVarUint(user)
if (user !== 0xFFFFFF) {
this.writeVarUint(id[1])
} else {
this.writeVarString(id[1])
}
}
}
export class BinaryDecoder {
constructor (buffer) {
if (buffer instanceof ArrayBuffer) {
this.uint8arr = new Uint8Array(buffer)
} else if (buffer instanceof Uint8Array || (typeof Buffer !== 'undefined' && buffer instanceof Buffer)) {
this.uint8arr = buffer
} else {
throw new Error('Expected an ArrayBuffer or Uint8Array!')
}
this.pos = 0
}
skip8 () {
this.pos++
}
readUint8 () {
return this.uint8arr[this.pos++]
}
readUint32 () {
let uint =
this.uint8arr[this.pos] +
(this.uint8arr[this.pos + 1] << 8) +
(this.uint8arr[this.pos + 2] << 16) +
(this.uint8arr[this.pos + 3] << 24)
this.pos += 4
return uint
}
peekUint8 () {
return this.uint8arr[this.pos]
}
readVarUint () {
let num = 0
let len = 0
while (true) {
let r = this.uint8arr[this.pos++]
num = num | ((r & bits7) << len)
len += 7
if (r < 1 << 7) {
return num >>> 0 // return unsigned number!
}
if (len > 35) {
throw new Error('Integer out of range!')
}
}
}
readVarString () {
let len = this.readVarUint()
let bytes = new Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = this.uint8arr[this.pos++]
}
return utf8.getStringFromBytes(bytes)
}
peekVarString () {
let pos = this.pos
let s = this.readVarString()
this.pos = pos
return s
}
readOpID () {
let user = this.readVarUint()
if (user !== 0xFFFFFF) {
return [user, this.readVarUint()]
} else {
return [user, this.readVarString()]
}
}
}

193
src/MessageHandler.js Normal file
View File

@@ -0,0 +1,193 @@
import Y from './y.js'
import { BinaryDecoder, BinaryEncoder } from './Encoding.js'
export function formatYjsMessage (buffer) {
let decoder = new BinaryDecoder(buffer)
decoder.readVarString() // read roomname
let type = decoder.readVarString()
let strBuilder = []
strBuilder.push('\n === ' + type + ' ===\n')
if (type === 'update') {
logMessageUpdate(decoder, strBuilder)
} else if (type === 'sync step 1') {
logMessageSyncStep1(decoder, strBuilder)
} else if (type === 'sync step 2') {
logMessageSyncStep2(decoder, strBuilder)
} else {
strBuilder.push('-- Unknown message type - probably an encoding issue!!!')
}
return strBuilder.join('')
}
export function formatYjsMessageType (buffer) {
let decoder = new BinaryDecoder(buffer)
decoder.readVarString() // roomname
return decoder.readVarString()
}
export function logMessageUpdate (decoder, strBuilder) {
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
strBuilder.push(JSON.stringify(Y.Struct.binaryDecodeOperation(decoder)) + '\n')
}
}
export function computeMessageUpdate (decoder, encoder, conn) {
if (conn.y.db.forwardAppliedOperations || conn.y.persistence != null) {
let messagePosition = decoder.pos
let len = decoder.readUint32()
let delops = []
for (let i = 0; i < len; i++) {
let op = Y.Struct.binaryDecodeOperation(decoder)
if (op.struct === 'Delete') {
delops.push(op)
}
}
if (delops.length > 0) {
if (conn.y.db.forwardAppliedOperations) {
conn.broadcastOps(delops)
}
if (conn.y.persistence) {
conn.y.persistence.saveOperations(delops)
}
}
decoder.pos = messagePosition
}
conn.y.db.applyOperations(decoder)
}
export function sendSyncStep1 (conn, syncUser) {
conn.y.db.requestTransaction(function () {
let encoder = new BinaryEncoder()
encoder.writeVarString(conn.opts.room || '')
encoder.writeVarString('sync step 1')
encoder.writeVarString(conn.authInfo || '')
encoder.writeVarUint(conn.protocolVersion)
let preferUntransformed = conn.preferUntransformed && this.os.length === 0 // TODO: length may not be defined
encoder.writeUint8(preferUntransformed ? 1 : 0)
this.writeStateSet(encoder)
conn.send(syncUser, encoder.createBuffer())
})
}
export function logMessageSyncStep1 (decoder, strBuilder) {
let auth = decoder.readVarString()
let protocolVersion = decoder.readVarUint()
let preferUntransformed = decoder.readUint8() === 1
strBuilder.push(`
- auth: "${auth}"
- protocolVersion: ${protocolVersion}
- preferUntransformed: ${preferUntransformed}
`)
logSS(decoder, strBuilder)
}
export function computeMessageSyncStep1 (decoder, encoder, conn, senderConn, sender) {
let protocolVersion = decoder.readVarUint()
let preferUntransformed = decoder.readUint8() === 1
// check protocol version
if (protocolVersion !== conn.protocolVersion) {
console.warn(
`You tried to sync with a yjs instance that has a different protocol version
(You: ${protocolVersion}, Client: ${protocolVersion}).
The sync was stopped. You need to upgrade your dependencies (especially Yjs & the Connector)!
`)
conn.y.destroy()
}
return conn.y.db.whenTransactionsFinished().then(() => {
// send sync step 2
conn.y.db.requestTransaction(function () {
encoder.writeVarString('sync step 2')
encoder.writeVarString(conn.authInfo || '')
if (preferUntransformed) {
encoder.writeUint8(1)
this.writeOperationsUntransformed(encoder)
} else {
encoder.writeUint8(0)
this.writeOperations(encoder, decoder)
}
this.writeDeleteSet(encoder)
conn.send(senderConn.uid, encoder.createBuffer())
senderConn.receivedSyncStep2 = true
})
return conn.y.db.whenTransactionsFinished().then(() => {
if (conn.role === 'slave') {
sendSyncStep1(conn, sender)
}
})
})
}
export function logSS (decoder, strBuilder) {
strBuilder.push(' == SS: \n')
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let user = decoder.readVarUint()
let clock = decoder.readVarUint()
strBuilder.push(` ${user}: ${clock}\n`)
}
}
export function logOS (decoder, strBuilder) {
strBuilder.push(' == OS: \n')
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let op = Y.Struct.binaryDecodeOperation(decoder)
strBuilder.push(JSON.stringify(op) + '\n')
}
}
export function logDS (decoder, strBuilder) {
strBuilder.push(' == DS: \n')
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let user = decoder.readVarUint()
strBuilder.push(` User: ${user}: `)
let len2 = decoder.readVarUint()
for (let j = 0; j < len2; j++) {
let from = decoder.readVarUint()
let to = decoder.readVarUint()
let gc = decoder.readUint8() === 1
strBuilder.push(`[${from}, ${to}, ${gc}]`)
}
}
}
export function logMessageSyncStep2 (decoder, strBuilder) {
strBuilder.push(' - auth: ' + decoder.readVarString() + '\n')
let osTransformed = decoder.readUint8() === 1
strBuilder.push(' - osUntransformed: ' + osTransformed + '\n')
logOS(decoder, strBuilder)
if (osTransformed) {
logSS(decoder, strBuilder)
}
logDS(decoder, strBuilder)
}
export function computeMessageSyncStep2 (decoder, encoder, conn, senderConn, sender) {
var db = conn.y.db
let defer = senderConn.syncStep2
// apply operations first
db.requestTransaction(function () {
let osUntransformed = decoder.readUint8()
if (osUntransformed === 1) {
this.applyOperationsUntransformed(decoder)
} else {
this.store.applyOperations(decoder)
}
})
// then apply ds
db.requestTransaction(function () {
this.applyDeleteSet(decoder)
})
return db.whenTransactionsFinished().then(() => {
conn._setSyncedWith(sender)
defer.resolve()
})
}

View File

@@ -1,32 +0,0 @@
import { writeStructs } from './syncStep1.js'
import { integrateRemoteStructs } from './integrateRemoteStructs.js'
import { readDeleteSet, writeDeleteSet } from './deleteSet.js'
import BinaryEncoder from '../Util/Binary/Encoder.js'
/**
* Read the Decoder and fill the Yjs instance with data in the decoder.
*
* @param {Y} y The Yjs instance
* @param {BinaryDecoder} decoder The BinaryDecoder to read from.
*/
export function fromBinary (y, decoder) {
y.transact(function () {
integrateRemoteStructs(y, decoder)
readDeleteSet(y, decoder)
})
}
/**
* Encode the Yjs model to binary format.
*
* @param {Y} y The Yjs instance
* @return {BinaryEncoder} The encoder instance that can be transformed
* to ArrayBuffer or Buffer.
*/
export function toBinary (y) {
let encoder = new BinaryEncoder()
writeStructs(y, encoder, new Map())
writeDeleteSet(y, encoder)
return encoder
}

View File

@@ -1,130 +0,0 @@
import { deleteItemRange } from '../Struct/Delete.js'
import ID from '../Util/ID/ID.js'
export function stringifyDeleteSet (y, decoder, strBuilder) {
let dsLength = decoder.readUint32()
for (let i = 0; i < dsLength; i++) {
let user = decoder.readVarUint()
strBuilder.push(' -' + user + ':')
let dvLength = decoder.readVarUint()
for (let j = 0; j < dvLength; j++) {
let from = decoder.readVarUint()
let len = decoder.readVarUint()
let gc = decoder.readUint8() === 1
strBuilder.push(`clock: ${from}, length: ${len}, gc: ${gc}`)
}
}
return strBuilder
}
export function writeDeleteSet (y, encoder) {
let currentUser = null
let currentLength
let lastLenPos
let numberOfUsers = 0
let laterDSLenPus = encoder.pos
encoder.writeUint32(0)
y.ds.iterate(null, null, function (n) {
var user = n._id.user
var clock = n._id.clock
var len = n.len
var gc = n.gc
if (currentUser !== user) {
numberOfUsers++
// a new user was found
if (currentUser !== null) { // happens on first iteration
encoder.setUint32(lastLenPos, currentLength)
}
currentUser = user
encoder.writeVarUint(user)
// pseudo-fill pos
lastLenPos = encoder.pos
encoder.writeUint32(0)
currentLength = 0
}
encoder.writeVarUint(clock)
encoder.writeVarUint(len)
encoder.writeUint8(gc ? 1 : 0)
currentLength++
})
if (currentUser !== null) { // happens on first iteration
encoder.setUint32(lastLenPos, currentLength)
}
encoder.setUint32(laterDSLenPus, numberOfUsers)
}
export function readDeleteSet (y, decoder) {
let dsLength = decoder.readUint32()
for (let i = 0; i < dsLength; i++) {
let user = decoder.readVarUint()
let dv = []
let dvLength = decoder.readUint32()
for (let j = 0; j < dvLength; j++) {
let from = decoder.readVarUint()
let len = decoder.readVarUint()
let gc = decoder.readUint8() === 1
dv.push([from, len, gc])
}
if (dvLength > 0) {
let pos = 0
let d = dv[pos]
let deletions = []
y.ds.iterate(new ID(user, 0), new ID(user, Number.MAX_VALUE), function (n) {
// cases:
// 1. d deletes something to the right of n
// => go to next n (break)
// 2. d deletes something to the left of n
// => create deletions
// => reset d accordingly
// *)=> if d doesn't delete anything anymore, go to next d (continue)
// 3. not 2) and d deletes something that also n deletes
// => reset d so that it doesn't contain n's deletion
// *)=> if d does not delete anything anymore, go to next d (continue)
while (d != null) {
var diff = 0 // describe the diff of length in 1) and 2)
if (n._id.clock + n.len <= d[0]) {
// 1)
break
} else if (d[0] < n._id.clock) {
// 2)
// delete maximum the len of d
// else delete as much as possible
diff = Math.min(n._id.clock - d[0], d[1])
// deleteItemRange(y, user, d[0], diff, true)
deletions.push([user, d[0], diff])
} else {
// 3)
diff = n._id.clock + n.len - d[0] // never null (see 1)
if (d[2] && !n.gc) {
// d marks as gc'd but n does not
// then delete either way
// deleteItemRange(y, user, d[0], Math.min(diff, d[1]), true)
deletions.push([user, d[0], Math.min(diff, d[1])])
}
}
if (d[1] <= diff) {
// d doesn't delete anything anymore
d = dv[++pos]
} else {
d[0] = d[0] + diff // reset pos
d[1] = d[1] - diff // reset length
}
}
})
// TODO: It would be more performant to apply the deletes in the above loop
// Adapt the Tree implementation to support delete while iterating
for (let i = deletions.length - 1; i >= 0; i--) {
const del = deletions[i]
deleteItemRange(y, del[0], del[1], del[2], true)
}
// for the rest.. just apply it
for (; pos < dv.length; pos++) {
d = dv[pos]
deleteItemRange(y, user, d[0], d[1], true)
// deletions.push([user, d[0], d[1], d[2]])
}
}
}
}

View File

@@ -1,109 +0,0 @@
import { getStruct } from '../Util/structReferences.js'
import BinaryDecoder from '../Util/Binary/Decoder.js'
import { logID } from './messageToString.js'
import GC from '../Struct/GC.js'
class MissingEntry {
constructor (decoder, missing, struct) {
this.decoder = decoder
this.missing = missing.length
this.struct = struct
}
}
/**
* @private
* Integrate remote struct
* When a remote struct is integrated, other structs might be ready to ready to
* integrate.
*/
function _integrateRemoteStructHelper (y, struct) {
const id = struct._id
if (id === undefined) {
struct._integrate(y)
} else {
if (y.ss.getState(id.user) > id.clock) {
return
}
if (struct.constructor === GC || (struct._parent.constructor !== GC && struct._parent._deleted === false)) {
// Is either a GC or Item with an undeleted parent
// save to integrate
struct._integrate(y)
} else {
// Is an Item. parent was deleted.
struct._gc(y)
}
let msu = y._missingStructs.get(id.user)
if (msu != null) {
let clock = id.clock
const finalClock = clock + struct._length
for (;clock < finalClock; clock++) {
const missingStructs = msu.get(clock)
if (missingStructs !== undefined) {
missingStructs.forEach(missingDef => {
missingDef.missing--
if (missingDef.missing === 0) {
const decoder = missingDef.decoder
let oldPos = decoder.pos
let missing = missingDef.struct._fromBinary(y, decoder)
decoder.pos = oldPos
if (missing.length === 0) {
y._readyToIntegrate.push(missingDef.struct)
}
}
})
msu.delete(clock)
}
}
}
}
}
export function stringifyStructs (y, decoder, strBuilder) {
const len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let reference = decoder.readVarUint()
let Constr = getStruct(reference)
let struct = new Constr()
let missing = struct._fromBinary(y, decoder)
let logMessage = ' ' + struct._logString()
if (missing.length > 0) {
logMessage += ' .. missing: ' + missing.map(logID).join(', ')
}
strBuilder.push(logMessage)
}
}
export function integrateRemoteStructs (y, decoder) {
const len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let reference = decoder.readVarUint()
let Constr = getStruct(reference)
let struct = new Constr()
let decoderPos = decoder.pos
let missing = struct._fromBinary(y, decoder)
if (missing.length === 0) {
while (struct != null) {
_integrateRemoteStructHelper(y, struct)
struct = y._readyToIntegrate.shift()
}
} else {
let _decoder = new BinaryDecoder(decoder.uint8arr)
_decoder.pos = decoderPos
let missingEntry = new MissingEntry(_decoder, missing, struct)
let missingStructs = y._missingStructs
for (let i = missing.length - 1; i >= 0; i--) {
let m = missing[i]
if (!missingStructs.has(m.user)) {
missingStructs.set(m.user, new Map())
}
let msu = missingStructs.get(m.user)
if (!msu.has(m.clock)) {
msu.set(m.clock, [])
}
let mArray = msu = msu.get(m.clock)
mArray.push(missingEntry)
}
}
}
}

View File

@@ -1,65 +0,0 @@
import BinaryDecoder from '../Util/Binary/Decoder.js'
import { stringifyStructs } from './integrateRemoteStructs.js'
import { stringifySyncStep1 } from './syncStep1.js'
import { stringifySyncStep2 } from './syncStep2.js'
import ID from '../Util/ID/ID.js'
import RootID from '../Util/ID/RootID.js'
import Y from '../Y.js'
export function messageToString ([y, buffer]) {
let decoder = new BinaryDecoder(buffer)
decoder.readVarString() // read roomname
let type = decoder.readVarString()
let strBuilder = []
strBuilder.push('\n === ' + type + ' ===')
if (type === 'update') {
stringifyStructs(y, decoder, strBuilder)
} else if (type === 'sync step 1') {
stringifySyncStep1(y, decoder, strBuilder)
} else if (type === 'sync step 2') {
stringifySyncStep2(y, decoder, strBuilder)
} else {
strBuilder.push('-- Unknown message type - probably an encoding issue!!!')
}
return strBuilder.join('\n')
}
export function messageToRoomname (buffer) {
let decoder = new BinaryDecoder(buffer)
decoder.readVarString() // roomname
return decoder.readVarString() // messageType
}
export function logID (id) {
if (id !== null && id._id != null) {
id = id._id
}
if (id === null) {
return '()'
} else if (id instanceof ID) {
return `(${id.user},${id.clock})`
} else if (id instanceof RootID) {
return `(${id.name},${id.type})`
} else if (id.constructor === Y) {
return `y`
} else {
throw new Error('This is not a valid ID!')
}
}
/**
* Helper utility to convert an item to a readable format.
*
* @param {String} name The name of the item class (YText, ItemString, ..).
* @param {Item} item The item instance.
* @param {String} [append] Additional information to append to the returned
* string.
* @return {String} A readable string that represents the item object.
*
* @private
*/
export function logItemHelper (name, item, append) {
const left = item._left !== null ? item._left._lastId : null
const origin = item._origin !== null ? item._origin._lastId : null
return `${name}(id:${logID(item._id)},start:${logID(item._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(item._right)},parent:${logID(item._parent)},parentSub:${item._parentSub}${append !== undefined ? ' - ' + append : ''})`
}

View File

@@ -1,23 +0,0 @@
export function readStateSet (decoder) {
let ss = new Map()
let ssLength = decoder.readUint32()
for (let i = 0; i < ssLength; i++) {
let user = decoder.readVarUint()
let clock = decoder.readVarUint()
ss.set(user, clock)
}
return ss
}
export function writeStateSet (y, encoder) {
let lenPosition = encoder.pos
let len = 0
encoder.writeUint32(0)
for (let [user, clock] of y.ss.state) {
encoder.writeVarUint(user)
encoder.writeVarUint(clock)
len++
}
encoder.setUint32(lenPosition, len)
}

View File

@@ -1,83 +0,0 @@
import BinaryEncoder from '../Util/Binary/Encoder.js'
import { readStateSet, writeStateSet } from './stateSet.js'
import { writeDeleteSet } from './deleteSet.js'
import ID from '../Util/ID/ID.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
export function stringifySyncStep1 (y, decoder, strBuilder) {
let auth = decoder.readVarString()
let protocolVersion = decoder.readVarUint()
strBuilder.push(` - auth: "${auth}"`)
strBuilder.push(` - protocolVersion: ${protocolVersion}`)
// write SS
let ssBuilder = []
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let user = decoder.readVarUint()
let clock = decoder.readVarUint()
ssBuilder.push(`(${user}:${clock})`)
}
strBuilder.push(' == SS: ' + ssBuilder.join(','))
}
export function sendSyncStep1 (connector, syncUser) {
let encoder = new BinaryEncoder()
encoder.writeVarString(connector.y.room)
encoder.writeVarString('sync step 1')
encoder.writeVarString(connector.authInfo || '')
encoder.writeVarUint(connector.protocolVersion)
writeStateSet(connector.y, encoder)
connector.send(syncUser, encoder.createBuffer())
}
/**
* @private
* Write all Items that are not not included in ss to
* the encoder object.
*/
export function writeStructs (y, encoder, ss) {
const lenPos = encoder.pos
encoder.writeUint32(0)
let len = 0
for (let user of y.ss.state.keys()) {
let clock = ss.get(user) || 0
if (user !== RootFakeUserID) {
const minBound = new ID(user, clock)
const overlappingLeft = y.os.findPrev(minBound)
const rightID = overlappingLeft === null ? null : overlappingLeft._id
if (rightID !== null && rightID.user === user && rightID.clock + overlappingLeft._length > clock) {
const struct = overlappingLeft._clonePartial(clock - rightID.clock)
struct._toBinary(encoder)
len++
}
y.os.iterate(minBound, new ID(user, Number.MAX_VALUE), function (struct) {
struct._toBinary(encoder)
len++
})
}
}
encoder.setUint32(lenPos, len)
}
export function readSyncStep1 (decoder, encoder, y, senderConn, sender) {
let protocolVersion = decoder.readVarUint()
// check protocol version
if (protocolVersion !== y.connector.protocolVersion) {
console.warn(
`You tried to sync with a Yjs instance that has a different protocol version
(You: ${protocolVersion}, Client: ${protocolVersion}).
`)
y.destroy()
}
// write sync step 2
encoder.writeVarString('sync step 2')
encoder.writeVarString(y.connector.authInfo || '')
const ss = readStateSet(decoder)
writeStructs(y, encoder, ss)
writeDeleteSet(y, encoder)
y.connector.send(senderConn.uid, encoder.createBuffer())
senderConn.receivedSyncStep2 = true
if (y.connector.role === 'slave') {
sendSyncStep1(y.connector, sender)
}
}

View File

@@ -1,28 +0,0 @@
import { stringifyStructs, integrateRemoteStructs } from './integrateRemoteStructs.js'
import { readDeleteSet } from './deleteSet.js'
export function stringifySyncStep2 (y, decoder, strBuilder) {
strBuilder.push(' - auth: ' + decoder.readVarString())
strBuilder.push(' == OS:')
stringifyStructs(y, decoder, strBuilder)
// write DS to string
strBuilder.push(' == DS:')
let len = decoder.readUint32()
for (let i = 0; i < len; i++) {
let user = decoder.readVarUint()
strBuilder.push(` User: ${user}: `)
let len2 = decoder.readUint32()
for (let j = 0; j < len2; j++) {
let from = decoder.readVarUint()
let to = decoder.readVarUint()
let gc = decoder.readUint8() === 1
strBuilder.push(`[${from}, ${to}, ${gc}]`)
}
}
}
export function readSyncStep2 (decoder, encoder, y, senderConn, sender) {
integrateRemoteStructs(y, decoder)
readDeleteSet(y, decoder)
y.connector._setSyncedWith(sender)
}

12
src/Notes.md Normal file
View File

@@ -0,0 +1,12 @@
# Notes
### Terminology
* DB: DataBase that holds all the information of the shared object. It is devided into the OS, DS, and SS. This can be a persistent database or an in-memory database. Depending on the type of database, it could make sense to store OS, DS, and SS in different tables, or maybe different databases.
* OS: OperationStore holds all the operations. An operation is a js object with a fixed number of name fields.
* DS: DeleteStore holds the information about which operations are deleted and which operations were garbage collected (no longer available in the OS).
* SS: StateSet holds the current state of the OS. SS.getState(username) refers to the amount of operations that were received by that respective user.
* Op: Operation defines an action on a shared type. But it is also the format in which we store the model of a type. This is why it is also called a Struct/Structure.
* Type and Structure: We crearly distinguish between type and structure. Short explanation: A type (e.g. Strings, Numbers) have a number of functions that you can apply on them. (+) is well defined on both of them. They are *modeled* by a structure - the functions really change the structure of a type. Types can be implemented differently but still provide the same functionality. In Yjs, almost all types are realized as a doubly linked list (on which Yjs can provide eventual convergence)
*

View File

@@ -1,122 +1,46 @@
import BinaryEncoder from './Util/Binary/Encoder.js'
import BinaryDecoder from './Util/Binary/Decoder.js'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import { createMutualExclude } from './Util/mutualExclude.js'
import { BinaryEncoder } from './Encoding.js'
function getFreshCnf () {
let buffer = new BinaryEncoder()
buffer.writeUint32(0)
return {
len: 0,
buffer
}
}
/**
* Abstract persistence class.
*/
export default class AbstractPersistence {
constructor (opts) {
this.opts = opts
this.ys = new Map()
}
_init (y) {
let cnf = this.ys.get(y)
if (cnf === undefined) {
cnf = getFreshCnf()
cnf.mutualExclude = createMutualExclude()
this.ys.set(y, cnf)
return this.init(y).then(() => {
y.on('afterTransaction', (y, transaction) => {
let cnf = this.ys.get(y)
if (cnf.len > 0) {
cnf.buffer.setUint32(0, cnf.len)
this.saveUpdate(y, cnf.buffer.createBuffer(), transaction)
let _cnf = getFreshCnf()
for (let key in _cnf) {
cnf[key] = _cnf[key]
}
}
})
return this.retrieve(y)
}).then(function () {
return Promise.resolve(cnf)
})
} else {
return Promise.resolve(cnf)
export default function extendPersistence (Y) {
class AbstractPersistence {
constructor (y, opts) {
this.y = y
this.opts = opts
this.saveOperationsBuffer = []
this.log = Y.debug('y:persistence')
}
}
deinit (y) {
this.ys.delete(y)
y.persistence = null
}
destroy () {
this.ys = null
}
saveToMessageQueue (binary) {
this.log('Room %s: Save message to message queue', this.y.options.connector.room)
}
/**
* Remove all persisted data that belongs to a room.
* Automatically destroys all Yjs all Yjs instances that persist to
* the room. If `destroyYjsInstances = false` the persistence functionality
* will be removed from the Yjs instances.
*
* ** Must be overwritten! **
*/
removePersistedData (room, destroyYjsInstances = true) {
this.ys.forEach((cnf, y) => {
if (y.room === room) {
if (destroyYjsInstances) {
y.destroy()
} else {
this.deinit(y)
saveOperations (ops) {
ops = ops.map(function (op) {
return Y.Struct[op.struct].encode(op)
})
const saveOperations = () => {
if (this.saveOperationsBuffer.length > 0) {
let encoder = new BinaryEncoder()
encoder.writeVarString(this.opts.room)
encoder.writeVarString('update')
let ops = this.saveOperationsBuffer
this.saveOperationsBuffer = []
let length = ops.length
encoder.writeUint32(length)
for (var i = 0; i < length; i++) {
let op = ops[i]
Y.Struct[op.struct].binaryEncode(encoder, op)
}
this.saveToMessageQueue(encoder.createBuffer())
}
}
})
}
/* overwrite */
saveUpdate (buffer) {
}
/**
* Save struct to update buffer.
* saveUpdate is called when transaction ends
*/
saveStruct (y, struct) {
let cnf = this.ys.get(y)
if (cnf !== undefined) {
cnf.mutualExclude(function () {
struct._toBinary(cnf.buffer)
cnf.len++
})
if (this.saveOperationsBuffer.length === 0) {
this.saveOperationsBuffer = ops
this.y.db.whenTransactionsFinished().then(saveOperations)
} else {
this.saveOperationsBuffer = this.saveOperationsBuffer.concat(ops)
}
}
}
/* overwrite */
retrieve (y, model, updates) {
let cnf = this.ys.get(y)
if (cnf !== undefined) {
cnf.mutualExclude(function () {
y.transact(function () {
if (model != null) {
fromBinary(y, new BinaryDecoder(new Uint8Array(model)))
}
if (updates != null) {
for (let i = 0; i < updates.length; i++) {
integrateRemoteStructs(y, new BinaryDecoder(new Uint8Array(updates[i])))
}
}
})
y.emit('persistenceReady')
})
}
}
/* overwrite */
persist (y) {
return toBinary(y).createBuffer()
}
Y.AbstractPersistence = AbstractPersistence
}

506
src/RedBlackTree.js Normal file
View File

@@ -0,0 +1,506 @@
export default function extendRBTree (Y) {
class N {
// A created node is always red!
constructor (val) {
this.val = val
this.color = true
this._left = null
this._right = null
this._parent = null
if (val.id === null) {
throw new Error('You must define id!')
}
}
isRed () { return this.color }
isBlack () { return !this.color }
redden () { this.color = true; return this }
blacken () { this.color = false; return this }
get grandparent () {
return this.parent.parent
}
get parent () {
return this._parent
}
get sibling () {
return (this === this.parent.left)
? this.parent.right : this.parent.left
}
get left () {
return this._left
}
get right () {
return this._right
}
set left (n) {
if (n !== null) {
n._parent = this
}
this._left = n
}
set right (n) {
if (n !== null) {
n._parent = this
}
this._right = n
}
rotateLeft (tree) {
var parent = this.parent
var newParent = this.right
var newRight = this.right.left
newParent.left = this
this.right = newRight
if (parent === null) {
tree.root = newParent
newParent._parent = null
} else if (parent.left === this) {
parent.left = newParent
} else if (parent.right === this) {
parent.right = newParent
} else {
throw new Error('The elements are wrongly connected!')
}
}
next () {
if (this.right !== null) {
// search the most left node in the right tree
var o = this.right
while (o.left !== null) {
o = o.left
}
return o
} else {
var p = this
while (p.parent !== null && p !== p.parent.left) {
p = p.parent
}
return p.parent
}
}
prev () {
if (this.left !== null) {
// search the most right node in the left tree
var o = this.left
while (o.right !== null) {
o = o.right
}
return o
} else {
var p = this
while (p.parent !== null && p !== p.parent.right) {
p = p.parent
}
return p.parent
}
}
rotateRight (tree) {
var parent = this.parent
var newParent = this.left
var newLeft = this.left.right
newParent.right = this
this.left = newLeft
if (parent === null) {
tree.root = newParent
newParent._parent = null
} else if (parent.left === this) {
parent.left = newParent
} else if (parent.right === this) {
parent.right = newParent
} else {
throw new Error('The elements are wrongly connected!')
}
}
getUncle () {
// we can assume that grandparent exists when this is called!
if (this.parent === this.parent.parent.left) {
return this.parent.parent.right
} else {
return this.parent.parent.left
}
}
}
class RBTree {
constructor () {
this.root = null
this.length = 0
}
findNext (id) {
return this.findWithLowerBound([id[0], id[1] + 1])
}
findPrev (id) {
return this.findWithUpperBound([id[0], id[1] - 1])
}
findNodeWithLowerBound (from) {
if (from === void 0) {
throw new Error('You must define from!')
}
var o = this.root
if (o === null) {
return null
} else {
while (true) {
if ((from === null || Y.utils.smaller(from, o.val.id)) && o.left !== null) {
// o is included in the bound
// try to find an element that is closer to the bound
o = o.left
} else if (from !== null && Y.utils.smaller(o.val.id, from)) {
// o is not within the bound, maybe one of the right elements is..
if (o.right !== null) {
o = o.right
} else {
// there is no right element. Search for the next bigger element,
// this should be within the bounds
return o.next()
}
} else {
return o
}
}
}
}
findNodeWithUpperBound (to) {
if (to === void 0) {
throw new Error('You must define from!')
}
var o = this.root
if (o === null) {
return null
} else {
while (true) {
if ((to === null || Y.utils.smaller(o.val.id, to)) && o.right !== null) {
// o is included in the bound
// try to find an element that is closer to the bound
o = o.right
} else if (to !== null && Y.utils.smaller(to, o.val.id)) {
// o is not within the bound, maybe one of the left elements is..
if (o.left !== null) {
o = o.left
} else {
// there is no left element. Search for the prev smaller element,
// this should be within the bounds
return o.prev()
}
} else {
return o
}
}
}
}
findSmallestNode () {
var o = this.root
while (o != null && o.left != null) {
o = o.left
}
return o
}
findWithLowerBound (from) {
var n = this.findNodeWithLowerBound(from)
return n == null ? null : n.val
}
findWithUpperBound (to) {
var n = this.findNodeWithUpperBound(to)
return n == null ? null : n.val
}
iterate (t, from, to, f) {
var o
if (from === null) {
o = this.findSmallestNode()
} else {
o = this.findNodeWithLowerBound(from)
}
while (
o !== null &&
(
to === null || // eslint-disable-line no-unmodified-loop-condition
Y.utils.smaller(o.val.id, to) ||
Y.utils.compareIds(o.val.id, to)
)
) {
f.call(t, o.val)
o = o.next()
}
return true
}
logTable (from, to, filter) {
if (filter == null) {
filter = function () {
return true
}
}
if (from == null) { from = null }
if (to == null) { to = null }
var os = []
this.iterate(this, from, to, function (o) {
if (filter(o)) {
var o_ = {}
for (var key in o) {
if (typeof o[key] === 'object') {
o_[key] = JSON.stringify(o[key])
} else {
o_[key] = o[key]
}
}
os.push(o_)
}
})
if (console.table != null) {
console.table(os)
}
}
find (id) {
var n
return (n = this.findNode(id)) ? n.val : null
}
findNode (id) {
if (id == null || id.constructor !== Array) {
throw new Error('Expect id to be an array!')
}
var o = this.root
if (o === null) {
return false
} else {
while (true) {
if (o === null) {
return false
}
if (Y.utils.smaller(id, o.val.id)) {
o = o.left
} else if (Y.utils.smaller(o.val.id, id)) {
o = o.right
} else {
return o
}
}
}
}
delete (id) {
if (id == null || id.constructor !== Array) {
throw new Error('id is expected to be an Array!')
}
var d = this.findNode(id)
if (d == null) {
// throw new Error('Element does not exist!')
return
}
this.length--
if (d.left !== null && d.right !== null) {
// switch d with the greates element in the left subtree.
// o should have at most one child.
var o = d.left
// find
while (o.right !== null) {
o = o.right
}
// switch
d.val = o.val
d = o
}
// d has at most one child
// let n be the node that replaces d
var isFakeChild
var child = d.left || d.right
if (child === null) {
isFakeChild = true
child = new N({id: 0})
child.blacken()
d.right = child
} else {
isFakeChild = false
}
if (d.parent === null) {
if (!isFakeChild) {
this.root = child
child.blacken()
child._parent = null
} else {
this.root = null
}
return
} else if (d.parent.left === d) {
d.parent.left = child
} else if (d.parent.right === d) {
d.parent.right = child
} else {
throw new Error('Impossible!')
}
if (d.isBlack()) {
if (child.isRed()) {
child.blacken()
} else {
this._fixDelete(child)
}
}
this.root.blacken()
if (isFakeChild) {
if (child.parent.left === child) {
child.parent.left = null
} else if (child.parent.right === child) {
child.parent.right = null
} else {
throw new Error('Impossible #3')
}
}
}
_fixDelete (n) {
function isBlack (node) {
return node !== null ? node.isBlack() : true
}
function isRed (node) {
return node !== null ? node.isRed() : false
}
if (n.parent === null) {
// this can only be called after the first iteration of fixDelete.
return
}
// d was already replaced by the child
// d is not the root
// d and child are black
var sibling = n.sibling
if (isRed(sibling)) {
// make sibling the grandfather
n.parent.redden()
sibling.blacken()
if (n === n.parent.left) {
n.parent.rotateLeft(this)
} else if (n === n.parent.right) {
n.parent.rotateRight(this)
} else {
throw new Error('Impossible #2')
}
sibling = n.sibling
}
// parent, sibling, and children of n are black
if (n.parent.isBlack() &&
sibling.isBlack() &&
isBlack(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
this._fixDelete(n.parent)
} else if (n.parent.isRed() &&
sibling.isBlack() &&
isBlack(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
n.parent.blacken()
} else {
if (n === n.parent.left &&
sibling.isBlack() &&
isRed(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
sibling.left.blacken()
sibling.rotateRight(this)
sibling = n.sibling
} else if (n === n.parent.right &&
sibling.isBlack() &&
isRed(sibling.right) &&
isBlack(sibling.left)
) {
sibling.redden()
sibling.right.blacken()
sibling.rotateLeft(this)
sibling = n.sibling
}
sibling.color = n.parent.color
n.parent.blacken()
if (n === n.parent.left) {
sibling.right.blacken()
n.parent.rotateLeft(this)
} else {
sibling.left.blacken()
n.parent.rotateRight(this)
}
}
}
put (v) {
if (v == null || v.id == null || v.id.constructor !== Array) {
throw new Error('v is expected to have an id property which is an Array!')
}
var node = new N(v)
if (this.root !== null) {
var p = this.root // p abbrev. parent
while (true) {
if (Y.utils.smaller(node.val.id, p.val.id)) {
if (p.left === null) {
p.left = node
break
} else {
p = p.left
}
} else if (Y.utils.smaller(p.val.id, node.val.id)) {
if (p.right === null) {
p.right = node
break
} else {
p = p.right
}
} else {
p.val = node.val
return p
}
}
this._fixInsert(node)
} else {
this.root = node
}
this.length++
this.root.blacken()
return node
}
_fixInsert (n) {
if (n.parent === null) {
n.blacken()
return
} else if (n.parent.isBlack()) {
return
}
var uncle = n.getUncle()
if (uncle !== null && uncle.isRed()) {
// Note: parent: red, uncle: red
n.parent.blacken()
uncle.blacken()
n.grandparent.redden()
this._fixInsert(n.grandparent)
} else {
// Note: parent: red, uncle: black or null
// Now we transform the tree in such a way that
// either of these holds:
// 1) grandparent.left.isRed
// and grandparent.left.left.isRed
// 2) grandparent.right.isRed
// and grandparent.right.right.isRed
if (n === n.parent.right && n.parent === n.grandparent.left) {
n.parent.rotateLeft(this)
// Since we rotated and want to use the previous
// cases, we need to set n in such a way that
// n.parent.isRed again
n = n.left
} else if (n === n.parent.left && n.parent === n.grandparent.right) {
n.parent.rotateRight(this)
// see above
n = n.right
}
// Case 1) or 2) hold from here on.
// Now traverse grandparent, make parent a black node
// on the highest level which holds two red nodes.
n.parent.blacken()
n.grandparent.redden()
if (n === n.parent.left) {
// Case 1
n.grandparent.rotateRight(this)
} else {
// Case 2
n.grandparent.rotateLeft(this)
}
}
}
flush () {}
}
Y.utils.RBTree = RBTree
}

View File

@@ -1,90 +0,0 @@
import Tree from '../Util/Tree.js'
import ID from '../Util/ID/ID.js'
class DSNode {
constructor (id, len, gc) {
this._id = id
this.len = len
this.gc = gc
}
clone () {
return new DSNode(this._id, this.len, this.gc)
}
}
export default class DeleteStore extends Tree {
logTable () {
const deletes = []
this.iterate(null, null, function (n) {
deletes.push({
user: n._id.user,
clock: n._id.clock,
len: n.len,
gc: n.gc
})
})
console.table(deletes)
}
isDeleted (id) {
var n = this.findWithUpperBound(id)
return n !== null && n._id.user === id.user && id.clock < n._id.clock + n.len
}
mark (id, length, gc) {
if (length === 0) return
// Step 1. Unmark range
const leftD = this.findWithUpperBound(new ID(id.user, id.clock - 1))
// Resize left DSNode if necessary
if (leftD !== null && leftD._id.user === id.user) {
if (leftD._id.clock < id.clock && id.clock < leftD._id.clock + leftD.len) {
// node is overlapping. need to resize
if (id.clock + length < leftD._id.clock + leftD.len) {
// overlaps new mark range and some more
// create another DSNode to the right of new mark
this.put(new DSNode(new ID(id.user, id.clock + length), leftD._id.clock + leftD.len - id.clock - length, leftD.gc))
}
// resize left DSNode
leftD.len = id.clock - leftD._id.clock
} // Otherwise there is no overlapping
}
// Resize right DSNode if necessary
const upper = new ID(id.user, id.clock + length - 1)
const rightD = this.findWithUpperBound(upper)
if (rightD !== null && rightD._id.user === id.user) {
if (rightD._id.clock < id.clock + length && id.clock <= rightD._id.clock && id.clock + length < rightD._id.clock + rightD.len) { // we only consider the case where we resize the node
const d = id.clock + length - rightD._id.clock
rightD._id = new ID(rightD._id.user, rightD._id.clock + d)
rightD.len -= d
}
}
// Now we only have to delete all inner marks
const deleteNodeIds = []
this.iterate(id, upper, m => {
deleteNodeIds.push(m._id)
})
for (let i = deleteNodeIds.length - 1; i >= 0; i--) {
this.delete(deleteNodeIds[i])
}
let newMark = new DSNode(id, length, gc)
// Step 2. Check if we can extend left or right
if (leftD !== null && leftD._id.user === id.user && leftD._id.clock + leftD.len === id.clock && leftD.gc === gc) {
// We can extend left
leftD.len += length
newMark = leftD
}
const rightNext = this.find(new ID(id.user, id.clock + length))
if (rightNext !== null && rightNext._id.user === id.user && id.clock + length === rightNext._id.clock && gc === rightNext.gc) {
// We can merge newMark and rightNext
newMark.len += rightNext.len
this.delete(rightNext._id)
}
if (leftD !== newMark) {
// only put if we didn't extend left
this.put(newMark)
}
}
// TODO: exchange markDeleted for mark()
markDeleted (id, length) {
this.mark(id, length, false)
}
}

View File

@@ -1,94 +0,0 @@
import Tree from '../Util/Tree.js'
import RootID from '../Util/ID/RootID.js'
import { getStruct } from '../Util/structReferences.js'
import { logID } from '../MessageHandler/messageToString.js'
import GC from '../Struct/GC.js'
export default class OperationStore extends Tree {
constructor (y) {
super()
this.y = y
}
logTable () {
const items = []
this.iterate(null, null, function (item) {
if (item.constructor === GC) {
items.push({
id: logID(item),
content: item._length,
deleted: 'GC'
})
} else {
items.push({
id: logID(item),
origin: logID(item._origin === null ? null : item._origin._lastId),
left: logID(item._left === null ? null : item._left._lastId),
right: logID(item._right),
right_origin: logID(item._right_origin),
parent: logID(item._parent),
parentSub: item._parentSub,
deleted: item._deleted,
content: JSON.stringify(item._content)
})
}
})
console.table(items)
}
get (id) {
let struct = this.find(id)
if (struct === null && id instanceof RootID) {
const Constr = getStruct(id.type)
const y = this.y
struct = new Constr()
struct._id = id
struct._parent = y
y.transact(() => {
struct._integrate(y)
})
this.put(struct)
}
return struct
}
// Use getItem for structs with _length > 1
getItem (id) {
var item = this.findWithUpperBound(id)
if (item === null) {
return null
}
const itemID = item._id
if (id.user === itemID.user && id.clock < itemID.clock + item._length) {
return item
} else {
return null
}
}
// Return an insertion such that id is the first element of content
// This function manipulates an item, if necessary
getItemCleanStart (id) {
var ins = this.getItem(id)
if (ins === null || ins._length === 1) {
return ins
}
const insID = ins._id
if (insID.clock === id.clock) {
return ins
} else {
return ins._splitAt(this.y, id.clock - insID.clock)
}
}
// Return an insertion such that id is the last element of content
// This function manipulates an operation, if necessary
getItemCleanEnd (id) {
var ins = this.getItem(id)
if (ins === null || ins._length === 1) {
return ins
}
const insID = ins._id
if (insID.clock + ins._length - 1 === id.clock) {
return ins
} else {
ins._splitAt(this.y, id.clock - insID.clock + 1)
return ins
}
}
}

View File

@@ -1,47 +0,0 @@
import ID from '../Util/ID/ID.js'
export default class StateStore {
constructor (y) {
this.y = y
this.state = new Map()
}
logTable () {
const entries = []
for (let [user, state] of this.state) {
entries.push({
user, state
})
}
console.table(entries)
}
getNextID (len) {
const user = this.y.userID
const state = this.getState(user)
this.setState(user, state + len)
return new ID(user, state)
}
updateRemoteState (struct) {
let user = struct._id.user
let userState = this.state.get(user)
while (struct !== null && struct._id.clock === userState) {
userState += struct._length
struct = this.y.os.get(new ID(user, userState))
}
this.state.set(user, userState)
}
getState (user) {
let state = this.state.get(user)
if (state == null) {
return 0
}
return state
}
setState (user, state) {
// TODO: modify missingi structs here
const beforeState = this.y._transaction.beforeState
if (!beforeState.has(user)) {
beforeState.set(user, this.getState(user))
}
this.state.set(user, state)
}
}

619
src/Struct.js Normal file
View File

@@ -0,0 +1,619 @@
const CDELETE = 0
const CINSERT = 1
const CLIST = 2
const CMAP = 3
const CXML = 4
/*
An operation also defines the structure of a type. This is why operation and
structure are used interchangeably here.
It must be of the type Object. I hope to achieve some performance
improvements when working on databases that support the json format.
An operation must have the following properties:
* encode
- Encode the structure in a readable format (preferably string- todo)
* decode (todo)
- decode structure to json
* execute
- Execute the semantics of an operation.
* requiredOps
- Operations that are required to execute this operation.
*/
export default function extendStruct (Y) {
let Struct = {}
Y.Struct = Struct
Struct.binaryDecodeOperation = function (decoder) {
let code = decoder.peekUint8()
if (code === CDELETE) {
return Struct.Delete.binaryDecode(decoder)
} else if (code === CINSERT) {
return Struct.Insert.binaryDecode(decoder)
} else if (code === CLIST) {
return Struct.List.binaryDecode(decoder)
} else if (code === CMAP) {
return Struct.Map.binaryDecode(decoder)
} else if (code === CXML) {
return Struct.Xml.binaryDecode(decoder)
} else {
throw new Error('Unable to decode operation!')
}
}
/* This is the only operation that is actually not a structure, because
it is not stored in the OS. This is why it _does not_ have an id
op = {
target: Id
}
*/
Struct.Delete = {
encode: function (op) {
return {
target: op.target,
length: op.length || 0,
struct: 'Delete'
}
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CDELETE)
encoder.writeOpID(op.target)
encoder.writeVarUint(op.length || 0)
},
binaryDecode: function (decoder) {
decoder.skip8()
return {
target: decoder.readOpID(),
length: decoder.readVarUint(),
struct: 'Delete'
}
},
requiredOps: function (op) {
return [] // [op.target]
},
execute: function (op) {
return this.deleteOperation(op.target, op.length || 1)
}
}
/* {
content: [any],
opContent: Id,
id: Id,
left: Id,
origin: Id,
right: Id,
parent: Id,
parentSub: string (optional), // child of Map type
}
*/
Struct.Insert = {
encode: function (op/* :Insertion */) /* :Insertion */ {
// TODO: you could not send the "left" property, then you also have to
// "op.left = null" in $execute or $decode
var e/* :any */ = {
id: op.id,
left: op.left,
right: op.right,
origin: op.origin,
parent: op.parent,
struct: op.struct
}
if (op.parentSub != null) {
e.parentSub = op.parentSub
}
if (op.hasOwnProperty('opContent')) {
e.opContent = op.opContent
} else {
e.content = op.content.slice()
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CINSERT)
// compute info property
let contentIsText = op.content != null && op.content.every(c => typeof c === 'string' && c.length === 1)
let originIsLeft = Y.utils.compareIds(op.left, op.origin)
let info =
(op.parentSub != null ? 1 : 0) |
(op.opContent != null ? 2 : 0) |
(contentIsText ? 4 : 0) |
(originIsLeft ? 8 : 0) |
(op.left != null ? 16 : 0) |
(op.right != null ? 32 : 0) |
(op.origin != null ? 64 : 0)
encoder.writeUint8(info)
encoder.writeOpID(op.id)
encoder.writeOpID(op.parent)
if (info & 16) {
encoder.writeOpID(op.left)
}
if (info & 32) {
encoder.writeOpID(op.right)
}
if (!originIsLeft && info & 64) {
encoder.writeOpID(op.origin)
}
if (info & 1) {
// write parentSub
encoder.writeVarString(op.parentSub)
}
if (info & 2) {
// write opContent
encoder.writeOpID(op.opContent)
} else if (info & 4) {
// write text
encoder.writeVarString(op.content.join(''))
} else {
// convert to JSON and write
encoder.writeVarString(JSON.stringify(op.content))
}
},
binaryDecode: function (decoder) {
let op = {
struct: 'Insert'
}
decoder.skip8()
// get info property
let info = decoder.readUint8()
op.id = decoder.readOpID()
op.parent = decoder.readOpID()
if (info & 16) {
op.left = decoder.readOpID()
} else {
op.left = null
}
if (info & 32) {
op.right = decoder.readOpID()
} else {
op.right = null
}
if (info & 8) {
// origin is left
op.origin = op.left
} else if (info & 64) {
op.origin = decoder.readOpID()
} else {
op.origin = null
}
if (info & 1) {
// has parentSub
op.parentSub = decoder.readVarString()
}
if (info & 2) {
// has opContent
op.opContent = decoder.readOpID()
} else if (info & 4) {
// has pure text content
op.content = decoder.readVarString().split('')
} else {
// has mixed content
let s = decoder.readVarString()
op.content = JSON.parse(s)
}
return op
},
requiredOps: function (op) {
var ids = []
if (op.left != null) {
ids.push(op.left)
}
if (op.right != null) {
ids.push(op.right)
}
if (op.origin != null && !Y.utils.compareIds(op.left, op.origin)) {
ids.push(op.origin)
}
// if (op.right == null && op.left == null) {
ids.push(op.parent)
if (op.opContent != null) {
ids.push(op.opContent)
}
return ids
},
getDistanceToOrigin: function (op) {
if (op.left == null) {
return 0
} else {
var d = 0
var o = this.getInsertion(op.left)
while (!Y.utils.matchesId(o, op.origin)) {
d++
if (o.left == null) {
break
} else {
o = this.getInsertion(o.left)
}
}
return d
}
},
/*
# $this has to find a unique position between origin and the next known character
# case 1: $origin equals $o.origin: the $creator parameter decides if left or right
# let $OL= [o1,o2,o3,o4], whereby $this is to be inserted between o1 and o4
# o2,o3 and o4 origin is 1 (the position of o2)
# there is the case that $this.creator < o2.creator, but o3.creator < $this.creator
# then o2 knows o3. Since on another client $OL could be [o1,o3,o4] the problem is complex
# therefore $this would be always to the right of o3
# case 2: $origin < $o.origin
# if current $this insert_position > $o origin: $this ins
# else $insert_position will not change
# (maybe we encounter case 1 later, then this will be to the right of $o)
# case 3: $origin > $o.origin
# $this insert_position is to the left of $o (forever!)
*/
execute: function (op) {
var i // loop counter
// during this function some ops may get split into two pieces (e.g. with getInsertionCleanEnd)
// We try to merge them later, if possible
var tryToRemergeLater = []
if (op.origin != null) { // TODO: !== instead of !=
// we save in origin that op originates in it
// we need that later when we eventually garbage collect origin (see transaction)
var origin = this.getInsertionCleanEnd(op.origin)
if (origin.originOf == null) {
origin.originOf = []
}
origin.originOf.push(op.id)
this.setOperation(origin)
if (origin.right != null) {
tryToRemergeLater.push(origin.right)
}
}
var distanceToOrigin = i = Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0)
// now we begin to insert op in the list of insertions..
var o
var parent
var start
// find o. o is the first conflicting operation
if (op.left != null) {
o = this.getInsertionCleanEnd(op.left)
if (!Y.utils.compareIds(op.left, op.origin) && o.right != null) {
// only if not added previously
tryToRemergeLater.push(o.right)
}
o = (o.right == null) ? null : this.getOperation(o.right)
} else { // left == null
parent = this.getOperation(op.parent)
let startId = op.parentSub ? parent.map[op.parentSub] : parent.start
start = startId == null ? null : this.getOperation(startId)
o = start
}
// make sure to split op.right if necessary (also add to tryCombineWithLeft)
if (op.right != null) {
tryToRemergeLater.push(op.right)
this.getInsertionCleanStart(op.right)
}
// handle conflicts
while (true) {
if (o != null && !Y.utils.compareIds(o.id, op.right)) {
var oOriginDistance = Struct.Insert.getDistanceToOrigin.call(this, o)
if (oOriginDistance === i) {
// case 1
if (o.id[0] < op.id[0]) {
op.left = Y.utils.getLastId(o)
distanceToOrigin = i + 1 // just ignore o.content.length, doesn't make a difference
}
} else if (oOriginDistance < i) {
// case 2
if (i - distanceToOrigin <= oOriginDistance) {
op.left = Y.utils.getLastId(o)
distanceToOrigin = i + 1 // just ignore o.content.length, doesn't make a difference
}
} else {
break
}
i++
if (o.right != null) {
o = this.getInsertion(o.right)
} else {
o = null
}
} else {
break
}
}
// reconnect..
var left = null
var right = null
if (parent == null) {
parent = this.getOperation(op.parent)
}
// reconnect left and set right of op
if (op.left != null) {
left = this.getInsertion(op.left)
// link left
op.right = left.right
left.right = op.id
this.setOperation(left)
} else {
// set op.right from parent, if necessary
op.right = op.parentSub ? parent.map[op.parentSub] || null : parent.start
}
// reconnect right
if (op.right != null) {
// TODO: wanna connect right too?
right = this.getOperation(op.right)
right.left = Y.utils.getLastId(op)
// if right exists, and it is supposed to be gc'd. Remove it from the gc
if (right.gc != null) {
if (right.content != null && right.content.length > 1) {
right = this.getInsertionCleanEnd(right.id)
}
this.store.removeFromGarbageCollector(right)
}
this.setOperation(right)
}
// update parents .map/start/end properties
if (op.parentSub != null) {
if (left == null) {
parent.map[op.parentSub] = op.id
this.setOperation(parent)
}
// is a child of a map struct.
// Then also make sure that only the most left element is not deleted
// We do not call the type in this case (this is what the third parameter is for)
if (op.right != null) {
this.deleteOperation(op.right, 1, true)
}
if (op.left != null) {
this.deleteOperation(op.id, 1, true)
}
} else {
if (right == null || left == null) {
if (right == null) {
parent.end = Y.utils.getLastId(op)
}
if (left == null) {
parent.start = op.id
}
this.setOperation(parent)
}
}
// try to merge original op.left and op.origin
for (i = 0; i < tryToRemergeLater.length; i++) {
var m = this.getOperation(tryToRemergeLater[i])
this.tryCombineWithLeft(m)
}
}
}
/*
{
start: null,
end: null,
struct: "List",
type: "",
id: this.os.getNextOpId(1)
}
*/
Struct.List = {
create: function (id) {
return {
start: null,
end: null,
struct: 'List',
id: id
}
},
encode: function (op) {
var e = {
struct: 'List',
id: op.id,
type: op.type
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CLIST)
encoder.writeOpID(op.id)
encoder.writeVarString(op.type)
},
binaryDecode: function (decoder) {
decoder.skip8()
let op = {
id: decoder.readOpID(),
type: decoder.readVarString(),
struct: 'List',
start: null,
end: null
}
return op
},
requiredOps: function () {
/*
var ids = []
if (op.start != null) {
ids.push(op.start)
}
if (op.end != null){
ids.push(op.end)
}
return ids
*/
return []
},
execute: function (op) {
op.start = null
op.end = null
},
ref: function (op, pos) {
if (op.start == null) {
return null
}
var res = null
var o = this.getOperation(op.start)
while (true) {
if (!o.deleted) {
res = o
pos--
}
if (pos >= 0 && o.right != null) {
o = this.getOperation(o.right)
} else {
break
}
}
return res
},
map: function (o, f) {
o = o.start
var res = []
while (o != null) { // TODO: change to != (at least some convention)
var operation = this.getOperation(o)
if (!operation.deleted) {
res.push(f(operation))
}
o = operation.right
}
return res
}
}
/*
{
map: {},
struct: "Map",
type: "",
id: this.os.getNextOpId(1)
}
*/
Struct.Map = {
create: function (id) {
return {
id: id,
map: {},
struct: 'Map'
}
},
encode: function (op) {
var e = {
struct: 'Map',
type: op.type,
id: op.id,
map: {} // overwrite map!!
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CMAP)
encoder.writeOpID(op.id)
encoder.writeVarString(op.type)
},
binaryDecode: function (decoder) {
decoder.skip8()
let op = {
id: decoder.readOpID(),
type: decoder.readVarString(),
struct: 'Map',
map: {}
}
return op
},
requiredOps: function () {
return []
},
execute: function (op) {
op.start = null
op.end = null
},
/*
Get a property by name
*/
get: function (op, name) {
var oid = op.map[name]
if (oid != null) {
var res = this.getOperation(oid)
if (res == null || res.deleted) {
return void 0
} else if (res.opContent == null) {
return res.content[0]
} else {
return this.getType(res.opContent)
}
}
}
}
/*
{
map: {},
start: null,
end: null,
struct: "Xml",
type: "",
id: this.os.getNextOpId(1)
}
*/
Struct.Xml = {
create: function (id, args) {
let nodeName = args != null ? args.nodeName : null
return {
id: id,
map: {},
start: null,
end: null,
struct: 'Xml',
nodeName
}
},
encode: function (op) {
var e = {
struct: 'Xml',
type: op.type,
id: op.id,
map: {},
nodeName: op.nodeName
}
return e
},
binaryEncode: function (encoder, op) {
encoder.writeUint8(CXML)
encoder.writeOpID(op.id)
encoder.writeVarString(op.type)
encoder.writeVarString(op.nodeName)
},
binaryDecode: function (decoder) {
decoder.skip8()
let op = {
id: decoder.readOpID(),
type: decoder.readVarString(),
struct: 'Xml',
map: {},
start: null,
end: null,
nodeName: decoder.readVarString()
}
return op
},
requiredOps: function () {
return []
},
execute: function () {},
ref: Struct.List.ref,
map: Struct.List.map,
/*
Get a property by name
*/
get: Struct.Map.get
}
}

View File

@@ -1,122 +0,0 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { logID } from '../MessageHandler/messageToString.js'
/**
* @private
* Delete all items in an ID-range
* TODO: implement getItemCleanStartNode for better performance (only one lookup)
*/
export function deleteItemRange (y, user, clock, range, gcChildren) {
const createDelete = y.connector !== null && y.connector._forwardAppliedStructs
let item = y.os.getItemCleanStart(new ID(user, clock))
if (item !== null) {
if (!item._deleted) {
item._splitAt(y, range)
item._delete(y, createDelete, true)
}
let itemLen = item._length
range -= itemLen
clock += itemLen
if (range > 0) {
let node = y.os.findNode(new ID(user, clock))
while (node !== null && node.val !== null && range > 0 && node.val._id.equals(new ID(user, clock))) {
const nodeVal = node.val
if (!nodeVal._deleted) {
nodeVal._splitAt(y, range)
nodeVal._delete(y, createDelete, gcChildren)
}
const nodeLen = nodeVal._length
range -= nodeLen
clock += nodeLen
node = node.next()
}
}
}
}
/**
* @private
* A Delete change is not a real Item, but it provides the same interface as an
* Item. The only difference is that it will not be saved in the ItemStore
* (OperationStore), but instead it is safed in the DeleteStore.
*/
export default class Delete {
constructor () {
this._target = null
this._length = null
}
/**
* @private
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
*/
_fromBinary (y, decoder) {
// TODO: set target, and add it to missing if not found
// There is an edge case in p2p networks!
const targetID = decoder.readID()
this._targetID = targetID
this._length = decoder.readVarUint()
if (y.os.getItem(targetID) === null) {
return [targetID]
} else {
return []
}
}
/**
* @private
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoder.writeID(this._targetID)
encoder.writeVarUint(this._length)
}
/**
* @private
* Integrates this Item into the shared structure.
*
* This method actually applies the change to the Yjs instance. In the case of
* Delete it marks the delete target as deleted.
*
* * If created remotely (a remote user deleted something),
* this Delete is applied to all structs in id-range.
* * If created lokally (e.g. when y-array deletes a range of elements),
* this struct is broadcasted only (it is already executed)
*/
_integrate (y, locallyCreated = false) {
if (!locallyCreated) {
// from remote
const id = this._targetID
deleteItemRange(y, id.user, id.clock, this._length, false)
} else if (y.connector !== null) {
// from local
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return `Delete - target: ${logID(this._targetID)}, len: ${this._length}`
}
}

View File

@@ -1,94 +0,0 @@
import { getStructReference } from '../Util/structReferences.js'
import { RootFakeUserID } from '../Util/ID/RootID.js'
import ID from '../Util/ID/ID.js'
// TODO should have the same base class as Item
export default class GC {
constructor () {
this._id = null
this._length = 0
}
get _deleted () {
return true
}
_integrate (y) {
const id = this._id
const userState = y.ss.getState(id.user)
if (id.clock === userState) {
y.ss.setState(id.user, id.clock + this._length)
}
y.ds.mark(this._id, this._length, true)
let n = y.os.put(this)
const prev = n.prev().val
if (prev !== null && prev.constructor === GC && prev._id.user === n.val._id.user && prev._id.clock + prev._length === n.val._id.clock) {
// TODO: do merging for all items!
prev._length += n.val._length
y.os.delete(n.val._id)
n = prev
}
if (n.val) {
n = n.val
}
const next = y.os.findNext(n._id)
if (next !== null && next.constructor === GC && next._id.user === n._id.user && next._id.clock === n._id.clock + n._length) {
n._length += next._length
y.os.delete(next._id)
}
if (id.user !== RootFakeUserID) {
if (y.connector !== null && (y.connector._forwardAppliedStructs || id.user === y.userID)) {
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
}
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
* @private
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
encoder.writeID(this._id)
encoder.writeVarUint(this._length)
}
/**
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
* @private
*/
_fromBinary (y, decoder) {
const id = decoder.readID()
this._id = id
this._length = decoder.readVarUint()
const missing = []
if (y.ss.getState(id.user) < id.clock) {
missing.push(new ID(id.user, id.clock - 1))
}
return missing
}
_splitAt () {
return this
}
_clonePartial (diff) {
const gc = new GC()
gc._id = new ID(this._id.user, this._id.clock + diff)
gc._length = this._length - diff
return gc
}
}

View File

@@ -1,527 +0,0 @@
import { getStructReference } from '../Util/structReferences.js'
import ID from '../Util/ID/ID.js'
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js'
import Delete from './Delete.js'
import { transactionTypeChanged } from '../Transaction.js'
import GC from './GC.js'
/**
* @private
* Helper utility to split an Item (see {@link Item#_splitAt})
* - copies all properties from a to b
* - connects a to b
* - assigns the correct _id
* - saves b to os
*/
export function splitHelper (y, a, b, diff) {
const aID = a._id
b._id = new ID(aID.user, aID.clock + diff)
b._origin = a
b._left = a
b._right = a._right
if (b._right !== null) {
b._right._left = b
}
b._right_origin = a._right_origin
// do not set a._right_origin, as this will lead to problems when syncing
a._right = b
b._parent = a._parent
b._parentSub = a._parentSub
b._deleted = a._deleted
// now search all relevant items to the right and update origin
// if origin is not it foundOrigins, we don't have to search any longer
let foundOrigins = new Set()
foundOrigins.add(a)
let o = b._right
while (o !== null && foundOrigins.has(o._origin)) {
if (o._origin === a) {
o._origin = b
}
foundOrigins.add(o)
o = o._right
}
y.os.put(b)
if (y._transaction.newTypes.has(a)) {
y._transaction.newTypes.add(b)
} else if (y._transaction.deletedStructs.has(a)) {
y._transaction.deletedStructs.add(b)
}
}
/**
* Abstract class that represents any content.
*/
export default class Item {
constructor () {
/**
* The uniqe identifier of this type.
* @type {ID}
*/
this._id = null
/**
* The item that was originally to the left of this item.
* @type {Item}
*/
this._origin = null
/**
* The item that is currently to the left of this item.
* @type {Item}
*/
this._left = null
/**
* The item that is currently to the right of this item.
* @type {Item}
*/
this._right = null
/**
* The item that was originally to the right of this item.
* @type {Item}
*/
this._right_origin = null
/**
* The parent type.
* @type {Y|YType}
*/
this._parent = null
/**
* If the parent refers to this item with some kind of key (e.g. YMap, the
* key is specified here. The key is then used to refer to the list in which
* to insert this item. If `parentSub = null` type._start is the list in
* which to insert to. Otherwise it is `parent._start`.
* @type {String}
*/
this._parentSub = null
/**
* Whether this item was deleted or not.
* @type {Boolean}
*/
this._deleted = false
/**
* If this type's effect is reundone this type refers to the type that undid
* this operation.
* @type {Item}
*/
this._redone = null
}
/**
* Creates an Item with the same effect as this Item (without position effect)
*
* @private
*/
_copy () {
return new this.constructor()
}
/**
* Redoes the effect of this operation.
*
* @param {Y} y The Yjs instance.
*
* @private
*/
_redo (y) {
if (this._redone !== null) {
return this._redone
}
let struct = this._copy()
let left = this._left
let right = this
let parent = this._parent
// make sure that parent is redone
if (parent._deleted === true && parent._redone === null) {
parent._redo(y)
}
if (parent._redone !== null) {
parent = parent._redone
// find next cloned items
while (left !== null) {
if (left._redone !== null && left._redone._parent === parent) {
left = left._redone
break
}
left = left._left
}
while (right !== null) {
if (right._redone !== null && right._redone._parent === parent) {
right = right._redone
}
right = right._right
}
}
struct._origin = left
struct._left = left
struct._right = right
struct._right_origin = right
struct._parent = parent
struct._parentSub = this._parentSub
struct._integrate(y)
this._redone = struct
return struct
}
/**
* Computes the last content address of this Item.
*
* @private
*/
get _lastId () {
return new ID(this._id.user, this._id.clock + this._length - 1)
}
/**
* Computes the length of this Item.
*
* @private
*/
get _length () {
return 1
}
/**
* Should return false if this Item is some kind of meta information
* (e.g. format information).
*
* * Whether this Item should be addressable via `yarray.get(i)`
* * Whether this Item should be counted when computing yarray.length
*
* @private
*/
get _countable () {
return true
}
/**
* Splits this Item so that another Items can be inserted in-between.
* This must be overwritten if _length > 1
* Returns right part after split
* * diff === 0 => this
* * diff === length => this._right
* * otherwise => split _content and return right part of split
* (see {@link ItemJSON}/{@link ItemString} for implementation)
*
* @private
*/
_splitAt (y, diff) {
if (diff === 0) {
return this
}
return this._right
}
/**
* Mark this Item as deleted.
*
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
*
* @private
*/
_delete (y, createDelete = true) {
if (!this._deleted) {
this._deleted = true
y.ds.mark(this._id, this._length, false)
let del = new Delete()
del._targetID = this._id
del._length = this._length
if (createDelete) {
// broadcast and persists Delete
del._integrate(y, true)
} else if (y.persistence !== null) {
// only persist Delete
y.persistence.saveStruct(y, del)
}
transactionTypeChanged(y, this._parent, this._parentSub)
y._transaction.deletedStructs.add(this)
}
}
_gcChildren (y) {}
_gc (y) {
const gc = new GC()
gc._id = this._id
gc._length = this._length
y.os.delete(this._id)
gc._integrate(y)
}
/**
* This is called right before this Item receives any children.
* It can be overwritten to apply pending changes before applying remote changes
*
* @private
*/
_beforeChange () {
// nop
}
/**
* Integrates this Item into the shared structure.
*
* This method actually applies the change to the Yjs instance. In case of
* Item it connects _left and _right to this Item and calls the
* {@link Item#beforeChange} method.
*
* * Integrate the struct so that other types/structs can see it
* * Add this struct to y.os
* * Check if this is struct deleted
*
* @private
*/
_integrate (y) {
y._transaction.newTypes.add(this)
const parent = this._parent
const selfID = this._id
const user = selfID === null ? y.userID : selfID.user
const userState = y.ss.getState(user)
if (selfID === null) {
this._id = y.ss.getNextID(this._length)
} else if (selfID.user === RootFakeUserID) {
// nop
} else if (selfID.clock < userState) {
// already applied..
return []
} else if (selfID.clock === userState) {
y.ss.setState(selfID.user, userState + this._length)
} else {
// missing content from user
throw new Error('Can not apply yet!')
}
if (!parent._deleted && !y._transaction.changedTypes.has(parent) && !y._transaction.newTypes.has(parent)) {
// this is the first time parent is updated
// or this types is new
this._parent._beforeChange()
}
/*
# $this has to find a unique position between origin and the next known character
# case 1: $origin equals $o.origin: the $creator parameter decides if left or right
# let $OL= [o1,o2,o3,o4], whereby $this is to be inserted between o1 and o4
# o2,o3 and o4 origin is 1 (the position of o2)
# there is the case that $this.creator < o2.creator, but o3.creator < $this.creator
# then o2 knows o3. Since on another client $OL could be [o1,o3,o4] the problem is complex
# therefore $this would be always to the right of o3
# case 2: $origin < $o.origin
# if current $this insert_position > $o origin: $this ins
# else $insert_position will not change
# (maybe we encounter case 1 later, then this will be to the right of $o)
# case 3: $origin > $o.origin
# $this insert_position is to the left of $o (forever!)
*/
// handle conflicts
let o
// set o to the first conflicting item
if (this._left !== null) {
o = this._left._right
} else if (this._parentSub !== null) {
o = this._parent._map.get(this._parentSub) || null
} else {
o = this._parent._start
}
let conflictingItems = new Set()
let itemsBeforeOrigin = new Set()
// Let c in conflictingItems, b in itemsBeforeOrigin
// ***{origin}bbbb{this}{c,b}{c,b}{o}***
// Note that conflictingItems is a subset of itemsBeforeOrigin
while (o !== null && o !== this._right) {
itemsBeforeOrigin.add(o)
conflictingItems.add(o)
if (this._origin === o._origin) {
// case 1
if (o._id.user < this._id.user) {
this._left = o
conflictingItems.clear()
}
} else if (itemsBeforeOrigin.has(o._origin)) {
// case 2
if (!conflictingItems.has(o._origin)) {
this._left = o
conflictingItems.clear()
}
} else {
break
}
// TODO: try to use right_origin instead.
// Then you could basically omit conflictingItems!
// Note: you probably can't use right_origin in every case.. only when setting _left
o = o._right
}
// reconnect left/right + update parent map/start if necessary
const parentSub = this._parentSub
if (this._left === null) {
let right
if (parentSub !== null) {
const pmap = parent._map
right = pmap.get(parentSub) || null
pmap.set(parentSub, this)
} else {
right = parent._start
parent._start = this
}
this._right = right
if (right !== null) {
right._left = this
}
} else {
const left = this._left
const right = left._right
this._right = right
left._right = this
if (right !== null) {
right._left = this
}
}
if (parent._deleted) {
this._delete(y, false)
}
y.os.put(this)
transactionTypeChanged(y, parent, parentSub)
if (this._id.user !== RootFakeUserID) {
if (y.connector !== null && (y.connector._forwardAppliedStructs || this._id.user === y.userID)) {
y.connector.broadcastStruct(this)
}
if (y.persistence !== null) {
y.persistence.saveStruct(y, this)
}
}
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/
_toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor))
let info = 0
if (this._origin !== null) {
info += 0b1 // origin is defined
}
// TODO: remove
/* no longer send _left
if (this._left !== this._origin) {
info += 0b10 // do not copy origin to left
}
*/
if (this._right_origin !== null) {
info += 0b100
}
if (this._parentSub !== null) {
info += 0b1000
}
encoder.writeUint8(info)
encoder.writeID(this._id)
if (info & 0b1) {
encoder.writeID(this._origin._lastId)
}
// TODO: remove
/* see above
if (info & 0b10) {
encoder.writeID(this._left._lastId)
}
*/
if (info & 0b100) {
encoder.writeID(this._right_origin._id)
}
if ((info & 0b101) === 0) {
// neither origin nor right is defined
encoder.writeID(this._parent._id)
}
if (info & 0b1000) {
encoder.writeVarString(JSON.stringify(this._parentSub))
}
}
/**
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
*
* @private
*/
_fromBinary (y, decoder) {
let missing = []
const info = decoder.readUint8()
const id = decoder.readID()
this._id = id
// read origin
if (info & 0b1) {
// origin != null
const originID = decoder.readID()
// we have to query for left again because it might have been split/merged..
const origin = y.os.getItemCleanEnd(originID)
if (origin === null) {
missing.push(originID)
} else {
this._origin = origin
this._left = this._origin
}
}
// read right
if (info & 0b100) {
// right != null
const rightID = decoder.readID()
// we have to query for right again because it might have been split/merged..
const right = y.os.getItemCleanStart(rightID)
if (right === null) {
missing.push(rightID)
} else {
this._right = right
this._right_origin = right
}
}
// read parent
if ((info & 0b101) === 0) {
// neither origin nor right is defined
const parentID = decoder.readID()
// parent does not change, so we don't have to search for it again
if (this._parent === null) {
let parent
if (parentID.constructor === RootID) {
parent = y.os.get(parentID)
} else {
parent = y.os.getItem(parentID)
}
if (parent === null) {
missing.push(parentID)
} else {
this._parent = parent
}
}
} else if (this._parent === null) {
if (this._origin !== null) {
if (this._origin.constructor === GC) {
// if origin is a gc, set parent also gc'd
this._parent = this._origin
} else {
this._parent = this._origin._parent
}
} else if (this._right_origin !== null) {
// if origin is a gc, set parent also gc'd
if (this._right_origin.constructor === GC) {
this._parent = this._right_origin
} else {
this._parent = this._right_origin._parent
}
}
}
if (info & 0b1000) {
// TODO: maybe put this in read parent condition (you can also read parentsub from left/right)
this._parentSub = JSON.parse(decoder.readVarString())
}
if (y.ss.getState(id.user) < id.clock) {
missing.push(new ID(id.user, id.clock - 1))
}
return missing
}
}

View File

@@ -1,35 +0,0 @@
import { default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemEmbed extends Item {
constructor () {
super()
this.embed = null
}
_copy (undeleteChildren, copyPosition) {
let struct = super._copy(undeleteChildren, copyPosition)
struct.embed = this.embed
return struct
}
get _length () {
return 1
}
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.embed = JSON.parse(decoder.readVarString())
return missing
}
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(JSON.stringify(this.embed))
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('ItemEmbed', this, `embed:${JSON.stringify(this.embed)}`)
}
}

View File

@@ -1,42 +0,0 @@
import { default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemFormat extends Item {
constructor () {
super()
this.key = null
this.value = null
}
_copy (undeleteChildren, copyPosition) {
let struct = super._copy(undeleteChildren, copyPosition)
struct.key = this.key
struct.value = this.value
return struct
}
get _length () {
return 1
}
get _countable () {
return false
}
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.key = decoder.readVarString()
this.value = JSON.parse(decoder.readVarString())
return missing
}
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this.key)
encoder.writeVarString(JSON.stringify(this.value))
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('ItemFormat', this, `key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)}`)
}
}

View File

@@ -1,68 +0,0 @@
import { splitHelper, default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemJSON extends Item {
constructor () {
super()
this._content = null
}
_copy () {
let struct = super._copy()
struct._content = this._content
return struct
}
get _length () {
return this._content.length
}
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder)
let len = decoder.readVarUint()
this._content = new Array(len)
for (let i = 0; i < len; i++) {
const ctnt = decoder.readVarString()
let parsed
if (ctnt === 'undefined') {
parsed = undefined
} else {
parsed = JSON.parse(ctnt)
}
this._content[i] = parsed
}
return missing
}
_toBinary (encoder) {
super._toBinary(encoder)
let len = this._content.length
encoder.writeVarUint(len)
for (let i = 0; i < len; i++) {
let encoded
let content = this._content[i]
if (content === undefined) {
encoded = 'undefined'
} else {
encoded = JSON.stringify(content)
}
encoder.writeVarString(encoded)
}
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('ItemJSON', this, `content:${JSON.stringify(this._content)}`)
}
_splitAt (y, diff) {
if (diff === 0) {
return this
} else if (diff >= this._length) {
return this._right
}
let item = new ItemJSON()
item._content = this._content.splice(diff)
splitHelper(y, this, item, diff)
return item
}
}

View File

@@ -1,47 +0,0 @@
import { splitHelper, default as Item } from './Item.js'
import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemString extends Item {
constructor () {
super()
this._content = null
}
_copy () {
let struct = super._copy()
struct._content = this._content
return struct
}
get _length () {
return this._content.length
}
_fromBinary (y, decoder) {
let missing = super._fromBinary(y, decoder)
this._content = decoder.readVarString()
return missing
}
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this._content)
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('ItemString', this, `content:"${this._content}"`)
}
_splitAt (y, diff) {
if (diff === 0) {
return this
} else if (diff >= this._length) {
return this._right
}
let item = new ItemString()
item._content = this._content.slice(diff)
this._content = this._content.slice(0, diff)
splitHelper(y, this, item, diff)
return item
}
}

View File

@@ -1,243 +0,0 @@
import Item from './Item.js'
import EventHandler from '../Util/EventHandler.js'
import ID from '../Util/ID/ID.js'
// restructure children as if they were inserted one after another
function integrateChildren (y, start) {
let right
do {
right = start._right
start._right = null
start._right_origin = null
start._origin = start._left
start._integrate(y)
start = right
} while (right !== null)
}
export function getListItemIDByPosition (type, i) {
let pos = 0
let n = type._start
while (n !== null) {
if (!n._deleted) {
if (pos <= i && i < pos + n._length) {
const id = n._id
return new ID(id.user, id.clock + i - pos)
}
pos++
}
n = n._right
}
}
function gcChildren (y, item) {
while (item !== null) {
item._delete(y, false, true)
item._gc(y)
item = item._right
}
}
/**
* Abstract Yjs Type class
*/
export default class Type extends Item {
constructor () {
super()
this._map = new Map()
this._start = null
this._y = null
this._eventHandler = new EventHandler()
this._deepEventHandler = new EventHandler()
}
/**
* Compute the path from this type to the specified target.
*
* @example
* It should be accessible via `this.get(result[0]).get(result[1])..`
* const path = type.getPathTo(child)
* // assuming `type instanceof YArray`
* console.log(path) // might look like => [2, 'key1']
* child === type.get(path[0]).get(path[1])
*
* @param {YType} type Type target
* @return {Array<string>} Path to the target
*/
getPathTo (type) {
if (type === this) {
return []
}
const path = []
const y = this._y
while (type !== this && type !== y) {
let parent = type._parent
if (type._parentSub !== null) {
path.unshift(type._parentSub)
} else {
// parent is array-ish
for (let [i, child] of parent) {
if (child === type) {
path.unshift(i)
break
}
}
}
type = parent
}
if (type !== this) {
throw new Error('The type is not a child of this node')
}
return path
}
/**
* @private
* Call event listeners with an event. This will also add an event to all
* parents (for `.observeDeep` handlers).
*/
_callEventHandler (transaction, event) {
const changedParentTypes = transaction.changedParentTypes
this._eventHandler.callEventListeners(transaction, event)
let type = this
while (type !== this._y) {
let events = changedParentTypes.get(type)
if (events === undefined) {
events = []
changedParentTypes.set(type, events)
}
events.push(event)
type = type._parent
}
}
/**
* @private
* Helper method to transact if the y instance is available.
*
* TODO: Currently event handlers are not thrown when a type is not registered
* with a Yjs instance.
*/
_transact (f) {
const y = this._y
if (y !== null) {
y.transact(f)
} else {
f(y)
}
}
/**
* Observe all events that are created on this type.
*
* @param {Function} f Observer function
*/
observe (f) {
this._eventHandler.addEventListener(f)
}
/**
* Observe all events that are created by this type and its children.
*
* @param {Function} f Observer function
*/
observeDeep (f) {
this._deepEventHandler.addEventListener(f)
}
/**
* Unregister an observer function.
*
* @param {Function} f Observer function
*/
unobserve (f) {
this._eventHandler.removeEventListener(f)
}
/**
* Unregister an observer function.
*
* @param {Function} f Observer function
*/
unobserveDeep (f) {
this._deepEventHandler.removeEventListener(f)
}
/**
* @private
* Integrate this type into the Yjs instance.
*
* * Save this struct in the os
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Y} y The Yjs instance
*/
_integrate (y) {
super._integrate(y)
this._y = y
// when integrating children we must make sure to
// integrate start
const start = this._start
if (start !== null) {
this._start = null
integrateChildren(y, start)
}
// integrate map children
const map = this._map
this._map = new Map()
for (let t of map.values()) {
// TODO make sure that right elements are deleted!
integrateChildren(y, t)
}
}
_gcChildren (y) {
gcChildren(y, this._start)
this._start = null
this._map.forEach(item => {
gcChildren(y, item)
})
this._map = new Map()
}
_gc (y) {
this._gcChildren(y)
super._gc(y)
}
/**
* @private
* Mark this Item as deleted.
*
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
* collect the children of this type.
*/
_delete (y, createDelete, gcChildren) {
if (gcChildren === undefined) {
gcChildren = y._hasUndoManager === false
}
super._delete(y, createDelete, gcChildren)
y._transaction.changedTypes.delete(this)
// delete map types
for (let value of this._map.values()) {
if (value instanceof Item && !value._deleted) {
value._delete(y, false, gcChildren)
}
}
// delete array types
let t = this._start
while (t !== null) {
if (!t._deleted) {
t._delete(y, false, gcChildren)
}
t = t._right
}
if (gcChildren) {
this._gcChildren(y)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,378 +0,0 @@
import Type from '../../Struct/Type.js'
import ItemJSON from '../../Struct/ItemJSON.js'
import ItemString from '../../Struct/ItemString.js'
import { logID, logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js'
/**
* Event that describes the changes on a YArray
*
* @param {YArray} yarray The changed type
* @param {Boolean} remote Whether the changed was caused by a remote peer
* @param {Transaction} transaction The transaction object
*/
export class YArrayEvent extends YEvent {
constructor (yarray, remote, transaction) {
super(yarray)
this.remote = remote
this._transaction = transaction
this._addedElements = null
this._removedElements = null
}
/**
* Child elements that were added in this transaction.
*
* @return {Set}
*/
get addedElements () {
if (this._addedElements === null) {
const target = this.target
const transaction = this._transaction
const addedElements = new Set()
transaction.newTypes.forEach(function (type) {
if (type._parent === target && !transaction.deletedStructs.has(type)) {
addedElements.add(type)
}
})
this._addedElements = addedElements
}
return this._addedElements
}
/**
* Child elements that were removed in this transaction.
*
* @return {Set}
*/
get removedElements () {
if (this._removedElements === null) {
const target = this.target
const transaction = this._transaction
const removedElements = new Set()
transaction.deletedStructs.forEach(function (struct) {
if (struct._parent === target && !transaction.newTypes.has(struct)) {
removedElements.add(struct)
}
})
this._removedElements = removedElements
}
return this._removedElements
}
}
/**
* A shared Array implementation.
*/
export default class YArray extends Type {
/**
* @private
* Creates YArray Event and calls observers.
*/
_callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YArrayEvent(this, remote, transaction))
}
/**
* Returns the i-th element from a YArray.
*
* @param {Integer} index The index of the element to return from the YArray
*/
get (index) {
let n = this._start
while (n !== null) {
if (!n._deleted && n._countable) {
if (index < n._length) {
if (n.constructor === ItemJSON || n.constructor === ItemString) {
return n._content[index]
} else {
return n
}
}
index -= n._length
}
n = n._right
}
}
/**
* Transforms this YArray to a JavaScript Array.
*
* @return {Array}
*/
toArray () {
return this.map(c => c)
}
/**
* Transforms this Shared Type to a JSON object.
*
* @return {Array}
*/
toJSON () {
return this.map(c => {
if (c instanceof Type) {
if (c.toJSON !== null) {
return c.toJSON()
} else {
return c.toString()
}
}
return c
})
}
/**
* Returns an Array with the result of calling a provided function on every
* element of this YArray.
*
* @param {Function} f Function that produces an element of the new Array
* @return {Array} A new array with each element being the result of the
* callback function
*/
map (f) {
const res = []
this.forEach((c, i) => {
res.push(f(c, i, this))
})
return res
}
/**
* Executes a provided function on once on overy element of this YArray.
*
* @param {Function} f A function to execute on every element of this YArray.
*/
forEach (f) {
let index = 0
let n = this._start
while (n !== null) {
if (!n._deleted && n._countable) {
if (n instanceof Type) {
f(n, index++, this)
} else {
const content = n._content
const contentLen = content.length
for (let i = 0; i < contentLen; i++) {
index++
f(content[i], index, this)
}
}
}
n = n._right
}
}
/**
* Computes the length of this YArray.
*/
get length () {
let length = 0
let n = this._start
while (n !== null) {
if (!n._deleted && n._countable) {
length += n._length
}
n = n._right
}
return length
}
[Symbol.iterator] () {
return {
next: function () {
while (this._item !== null && (this._item._deleted || this._item._length <= this._itemElement)) {
// item is deleted or itemElement does not exist (is deleted)
this._item = this._item._right
this._itemElement = 0
}
if (this._item === null) {
return {
done: true
}
}
let content
if (this._item instanceof Type) {
content = this._item
} else {
content = this._item._content[this._itemElement++]
}
return {
value: content,
done: false
}
},
_item: this._start,
_itemElement: 0,
_count: 0
}
}
/**
* Deletes elements starting from an index.
*
* @param {Integer} index Index at which to start deleting elements
* @param {Integer} length The number of elements to remove. Defaults to 1.
*/
delete (index, length = 1) {
this._y.transact(() => {
let item = this._start
let count = 0
while (item !== null && length > 0) {
if (!item._deleted && item._countable) {
if (count <= index && index < count + item._length) {
const diffDel = index - count
item = item._splitAt(this._y, diffDel)
item._splitAt(this._y, length)
length -= item._length
item._delete(this._y)
count += diffDel
} else {
count += item._length
}
}
item = item._right
}
})
if (length > 0) {
throw new Error('Delete exceeds the range of the YArray')
}
}
/**
* @private
* Inserts content after an element container.
*
* @param {Item} left The element container to use as a reference.
* @param {Array} content The Array of content to insert (see {@see insert})
*/
insertAfter (left, content) {
this._transact(y => {
let right
if (left === null) {
right = this._start
} else {
right = left._right
}
let prevJsonIns = null
for (let i = 0; i < content.length; i++) {
let c = content[i]
if (typeof c === 'function') {
c = new c() // eslint-disable-line new-cap
}
if (c instanceof Type) {
if (prevJsonIns !== null) {
if (y !== null) {
prevJsonIns._integrate(y)
}
left = prevJsonIns
prevJsonIns = null
}
c._origin = left
c._left = left
c._right = right
c._right_origin = right
c._parent = this
if (y !== null) {
c._integrate(y)
} else if (left === null) {
this._start = c
} else {
left._right = c
}
left = c
} else {
if (prevJsonIns === null) {
prevJsonIns = new ItemJSON()
prevJsonIns._origin = left
prevJsonIns._left = left
prevJsonIns._right = right
prevJsonIns._right_origin = right
prevJsonIns._parent = this
prevJsonIns._content = []
}
prevJsonIns._content.push(c)
}
}
if (prevJsonIns !== null) {
if (y !== null) {
prevJsonIns._integrate(y)
} else if (prevJsonIns._left === null) {
this._start = prevJsonIns
}
}
})
return content
}
/**
* Inserts new content at an index.
*
* Important: This function expects an array of content. Not just a content
* object. The reason for this "weirdness" is that inserting several elements
* is very efficient when it is done as a single operation.
*
* @example
* // Insert character 'a' at position 0
* yarray.insert(0, ['a'])
* // Insert numbers 1, 2 at position 1
* yarray.insert(2, [1, 2])
*
* @param {Integer} index The index to insert content at.
* @param {Array} content The array of content
*/
insert (index, content) {
this._transact(() => {
let left = null
let right = this._start
let count = 0
const y = this._y
while (right !== null) {
const rightLen = right._deleted ? 0 : (right._length - 1)
if (count <= index && index <= count + rightLen) {
const splitDiff = index - count
right = right._splitAt(y, splitDiff)
left = right._left
count += splitDiff
break
}
if (!right._deleted) {
count += right._length
}
left = right
right = right._right
}
if (index > count) {
throw new Error('Index exceeds array range!')
}
this.insertAfter(left, content)
})
}
/**
* Appends content to this YArray.
*
* @param {Array} content Array of content to append.
*/
push (content) {
let n = this._start
let lastUndeleted = null
while (n !== null) {
if (!n._deleted) {
lastUndeleted = n
}
n = n._right
}
this.insertAfter(lastUndeleted, content)
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('YArray', this, `start:${logID(this._start)}"`)
}
}

View File

@@ -1,175 +0,0 @@
import Type from '../../Struct/Type.js'
import Item from '../../Struct/Item.js'
import ItemJSON from '../../Struct/ItemJSON.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js'
/**
* Event that describes the changes on a YMap.
*
* @param {YMap} ymap The YArray that changed.
* @param {Set<any>} subs The keys that changed.
* @param {boolean} remote Whether the change was created by a remote peer.
*/
export class YMapEvent extends YEvent {
constructor (ymap, subs, remote) {
super(ymap)
this.keysChanged = subs
this.remote = remote
}
}
/**
* A shared Map implementation.
*/
export default class YMap extends Type {
/**
* @private
* Creates YMap Event and calls observers.
*/
_callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YMapEvent(this, parentSubs, remote))
}
/**
* Transforms this Shared Type to a JSON object.
*
* @return {Object}
*/
toJSON () {
const map = {}
for (let [key, item] of this._map) {
if (!item._deleted) {
let res
if (item instanceof Type) {
if (item.toJSON !== undefined) {
res = item.toJSON()
} else {
res = item.toString()
}
} else {
res = item._content[0]
}
map[key] = res
}
}
return map
}
/**
* Returns the keys for each element in the YMap Type.
*
* @return {Array}
*/
keys () {
// TODO: Should return either Iterator or Set!
let keys = []
for (let [key, value] of this._map) {
if (!value._deleted) {
keys.push(key)
}
}
return keys
}
/**
* Remove a specified element from this YMap.
*
* @param {encodable} key The key of the element to remove.
*/
delete (key) {
this._transact((y) => {
let c = this._map.get(key)
if (y !== null && c !== undefined) {
c._delete(y)
}
})
}
/**
* Adds or updates an element with a specified key and value.
*
* @param {encodable} key The key of the element to add to this YMap.
* @param {encodable | YType} value The value of the element to add to this
* YMap.
*/
set (key, value) {
this._transact(y => {
const old = this._map.get(key) || null
if (old !== null) {
if (
old.constructor === ItemJSON &&
!old._deleted && old._content[0] === value
) {
// Trying to overwrite with same value
// break here
return value
}
if (y !== null) {
old._delete(y)
}
}
let v
if (typeof value === 'function') {
v = new value() // eslint-disable-line new-cap
value = v
} else if (value instanceof Item) {
v = value
} else {
v = new ItemJSON()
v._content = [value]
}
v._right = old
v._right_origin = old
v._parent = this
v._parentSub = key
if (y !== null) {
v._integrate(y)
} else {
this._map.set(key, v)
}
})
return value
}
/**
* Returns a specified element from this YMap.
*
* @param {encodable} key The key of the element to return.
*/
get (key) {
let v = this._map.get(key)
if (v === undefined || v._deleted) {
return undefined
}
if (v instanceof Type) {
return v
} else {
return v._content[v._content.length - 1]
}
}
/**
* Returns a boolean indicating whether the specified key exists or not.
*
* @param {encodable} key The key to test.
*/
has (key) {
let v = this._map.get(key)
if (v === undefined || v._deleted) {
return false
} else {
return true
}
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('YMap', this, `mapSize:${this._map.size}`)
}
}

View File

@@ -1,654 +0,0 @@
import ItemString from '../../Struct/ItemString.js'
import ItemEmbed from '../../Struct/ItemEmbed.js'
import ItemFormat from '../../Struct/ItemFormat.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
import { YArrayEvent, default as YArray } from '../YArray/YArray.js'
/**
* @private
*/
function integrateItem (item, parent, y, left, right) {
item._origin = left
item._left = left
item._right = right
item._right_origin = right
item._parent = parent
if (y !== null) {
item._integrate(y)
} else if (left === null) {
parent._start = item
} else {
left._right = item
}
}
/**
* @private
*/
function findNextPosition (currentAttributes, parent, left, right, count) {
while (right !== null && count > 0) {
switch (right.constructor) {
case ItemEmbed:
case ItemString:
const rightLen = right._deleted ? 0 : (right._length - 1)
if (count <= rightLen) {
right = right._splitAt(parent._y, count)
left = right._left
return [left, right, currentAttributes]
}
if (right._deleted === false) {
count -= right._length
}
break
case ItemFormat:
if (right._deleted === false) {
updateCurrentAttributes(currentAttributes, right)
}
break
}
left = right
right = right._right
}
return [left, right, currentAttributes]
}
/**
* @private
*/
function findPosition (parent, index) {
let currentAttributes = new Map()
let left = null
let right = parent._start
return findNextPosition(currentAttributes, parent, left, right, index)
}
/**
* Negate applied formats
*
* @private
*/
function insertNegatedAttributes (y, parent, left, right, negatedAttributes) {
// check if we really need to remove attributes
while (
right !== null && (
right._deleted === true || (
right.constructor === ItemFormat &&
(negatedAttributes.get(right.key) === right.value)
)
)
) {
if (right._deleted === false) {
negatedAttributes.delete(right.key)
}
left = right
right = right._right
}
for (let [key, val] of negatedAttributes) {
let format = new ItemFormat()
format.key = key
format.value = val
integrateItem(format, parent, y, left, right)
left = format
}
return [left, right]
}
/**
* @private
*/
function updateCurrentAttributes (currentAttributes, item) {
const value = item.value
const key = item.key
if (value === null) {
currentAttributes.delete(key)
} else {
currentAttributes.set(key, value)
}
}
/**
* @private
*/
function minimizeAttributeChanges (left, right, currentAttributes, attributes) {
// go right while attributes[right.key] === right.value (or right is deleted)
while (true) {
if (right === null) {
break
} else if (right._deleted === true) {
// continue
} else if (right.constructor === ItemFormat && (attributes[right.key] || null) === right.value) {
// found a format, update currentAttributes and continue
updateCurrentAttributes(currentAttributes, right)
} else {
break
}
left = right
right = right._right
}
return [left, right]
}
/**
* @private
*/
function insertAttributes (y, parent, left, right, attributes, currentAttributes) {
const negatedAttributes = new Map()
// insert format-start items
for (let key in attributes) {
const val = attributes[key]
const currentVal = currentAttributes.get(key)
if (currentVal !== val) {
// save negated attribute (set null if currentVal undefined)
negatedAttributes.set(key, currentVal || null)
let format = new ItemFormat()
format.key = key
format.value = val
integrateItem(format, parent, y, left, right)
left = format
}
}
return [left, right, negatedAttributes]
}
/**
* @private
*/
function insertText (y, text, parent, left, right, currentAttributes, attributes) {
for (let [key] of currentAttributes) {
if (attributes.hasOwnProperty(key) === false) {
attributes[key] = null
}
}
[left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes)
let negatedAttributes
[left, right, negatedAttributes] = insertAttributes(y, parent, left, right, attributes, currentAttributes)
// insert content
let item
if (text.constructor === String) {
item = new ItemString()
item._content = text
} else {
item = new ItemEmbed()
item.embed = text
}
integrateItem(item, parent, y, left, right)
left = item
return insertNegatedAttributes(y, parent, left, right, negatedAttributes)
}
/**
* @private
*/
function formatText (y, length, parent, left, right, currentAttributes, attributes) {
[left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes)
let negatedAttributes
[left, right, negatedAttributes] = insertAttributes(y, parent, left, right, attributes, currentAttributes)
// iterate until first non-format or null is found
// delete all formats with attributes[format.key] != null
while (length > 0 && right !== null) {
if (right._deleted === false) {
switch (right.constructor) {
case ItemFormat:
if (attributes.hasOwnProperty(right.key)) {
if (attributes[right.key] === right.value) {
negatedAttributes.delete(right.key)
} else {
negatedAttributes.set(right.key, right.value)
}
right._delete(y)
}
updateCurrentAttributes(currentAttributes, right)
break
case ItemEmbed:
case ItemString:
right._splitAt(y, length)
length -= right._length
break
}
}
left = right
right = right._right
}
return insertNegatedAttributes(y, parent, left, right, negatedAttributes)
}
/**
* @private
*/
function deleteText (y, length, parent, left, right, currentAttributes) {
while (length > 0 && right !== null) {
if (right._deleted === false) {
switch (right.constructor) {
case ItemFormat:
updateCurrentAttributes(currentAttributes, right)
break
case ItemEmbed:
case ItemString:
right._splitAt(y, length)
length -= right._length
right._delete(y)
break
}
}
left = right
right = right._right
}
return [left, right]
}
// TODO: In the quill delta representation we should also use the format {ops:[..]}
/**
* The Quill Delta format represents changes on a text document with
* formatting information. For mor information visit {@link https://quilljs.com/docs/delta/|Quill Delta}
*
* @example
* {
* ops: [
* { insert: 'Gandalf', attributes: { bold: true } },
* { insert: ' the ' },
* { insert: 'Grey', attributes: { color: '#cccccc' } }
* ]
* }
*
* @typedef {Array<Object>} Delta
*/
/**
* Attributes that can be assigned to a selection of text.
*
* @example
* {
* bold: true,
* font-size: '40px'
* }
*
* @typedef {Object} TextAttributes
*/
/**
* Event that describes the changes on a YText type.
*
* @private
*/
class YTextEvent extends YArrayEvent {
constructor (ytext, remote, transaction) {
super(ytext, remote, transaction)
this._delta = null
}
// TODO: Should put this in a separate function. toDelta shouldn't be included
// in every Yjs distribution
/**
* Compute the changes in the delta format.
*
* @return {Delta} A {@link https://quilljs.com/docs/delta/|Quill Delta}) that
* represents the changes on the document.
*
* @public
*/
get delta () {
if (this._delta === null) {
const y = this.target._y
y.transact(() => {
let item = this.target._start
const delta = []
const added = this.addedElements
const removed = this.removedElements
this._delta = delta
let action = null
let attributes = {} // counts added or removed new attributes for retain
const currentAttributes = new Map() // saves all current attributes for insert
const oldAttributes = new Map()
let insert = ''
let retain = 0
let deleteLen = 0
const addOp = function addOp () {
if (action !== null) {
let op
switch (action) {
case 'delete':
op = { delete: deleteLen }
deleteLen = 0
break
case 'insert':
op = { insert }
if (currentAttributes.size > 0) {
op.attributes = {}
for (let [key, value] of currentAttributes) {
if (value !== null) {
op.attributes[key] = value
}
}
}
insert = ''
break
case 'retain':
op = { retain }
if (Object.keys(attributes).length > 0) {
op.attributes = {}
for (let key in attributes) {
op.attributes[key] = attributes[key]
}
}
retain = 0
break
}
delta.push(op)
action = null
}
}
while (item !== null) {
switch (item.constructor) {
case ItemEmbed:
if (added.has(item)) {
addOp()
action = 'insert'
insert = item.embed
addOp()
} else if (removed.has(item)) {
if (action !== 'delete') {
addOp()
action = 'delete'
}
deleteLen += 1
} else if (item._deleted === false) {
if (action !== 'retain') {
addOp()
action = 'retain'
}
retain += 1
}
break
case ItemString:
if (added.has(item)) {
if (action !== 'insert') {
addOp()
action = 'insert'
}
insert += item._content
} else if (removed.has(item)) {
if (action !== 'delete') {
addOp()
action = 'delete'
}
deleteLen += item._length
} else if (item._deleted === false) {
if (action !== 'retain') {
addOp()
action = 'retain'
}
retain += item._length
}
break
case ItemFormat:
if (added.has(item)) {
const curVal = currentAttributes.get(item.key) || null
if (curVal !== item.value) {
if (action === 'retain') {
addOp()
}
if (item.value === (oldAttributes.get(item.key) || null)) {
delete attributes[item.key]
} else {
attributes[item.key] = item.value
}
} else {
item._delete(y)
}
} else if (removed.has(item)) {
oldAttributes.set(item.key, item.value)
const curVal = currentAttributes.get(item.key) || null
if (curVal !== item.value) {
if (action === 'retain') {
addOp()
}
attributes[item.key] = curVal
}
} else if (item._deleted === false) {
oldAttributes.set(item.key, item.value)
if (attributes.hasOwnProperty(item.key)) {
if (attributes[item.key] !== item.value) {
if (action === 'retain') {
addOp()
}
if (item.value === null) {
attributes[item.key] = item.value
} else {
delete attributes[item.key]
}
} else {
item._delete(y)
}
}
}
if (item._deleted === false) {
if (action === 'insert') {
addOp()
}
updateCurrentAttributes(currentAttributes, item)
}
break
}
item = item._right
}
addOp()
while (this._delta.length > 0) {
let lastOp = this._delta[this._delta.length - 1]
if (lastOp.hasOwnProperty('retain') && !lastOp.hasOwnProperty('attributes')) {
// retain delta's if they don't assign attributes
this._delta.pop()
} else {
break
}
}
})
}
return this._delta
}
}
/**
* Type that represents text with formatting information.
*
* This type replaces y-richtext as this implementation is able to handle
* block formats (format information on a paragraph), embeds (complex elements
* like pictures and videos), and text formats (**bold**, *italic*).
*
* @param {String} string The initial value of the YText.
*/
export default class YText extends YArray {
constructor (string) {
super()
if (typeof string === 'string') {
const start = new ItemString()
start._parent = this
start._content = string
this._start = start
}
}
/**
* @private
* Creates YMap Event and calls observers.
*/
_callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YTextEvent(this, remote, transaction))
}
/**
* Returns the unformatted string representation of this YText type.
*
* @public
*/
toString () {
let str = ''
let n = this._start
while (n !== null) {
if (!n._deleted && n._countable) {
str += n._content
}
n = n._right
}
return str
}
/**
* Apply a {@link Delta} on this shared YText type.
*
* @param {Delta} delta The changes to apply on this element.
*
* @public
*/
applyDelta (delta) {
this._transact(y => {
let left = null
let right = this._start
const currentAttributes = new Map()
for (let i = 0; i < delta.length; i++) {
let op = delta[i]
if (op.hasOwnProperty('insert')) {
;[left, right] = insertText(y, op.insert, this, left, right, currentAttributes, op.attributes || {})
} else if (op.hasOwnProperty('retain')) {
;[left, right] = formatText(y, op.retain, this, left, right, currentAttributes, op.attributes || {})
} else if (op.hasOwnProperty('delete')) {
;[left, right] = deleteText(y, op.delete, this, left, right, currentAttributes)
}
}
})
}
/**
* Returns the Delta representation of this YText type.
*
* @return {Delta} The Delta representation of this type.
*
* @public
*/
toDelta () {
let ops = []
let currentAttributes = new Map()
let str = ''
let n = this._start
function packStr () {
if (str.length > 0) {
// pack str with attributes to ops
let attributes = {}
let addAttributes = false
for (let [key, value] of currentAttributes) {
addAttributes = true
attributes[key] = value
}
let op = { insert: str }
if (addAttributes) {
op.attributes = attributes
}
ops.push(op)
str = ''
}
}
while (n !== null) {
if (!n._deleted) {
switch (n.constructor) {
case ItemString:
str += n._content
break
case ItemFormat:
packStr()
updateCurrentAttributes(currentAttributes, n)
break
}
}
n = n._right
}
packStr()
return ops
}
/**
* Insert text at a given index.
*
* @param {Integer} index The index at which to start inserting.
* @param {String} text The text to insert at the specified position.
* @param {TextAttributes} attributes Optionally define some formatting
* information to apply on the inserted
* Text.
*
* @public
*/
insert (index, text, attributes = {}) {
if (text.length <= 0) {
return
}
this._transact(y => {
let [left, right, currentAttributes] = findPosition(this, index)
insertText(y, text, this, left, right, currentAttributes, attributes)
})
}
/**
* Inserts an embed at a index.
*
* @param {Integer} index The index to insert the embed at.
* @param {Object} embed The Object that represents the embed.
* @param {TextAttributes} attributes Attribute information to apply on the
* embed
*
* @public
*/
insertEmbed (index, embed, attributes = {}) {
if (embed.constructor !== Object) {
throw new Error('Embed must be an Object')
}
this._transact(y => {
let [left, right, currentAttributes] = findPosition(this, index)
insertText(y, embed, this, left, right, currentAttributes, attributes)
})
}
/**
* Deletes text starting from an index.
*
* @param {Integer} index Index at which to start deleting.
* @param {Integer} length The number of characters to remove. Defaults to 1.
*
* @public
*/
delete (index, length) {
if (length === 0) {
return
}
this._transact(y => {
let [left, right, currentAttributes] = findPosition(this, index)
deleteText(y, length, this, left, right, currentAttributes)
})
}
/**
* Assigns properties to a range of text.
*
* @param {Integer} index The position where to start formatting.
* @param {Integer} length The amount of characters to assign properties to.
* @param {TextAttributes} attributes Attribute information to apply on the
* text.
*
* @public
*/
format (index, length, attributes) {
this._transact(y => {
let [left, right, currentAttributes] = findPosition(this, index)
if (right === null) {
return
}
formatText(y, length, this, left, right, currentAttributes, attributes)
})
}
// TODO: De-duplicate code. The following code is in every type.
/**
* Transform this YText to a readable format.
* Useful for logging as all Items implement this method.
*
* @private
*/
_logString () {
return logItemHelper('YText', this)
}
}

View File

@@ -1,12 +0,0 @@
import YXmlFragment from './YXmlFragment.js'
import YXmlElement from './YXmlElement.js'
import YXmlHook from './YXmlHook.js'
export { default as YXmlFragment } from './YXmlFragment.js'
export { default as YXmlElement } from './YXmlElement.js'
export { default as YXmlText } from './YXmlText.js'
export { default as YXmlHook } from './YXmlHook.js'
YXmlFragment._YXmlElement = YXmlElement
YXmlFragment._YXmlHook = YXmlHook

View File

@@ -1,188 +0,0 @@
import YMap from '../YMap/YMap.js'
import { YXmlFragment } from './YXml.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
/**
* An YXmlElement imitates the behavior of a
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}.
*
* * An YXmlElement has attributes (key value pairs)
* * An YXmlElement has childElements that must inherit from YXmlElement
*
* @param {String} nodeName Node name
*/
export default class YXmlElement extends YXmlFragment {
constructor (nodeName = 'UNDEFINED') {
super()
this.nodeName = nodeName.toUpperCase()
}
/**
* @private
* Creates an Item with the same effect as this Item (without position effect)
*/
_copy () {
let struct = super._copy()
struct.nodeName = this.nodeName
return struct
}
/**
* @private
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
*/
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.nodeName = decoder.readVarString()
return missing
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this.nodeName)
}
/**
* Integrates this Item into the shared structure.
*
* This method actually applies the change to the Yjs instance. In case of
* Item it connects _left and _right to this Item and calls the
* {@link Item#beforeChange} method.
*
* * Checks for nodeName
* * Sets domFilter
*
* @param {Y} y The Yjs instance
*
* @private
*/
_integrate (y) {
if (this.nodeName === null) {
throw new Error('nodeName must be defined!')
}
super._integrate(y)
}
/**
* Returns the string representation of this YXmlElement.
* The attributes are ordered by attribute-name, so you can easily use this
* method to compare YXmlElements
*
* @return {String} The string representation of this type.
*
* @public
*/
toString () {
const attrs = this.getAttributes()
const stringBuilder = []
const keys = []
for (let key in attrs) {
keys.push(key)
}
keys.sort()
const keysLen = keys.length
for (let i = 0; i < keysLen; i++) {
const key = keys[i]
stringBuilder.push(key + '="' + attrs[key] + '"')
}
const nodeName = this.nodeName.toLocaleLowerCase()
const attrsString = stringBuilder.length > 0 ? ' ' + stringBuilder.join(' ') : ''
return `<${nodeName}${attrsString}>${super.toString()}</${nodeName}>`
}
/**
* Removes an attribute from this YXmlElement.
*
* @param {String} attributeName The attribute name that is to be removed.
*
* @public
*/
removeAttribute (attributeName) {
return YMap.prototype.delete.call(this, attributeName)
}
/**
* Sets or updates an attribute.
*
* @param {String} attributeName The attribute name that is to be set.
* @param {String} attributeValue The attribute value that is to be set.
*
* @public
*/
setAttribute (attributeName, attributeValue) {
return YMap.prototype.set.call(this, attributeName, attributeValue)
}
/**
* Returns an attribute value that belongs to the attribute name.
*
* @param {String} attributeName The attribute name that identifies the
* queried value.
* @return {String} The queried attribute value.
*
* @public
*/
getAttribute (attributeName) {
return YMap.prototype.get.call(this, attributeName)
}
/**
* Returns all attribute name/value pairs in a JSON Object.
*
* @return {Object} A JSON Object that describes the attributes.
*
* @public
*/
getAttributes () {
const obj = {}
for (let [key, value] of this._map) {
if (!value._deleted) {
obj[key] = value._content[0]
}
}
return obj
}
// TODO: outsource the binding property.
/**
* Creates a Dom Element that mirrors this YXmlElement.
*
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/
toDom (_document = document, hooks = {}, binding) {
const dom = _document.createElement(this.nodeName)
let attrs = this.getAttributes()
for (let key in attrs) {
dom.setAttribute(key, attrs[key])
}
this.forEach(yxml => {
dom.appendChild(yxml.toDom(_document, hooks, binding))
})
createAssociation(binding, dom, this)
return dom
}
}

View File

@@ -1,47 +0,0 @@
import YEvent from '../../Util/YEvent.js'
/**
* An Event that describes changes on a YXml Element or Yxml Fragment
*
* @protected
*/
export default class YXmlEvent extends YEvent {
/**
* @param {YType} target The target on which the event is created.
* @param {Set} subs The set of changed attributes. `null` is included if the
* child list changed.
* @param {Boolean} remote Whether this change was created by a remote peer.
* @param {Transaction} transaction The transaction instance with wich the
* change was created.
*/
constructor (target, subs, remote, transaction) {
super(target)
/**
* The transaction instance for the computed change.
* @type {Transaction}
*/
this._transaction = transaction
/**
* Whether the children changed.
* @type {Boolean}
*/
this.childListChanged = false
/**
* Set of all changed attributes.
* @type {Set}
*/
this.attributesChanged = new Set()
/**
* Whether this change was created by a remote peer.
* @type {Boolean}
*/
this.remote = remote
subs.forEach((sub) => {
if (sub === null) {
this.childListChanged = true
} else {
this.attributesChanged.add(sub)
}
})
}
}

View File

@@ -1,167 +0,0 @@
import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YXmlTreeWalker from './YXmlTreeWalker.js'
import YArray from '../YArray/YArray.js'
import YXmlEvent from './YXmlEvent.js'
import { logItemHelper } from '../../MessageHandler/messageToString.js'
/**
* Dom filter function.
*
* @callback domFilter
* @param {string} nodeName The nodeName of the element
* @param {Map} attributes The map of attributes.
* @return {boolean} Whether to include the Dom node in the YXmlElement.
*/
/**
* Define the elements to which a set of CSS queries apply.
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
*
* @example
* query = '.classSelector'
* query = 'nodeSelector'
* query = '#idSelector'
*
* @typedef {string} CSS_Selector
*/
/**
* Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
* A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
* nodeName and it does not have attributes. Though it can be bound to a DOM
* element - in this case the attributes and the nodeName are not shared.
*
* @public
*/
export default class YXmlFragment extends YArray {
/**
* Create a subtree of childNodes.
*
* @example
* const walker = elem.createTreeWalker(dom => dom.nodeName === 'div')
* for (let node in walker) {
* // `node` is a div node
* nop(node)
* }
*
* @param {Function} filter Function that is called on each child element and
* returns a Boolean indicating whether the child
* is to be included in the subtree.
* @return {TreeWalker} A subtree and a position within it.
*
* @public
*/
createTreeWalker (filter) {
return new YXmlTreeWalker(this, filter)
}
/**
* Returns the first YXmlElement that matches the query.
* Similar to DOM's {@link querySelector}.
*
* Query support:
* - tagname
* TODO:
* - id
* - attribute
*
* @param {CSS_Selector} query The query on the children.
* @return {?YXmlElement} The first element that matches the query or null.
*
* @public
*/
querySelector (query) {
query = query.toUpperCase()
const iterator = new YXmlTreeWalker(this, element => element.nodeName === query)
const next = iterator.next()
if (next.done) {
return null
} else {
return next.value
}
}
/**
* Returns all YXmlElements that match the query.
* Similar to Dom's {@link querySelectorAll}.
*
* TODO: Does not yet support all queries. Currently only query by tagName.
*
* @param {CSS_Selector} query The query on the children
* @return {Array<YXmlElement>} The elements that match this query.
*
* @public
*/
querySelectorAll (query) {
query = query.toUpperCase()
return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
}
/**
* Creates YArray Event and calls observers.
*
* @private
*/
_callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction))
}
/**
* Get the string representation of all the children of this YXmlFragment.
*
* @return {string} The string representation of all children.
*/
toString () {
return this.map(xml => xml.toString()).join('')
}
/**
* @private
* Unbind from Dom and mark this Item as deleted.
*
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
* collect the children of this type.
*
* @private
*/
_delete (y, createDelete, gcChildren) {
super._delete(y, createDelete, gcChildren)
}
/**
* Creates a Dom Element that mirrors this YXmlElement.
*
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {Object<key:hookDefinition>} [hooks={}] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/
toDom (_document = document, hooks = {}, binding) {
const fragment = _document.createDocumentFragment()
createAssociation(binding, fragment, this)
this.forEach(xmlType => {
fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
})
return fragment
}
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () {
return logItemHelper('YXml', this)
}
}

View File

@@ -1,108 +0,0 @@
import YMap from '../YMap/YMap.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
/**
* You can manage binding to a custom type with YXmlHook.
*
* @public
*/
export default class YXmlHook extends YMap {
/**
* @param {String} hookName nodeName of the Dom Node.
*/
constructor (hookName) {
super()
this.hookName = null
if (hookName !== undefined) {
this.hookName = hookName
}
}
/**
* Creates an Item with the same effect as this Item (without position effect)
*
* @private
*/
_copy () {
const struct = super._copy()
struct.hookName = this.hookName
return struct
}
/**
* Creates a Dom Element that mirrors this YXmlElement.
*
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/
toDom (_document = document, hooks = {}, binding) {
const hook = hooks[this.hookName]
let dom
if (hook !== undefined) {
dom = hook.createDom(this)
} else {
dom = document.createElement(this.hookName)
}
dom.setAttribute('data-yjs-hook', this.hookName)
createAssociation(binding, dom, this)
return dom
}
/**
* Read the next Item in a Decoder and fill this Item with the read data.
*
* This is called when data is received from a remote peer.
*
* @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from.
*
* @private
*/
_fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder)
this.hookName = decoder.readVarString()
return missing
}
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
*
* This is called when this Item is sent to a remote peer.
*
* @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/
_toBinary (encoder) {
super._toBinary(encoder)
encoder.writeVarString(this.hookName)
}
/**
* Integrate this type into the Yjs instance.
*
* * Save this struct in the os
* * This type is sent to other client
* * Observer functions are fired
*
* @param {Y} y The Yjs instance
*
* @private
*/
_integrate (y) {
if (this.hookName === null) {
throw new Error('hookName must be defined!')
}
super._integrate(y)
}
}

View File

@@ -1,46 +0,0 @@
import YText from '../YText/YText.js'
import { createAssociation } from '../../Bindings/DomBinding/util.js'
/**
* Represents text in a Dom Element. In the future this type will also handle
* simple formatting information like bold and italic.
*
* @param {String} arg1 Initial value.
*/
export default class YXmlText extends YText {
/**
* Creates a Dom Element that mirrors this YXmlText.
*
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {Object<key:hookDefinition>} [hooks] Optional property to customize how hooks
* are presented in the DOM
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/
toDom (_document = document, hooks, binding) {
const dom = _document.createTextNode(this.toString())
createAssociation(binding, dom, this)
return dom
}
/**
* Mark this Item as deleted.
*
* @param {Y} y The Yjs instance
* @param {boolean} createDelete Whether to propagate a message that this
* Type was deleted.
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
* collect the children of this type.
*
* @private
*/
_delete (y, createDelete, gcChildren) {
super._delete(y, createDelete, gcChildren)
}
}

View File

@@ -1,76 +0,0 @@
import YXmlFragment from './YXmlFragment.js'
/**
* Define the elements to which a set of CSS queries apply.
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
*
* @example
* query = '.classSelector'
* query = 'nodeSelector'
* query = '#idSelector'
*
* @typedef {string} CSS_Selector
*/
/**
* Represents a subset of the nodes of a YXmlElement / YXmlFragment and a
* position within them.
*
* Can be created with {@link YXmlFragment#createTreeWalker}
*
* @public
*/
export default class YXmlTreeWalker {
constructor (root, f) {
this._filter = f || (() => true)
this._root = root
this._currentNode = root
this._firstCall = true
}
[Symbol.iterator] () {
return this
}
/**
* Get the next node.
*
* @return {YXmlElement} The next node.
*
* @public
*/
next () {
let n = this._currentNode
if (this._firstCall) {
this._firstCall = false
if (!n._deleted && this._filter(n)) {
return { value: n, done: false }
}
}
do {
if (!n._deleted && (n.constructor === YXmlFragment._YXmlElement || n.constructor === YXmlFragment) && n._start !== null) {
// walk down in the tree
n = n._start
} else {
// walk right or up in the tree
while (n !== this._root) {
if (n._right !== null) {
n = n._right
break
}
n = n._parent
}
if (n === this._root) {
n = null
}
}
if (n === this._root) {
break
}
} while (n !== null && (n._deleted || !this._filter(n)))
this._currentNode = n
if (n === null) {
return { done: true }
} else {
return { value: n, done: false }
}
}
}

View File

@@ -1,151 +0,0 @@
import ID from '../ID/ID.js'
import { default as RootID, RootFakeUserID } from '../ID/RootID.js'
/**
* A BinaryDecoder handles the decoding of an ArrayBuffer.
*/
export default class BinaryDecoder {
/**
* @param {Uint8Array|Buffer} buffer The binary data that this instance
* decodes.
*/
constructor (buffer) {
if (buffer instanceof ArrayBuffer) {
this.uint8arr = new Uint8Array(buffer)
} else if (
buffer instanceof Uint8Array ||
(
typeof Buffer !== 'undefined' && buffer instanceof Buffer
)
) {
this.uint8arr = buffer
} else {
throw new Error('Expected an ArrayBuffer or Uint8Array!')
}
this.pos = 0
}
/**
* Clone this decoder instance.
* Optionally set a new position parameter.
*/
clone (newPos = this.pos) {
let decoder = new BinaryDecoder(this.uint8arr)
decoder.pos = newPos
return decoder
}
/**
* Number of bytes.
*/
get length () {
return this.uint8arr.length
}
/**
* Skip one byte, jump to the next position.
*/
skip8 () {
this.pos++
}
/**
* Read one byte as unsigned integer.
*/
readUint8 () {
return this.uint8arr[this.pos++]
}
/**
* Read 4 bytes as unsigned integer.
*
* @return {number} An unsigned integer.
*/
readUint32 () {
let uint =
this.uint8arr[this.pos] +
(this.uint8arr[this.pos + 1] << 8) +
(this.uint8arr[this.pos + 2] << 16) +
(this.uint8arr[this.pos + 3] << 24)
this.pos += 4
return uint
}
/**
* Look ahead without incrementing position.
* to the next byte and read it as unsigned integer.
*
* @return {number} An unsigned integer.
*/
peekUint8 () {
return this.uint8arr[this.pos]
}
/**
* Read unsigned integer (32bit) with variable length.
* 1/8th of the storage is used as encoding overhead.
* * numbers < 2^7 is stored in one byte.
* * numbers < 2^14 is stored in two bytes.
*
* @return {number} An unsigned integer.
*/
readVarUint () {
let num = 0
let len = 0
while (true) {
let r = this.uint8arr[this.pos++]
num = num | ((r & 0b1111111) << len)
len += 7
if (r < 1 << 7) {
return num >>> 0 // return unsigned number!
}
if (len > 35) {
throw new Error('Integer out of range!')
}
}
}
/**
* Read string of variable length
* * varUint is used to store the length of the string
*
* @return {String} The read String.
*/
readVarString () {
let len = this.readVarUint()
let bytes = new Array(len)
for (let i = 0; i < len; i++) {
bytes[i] = this.uint8arr[this.pos++]
}
let encodedString = bytes.map(b => String.fromCodePoint(b)).join('')
return decodeURIComponent(escape(encodedString))
}
/**
* Look ahead and read varString without incrementing position
*/
peekVarString () {
let pos = this.pos
let s = this.readVarString()
this.pos = pos
return s
}
/**
* Read ID.
* * If first varUint read is 0xFFFFFF a RootID is returned.
* * Otherwise an ID is returned.
*
* @return ID
*/
readID () {
let user = this.readVarUint()
if (user === RootFakeUserID) {
// read property name and type id
const rid = new RootID(this.readVarString(), null)
rid.type = this.readVarUint()
return rid
}
return new ID(user, this.readVarUint())
}
}

View File

@@ -1,145 +0,0 @@
import { RootFakeUserID } from '../ID/RootID.js'
const bits7 = 0b1111111
const bits8 = 0b11111111
/**
* A BinaryEncoder handles the encoding to an ArrayBuffer.
*/
export default class BinaryEncoder {
constructor () {
// TODO: implement chained Uint8Array buffers instead of Array buffer
// TODO: Rewrite all methods as functions!
this.data = []
}
/**
* The current length of the encoded data.
*/
get length () {
return this.data.length
}
/**
* The current write pointer (the same as {@link length}).
*/
get pos () {
return this.data.length
}
/**
* Create an ArrayBuffer.
*
* @return {Uint8Array} A Uint8Array that represents the written data.
*/
createBuffer () {
return Uint8Array.from(this.data).buffer
}
/**
* Write one byte as an unsigned integer.
*
* @param {number} num The number that is to be encoded.
*/
writeUint8 (num) {
this.data.push(num & bits8)
}
/**
* Write one byte as an unsigned Integer at a specific location.
*
* @param {number} pos The location where the data will be written.
* @param {number} num The number that is to be encoded.
*/
setUint8 (pos, num) {
this.data[pos] = num & bits8
}
/**
* Write two bytes as an unsigned integer.
*
* @param {number} num The number that is to be encoded.
*/
writeUint16 (num) {
this.data.push(num & bits8, (num >>> 8) & bits8)
}
/**
* Write two bytes as an unsigned integer at a specific location.
*
* @param {number} pos The location where the data will be written.
* @param {number} num The number that is to be encoded.
*/
setUint16 (pos, num) {
this.data[pos] = num & bits8
this.data[pos + 1] = (num >>> 8) & bits8
}
/**
* Write two bytes as an unsigned integer
*
* @param {number} num The number that is to be encoded.
*/
writeUint32 (num) {
for (let i = 0; i < 4; i++) {
this.data.push(num & bits8)
num >>>= 8
}
}
/**
* Write two bytes as an unsigned integer at a specific location.
*
* @param {number} pos The location where the data will be written.
* @param {number} num The number that is to be encoded.
*/
setUint32 (pos, num) {
for (let i = 0; i < 4; i++) {
this.data[pos + i] = num & bits8
num >>>= 8
}
}
/**
* Write a variable length unsigned integer.
*
* @param {number} num The number that is to be encoded.
*/
writeVarUint (num) {
while (num >= 0b10000000) {
this.data.push(0b10000000 | (bits7 & num))
num >>>= 7
}
this.data.push(bits7 & num)
}
/**
* Write a variable length string.
*
* @param {String} str The string that is to be encoded.
*/
writeVarString (str) {
let encodedString = unescape(encodeURIComponent(str))
let bytes = encodedString.split('').map(c => c.codePointAt())
let len = bytes.length
this.writeVarUint(len)
for (let i = 0; i < len; i++) {
this.data.push(bytes[i])
}
}
/**
* Write an ID at the current position.
*
* @param {ID} id The ID that is to be written.
*/
writeID (id) {
const user = id.user
this.writeVarUint(user)
if (user !== RootFakeUserID) {
this.writeVarUint(id.clock)
} else {
this.writeVarString(id.name)
this.writeVarUint(id.type)
}
}
}

View File

@@ -1,69 +0,0 @@
/**
* General event handler implementation.
*/
export default class EventHandler {
constructor () {
this.eventListeners = []
}
/**
* To prevent memory leaks, call this method when the eventListeners won't be
* used anymore.
*/
destroy () {
this.eventListeners = null
}
/**
* Adds an event listener that is called when
* {@link EventHandler#callEventListeners} is called.
*
* @param {Function} f The event handler.
*/
addEventListener (f) {
this.eventListeners.push(f)
}
/**
* Removes an event listener.
*
* @param {Function} f The event handler that was added with
* {@link EventHandler#addEventListener}
*/
removeEventListener (f) {
this.eventListeners = this.eventListeners.filter(function (g) {
return f !== g
})
}
/**
* Removes all event listeners.
*/
removeAllEventListeners () {
this.eventListeners = []
}
/**
* Call all event listeners that were added via
* {@link EventHandler#addEventListener}.
*
* @param {Transaction} transaction The transaction object // TODO: do we need this?
* @param {YEvent} event An event object that describes the change on a type.
*/
callEventListeners (transaction, event) {
for (var i = 0; i < this.eventListeners.length; i++) {
try {
const f = this.eventListeners[i]
f(event)
} catch (e) {
/*
Your observer threw an error. This error was caught so that Yjs
can ensure data consistency! In order to debug this error you
have to check "Pause On Caught Exceptions" in developer tools.
*/
console.error(e)
}
}
}
}

View File

@@ -1,20 +0,0 @@
export default class ID {
constructor (user, clock) {
this.user = user // TODO: rename to client
this.clock = clock
}
clone () {
return new ID(this.user, this.clock)
}
equals (id) {
return id !== null && id.user === this.user && id.clock === this.clock
}
lessThan (id) {
if (id.constructor === ID) {
return this.user < id.user || (this.user === id.user && this.clock < id.clock)
} else {
return false
}
}
}

View File

@@ -1,21 +0,0 @@
import { getStructReference } from '../structReferences.js'
export const RootFakeUserID = 0xFFFFFF
export default class RootID {
constructor (name, typeConstructor) {
this.user = RootFakeUserID
this.name = name
this.type = getStructReference(typeConstructor)
}
equals (id) {
return id !== null && id.user === this.user && id.name === this.name && id.type === this.type
}
lessThan (id) {
if (id.constructor === RootID) {
return this.user < id.user || (this.user === id.user && (this.name < id.name || (this.name === id.name && this.type < id.type)))
} else {
return true
}
}
}

View File

@@ -1,113 +0,0 @@
/**
* Handles named events.
*/
export default class NamedEventHandler {
constructor () {
this._eventListener = new Map()
this._stateListener = new Map()
}
/**
* @private
* Returns all listeners that listen to a specified name.
*
* @param {String} name The query event name.
*/
_getListener (name) {
let listeners = this._eventListener.get(name)
if (listeners === undefined) {
listeners = {
once: new Set(),
on: new Set()
}
this._eventListener.set(name, listeners)
}
return listeners
}
/**
* Adds a named event listener. The listener is removed after it has been
* called once.
*
* @param {String} name The event name to listen to.
* @param {Function} f The function that is executed when the event is fired.
*/
once (name, f) {
let listeners = this._getListener(name)
listeners.once.add(f)
}
/**
* Adds a named event listener.
*
* @param {String} name The event name to listen to.
* @param {Function} f The function that is executed when the event is fired.
*/
on (name, f) {
let listeners = this._getListener(name)
listeners.on.add(f)
}
/**
* @private
* Init the saved state for an event name.
*/
_initStateListener (name) {
let state = this._stateListener.get(name)
if (state === undefined) {
state = {}
state.promise = new Promise(function (resolve) {
state.resolve = resolve
})
this._stateListener.set(name, state)
}
return state
}
/**
* Returns a Promise that is resolved when the event name is called.
* The Promise is immediately resolved when the event name was called in the
* past.
*/
when (name) {
return this._initStateListener(name).promise
}
/**
* Remove an event listener that was registered with either
* {@link EventHandler#on} or {@link EventHandler#once}.
*/
off (name, f) {
if (name == null || f == null) {
throw new Error('You must specify event name and function!')
}
const listener = this._eventListener.get(name)
if (listener !== undefined) {
listener.on.delete(f)
listener.once.delete(f)
}
}
/**
* Emit a named event. All registered event listeners that listen to the
* specified name will receive the event.
*
* @param {String} name The event name.
* @param {Array} args The arguments that are applied to the event listener.
*/
emit (name, ...args) {
this._initStateListener(name).resolve()
const listener = this._eventListener.get(name)
if (listener !== undefined) {
listener.on.forEach(f => f.apply(null, args))
listener.once.forEach(f => f.apply(null, args))
listener.once = new Set()
} else if (name === 'error') {
console.error(args[0])
}
}
destroy () {
this._eventListener = null
}
}

View File

@@ -1,465 +0,0 @@
function rotate (tree, parent, newParent, n) {
if (parent === null) {
tree.root = newParent
newParent._parent = null
} else if (parent.left === n) {
parent.left = newParent
} else if (parent.right === n) {
parent.right = newParent
} else {
throw new Error('The elements are wrongly connected!')
}
}
class N {
// A created node is always red!
constructor (val) {
this.val = val
this.color = true
this._left = null
this._right = null
this._parent = null
}
isRed () { return this.color }
isBlack () { return !this.color }
redden () { this.color = true; return this }
blacken () { this.color = false; return this }
get grandparent () {
return this.parent.parent
}
get parent () {
return this._parent
}
get sibling () {
return (this === this.parent.left)
? this.parent.right : this.parent.left
}
get left () {
return this._left
}
get right () {
return this._right
}
set left (n) {
if (n !== null) {
n._parent = this
}
this._left = n
}
set right (n) {
if (n !== null) {
n._parent = this
}
this._right = n
}
rotateLeft (tree) {
const parent = this.parent
const newParent = this.right
const newRight = this.right.left
newParent.left = this
this.right = newRight
rotate(tree, parent, newParent, this)
}
next () {
if (this.right !== null) {
// search the most left node in the right tree
var o = this.right
while (o.left !== null) {
o = o.left
}
return o
} else {
var p = this
while (p.parent !== null && p !== p.parent.left) {
p = p.parent
}
return p.parent
}
}
prev () {
if (this.left !== null) {
// search the most right node in the left tree
var o = this.left
while (o.right !== null) {
o = o.right
}
return o
} else {
var p = this
while (p.parent !== null && p !== p.parent.right) {
p = p.parent
}
return p.parent
}
}
rotateRight (tree) {
const parent = this.parent
const newParent = this.left
const newLeft = this.left.right
newParent.right = this
this.left = newLeft
rotate(tree, parent, newParent, this)
}
getUncle () {
// we can assume that grandparent exists when this is called!
if (this.parent === this.parent.parent.left) {
return this.parent.parent.right
} else {
return this.parent.parent.left
}
}
}
/*
* This is a Red Black Tree implementation
*/
export default class Tree {
constructor () {
this.root = null
this.length = 0
}
findNext (id) {
var nextID = id.clone()
nextID.clock += 1
return this.findWithLowerBound(nextID)
}
findPrev (id) {
let prevID = id.clone()
prevID.clock -= 1
return this.findWithUpperBound(prevID)
}
findNodeWithLowerBound (from) {
var o = this.root
if (o === null) {
return null
} else {
while (true) {
if (from === null || (from.lessThan(o.val._id) && o.left !== null)) {
// o is included in the bound
// try to find an element that is closer to the bound
o = o.left
} else if (from !== null && o.val._id.lessThan(from)) {
// o is not within the bound, maybe one of the right elements is..
if (o.right !== null) {
o = o.right
} else {
// there is no right element. Search for the next bigger element,
// this should be within the bounds
return o.next()
}
} else {
return o
}
}
}
}
findNodeWithUpperBound (to) {
if (to === void 0) {
throw new Error('You must define from!')
}
var o = this.root
if (o === null) {
return null
} else {
while (true) {
if ((to === null || o.val._id.lessThan(to)) && o.right !== null) {
// o is included in the bound
// try to find an element that is closer to the bound
o = o.right
} else if (to !== null && to.lessThan(o.val._id)) {
// o is not within the bound, maybe one of the left elements is..
if (o.left !== null) {
o = o.left
} else {
// there is no left element. Search for the prev smaller element,
// this should be within the bounds
return o.prev()
}
} else {
return o
}
}
}
}
findSmallestNode () {
var o = this.root
while (o != null && o.left != null) {
o = o.left
}
return o
}
findWithLowerBound (from) {
var n = this.findNodeWithLowerBound(from)
return n == null ? null : n.val
}
findWithUpperBound (to) {
var n = this.findNodeWithUpperBound(to)
return n == null ? null : n.val
}
iterate (from, to, f) {
var o
if (from === null) {
o = this.findSmallestNode()
} else {
o = this.findNodeWithLowerBound(from)
}
while (
o !== null &&
(
to === null || // eslint-disable-line no-unmodified-loop-condition
o.val._id.lessThan(to) ||
o.val._id.equals(to)
)
) {
f(o.val)
o = o.next()
}
}
find (id) {
let n = this.findNode(id)
if (n !== null) {
return n.val
} else {
return null
}
}
findNode (id) {
var o = this.root
if (o === null) {
return null
} else {
while (true) {
if (o === null) {
return null
}
if (id.lessThan(o.val._id)) {
o = o.left
} else if (o.val._id.lessThan(id)) {
o = o.right
} else {
return o
}
}
}
}
delete (id) {
var d = this.findNode(id)
if (d == null) {
// throw new Error('Element does not exist!')
return
}
this.length--
if (d.left !== null && d.right !== null) {
// switch d with the greates element in the left subtree.
// o should have at most one child.
var o = d.left
// find
while (o.right !== null) {
o = o.right
}
// switch
d.val = o.val
d = o
}
// d has at most one child
// let n be the node that replaces d
var isFakeChild
var child = d.left || d.right
if (child === null) {
isFakeChild = true
child = new N(null)
child.blacken()
d.right = child
} else {
isFakeChild = false
}
if (d.parent === null) {
if (!isFakeChild) {
this.root = child
child.blacken()
child._parent = null
} else {
this.root = null
}
return
} else if (d.parent.left === d) {
d.parent.left = child
} else if (d.parent.right === d) {
d.parent.right = child
} else {
throw new Error('Impossible!')
}
if (d.isBlack()) {
if (child.isRed()) {
child.blacken()
} else {
this._fixDelete(child)
}
}
this.root.blacken()
if (isFakeChild) {
if (child.parent.left === child) {
child.parent.left = null
} else if (child.parent.right === child) {
child.parent.right = null
} else {
throw new Error('Impossible #3')
}
}
}
_fixDelete (n) {
function isBlack (node) {
return node !== null ? node.isBlack() : true
}
function isRed (node) {
return node !== null ? node.isRed() : false
}
if (n.parent === null) {
// this can only be called after the first iteration of fixDelete.
return
}
// d was already replaced by the child
// d is not the root
// d and child are black
var sibling = n.sibling
if (isRed(sibling)) {
// make sibling the grandfather
n.parent.redden()
sibling.blacken()
if (n === n.parent.left) {
n.parent.rotateLeft(this)
} else if (n === n.parent.right) {
n.parent.rotateRight(this)
} else {
throw new Error('Impossible #2')
}
sibling = n.sibling
}
// parent, sibling, and children of n are black
if (n.parent.isBlack() &&
sibling.isBlack() &&
isBlack(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
this._fixDelete(n.parent)
} else if (n.parent.isRed() &&
sibling.isBlack() &&
isBlack(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
n.parent.blacken()
} else {
if (n === n.parent.left &&
sibling.isBlack() &&
isRed(sibling.left) &&
isBlack(sibling.right)
) {
sibling.redden()
sibling.left.blacken()
sibling.rotateRight(this)
sibling = n.sibling
} else if (n === n.parent.right &&
sibling.isBlack() &&
isRed(sibling.right) &&
isBlack(sibling.left)
) {
sibling.redden()
sibling.right.blacken()
sibling.rotateLeft(this)
sibling = n.sibling
}
sibling.color = n.parent.color
n.parent.blacken()
if (n === n.parent.left) {
sibling.right.blacken()
n.parent.rotateLeft(this)
} else {
sibling.left.blacken()
n.parent.rotateRight(this)
}
}
}
put (v) {
var node = new N(v)
if (this.root !== null) {
var p = this.root // p abbrev. parent
while (true) {
if (node.val._id.lessThan(p.val._id)) {
if (p.left === null) {
p.left = node
break
} else {
p = p.left
}
} else if (p.val._id.lessThan(node.val._id)) {
if (p.right === null) {
p.right = node
break
} else {
p = p.right
}
} else {
p.val = node.val
return p
}
}
this._fixInsert(node)
} else {
this.root = node
}
this.length++
this.root.blacken()
return node
}
_fixInsert (n) {
if (n.parent === null) {
n.blacken()
return
} else if (n.parent.isBlack()) {
return
}
var uncle = n.getUncle()
if (uncle !== null && uncle.isRed()) {
// Note: parent: red, uncle: red
n.parent.blacken()
uncle.blacken()
n.grandparent.redden()
this._fixInsert(n.grandparent)
} else {
// Note: parent: red, uncle: black or null
// Now we transform the tree in such a way that
// either of these holds:
// 1) grandparent.left.isRed
// and grandparent.left.left.isRed
// 2) grandparent.right.isRed
// and grandparent.right.right.isRed
if (n === n.parent.right && n.parent === n.grandparent.left) {
n.parent.rotateLeft(this)
// Since we rotated and want to use the previous
// cases, we need to set n in such a way that
// n.parent.isRed again
n = n.left
} else if (n === n.parent.left && n.parent === n.grandparent.right) {
n.parent.rotateRight(this)
// see above
n = n.right
}
// Case 1) or 2) hold from here on.
// Now traverse grandparent, make parent a black node
// on the highest level which holds two red nodes.
n.parent.blacken()
n.grandparent.redden()
if (n === n.parent.left) {
// Case 1
n.grandparent.rotateRight(this)
} else {
// Case 2
n.grandparent.rotateLeft(this)
}
}
}
}

View File

@@ -1,132 +0,0 @@
import ID from './ID/ID.js'
import isParentOf from './isParentOf.js'
class ReverseOperation {
constructor (y, transaction) {
this.created = new Date()
const beforeState = transaction.beforeState
if (beforeState.has(y.userID)) {
this.toState = new ID(y.userID, y.ss.getState(y.userID) - 1)
this.fromState = new ID(y.userID, beforeState.get(y.userID))
} else {
this.toState = null
this.fromState = null
}
this.deletedStructs = transaction.deletedStructs
}
}
function applyReverseOperation (y, scope, reverseBuffer) {
let performedUndo = false
y.transact(() => {
while (!performedUndo && reverseBuffer.length > 0) {
let undoOp = reverseBuffer.pop()
// make sure that it is possible to iterate {from}-{to}
if (undoOp.fromState !== null) {
y.os.getItemCleanStart(undoOp.fromState)
y.os.getItemCleanEnd(undoOp.toState)
y.os.iterate(undoOp.fromState, undoOp.toState, op => {
while (op._deleted && op._redone !== null) {
op = op._redone
}
if (op._deleted === false && isParentOf(scope, op)) {
performedUndo = true
op._delete(y)
}
})
}
for (let op of undoOp.deletedStructs) {
if (
isParentOf(scope, op) &&
op._parent !== y &&
(
op._id.user !== y.userID ||
undoOp.fromState === null ||
op._id.clock < undoOp.fromState.clock ||
op._id.clock > undoOp.toState.clock
)
) {
performedUndo = true
op._redo(y)
}
}
}
})
return performedUndo
}
/**
* Saves a history of locally applied operations. The UndoManager handles the
* undoing and redoing of locally created changes.
*/
export default class UndoManager {
/**
* @param {YType} scope The scope on which to listen for changes.
* @param {Object} options Optionally provided configuration.
*/
constructor (scope, options = {}) {
this.options = options
options.captureTimeout = options.captureTimeout == null ? 500 : options.captureTimeout
this._undoBuffer = []
this._redoBuffer = []
this._scope = scope
this._undoing = false
this._redoing = false
this._lastTransactionWasUndo = false
const y = scope._y
this.y = y
y._hasUndoManager = true
y.on('afterTransaction', (y, transaction, remote) => {
if (!remote && transaction.changedParentTypes.has(scope)) {
let reverseOperation = new ReverseOperation(y, transaction)
if (!this._undoing) {
let lastUndoOp = this._undoBuffer.length > 0 ? this._undoBuffer[this._undoBuffer.length - 1] : null
if (
this._redoing === false &&
this._lastTransactionWasUndo === false &&
lastUndoOp !== null &&
reverseOperation.created - lastUndoOp.created <= options.captureTimeout
) {
lastUndoOp.created = reverseOperation.created
if (reverseOperation.toState !== null) {
lastUndoOp.toState = reverseOperation.toState
if (lastUndoOp.fromState === null) {
lastUndoOp.fromState = reverseOperation.fromState
}
}
reverseOperation.deletedStructs.forEach(lastUndoOp.deletedStructs.add, lastUndoOp.deletedStructs)
} else {
this._lastTransactionWasUndo = false
this._undoBuffer.push(reverseOperation)
}
if (!this._redoing) {
this._redoBuffer = []
}
} else {
this._lastTransactionWasUndo = true
this._redoBuffer.push(reverseOperation)
}
}
})
}
/**
* Undo the last locally created change.
*/
undo () {
this._undoing = true
const performedUndo = applyReverseOperation(this.y, this._scope, this._undoBuffer)
this._undoing = false
return performedUndo
}
/**
* Redo the last locally created change.
*/
redo () {
this._redoing = true
const performedRedo = applyReverseOperation(this.y, this._scope, this._redoBuffer)
this._redoing = false
return performedRedo
}
}

View File

@@ -1,36 +0,0 @@
/**
* YEvent describes the changes on a YType.
*/
export default class YEvent {
/**
* @param {YType} target The changed type.
*/
constructor (target) {
/**
* The type on which this event was created on.
* @type {YType}
*/
this.target = target
/**
* The current target on which the observe callback is called.
* @type {YType}
*/
this.currentTarget = target
}
/**
* Computes the path from `y` to the changed type.
*
* The following property holds:
* @example
* let type = y
* event.path.forEach(function (dir) {
* type = type.get(dir)
* })
* type === event.target // => true
*/
get path () {
return this.currentTarget.getPathTo(this.target)
}
}

View File

@@ -1,57 +0,0 @@
import ID from '../Util/ID/ID.js'
import ItemJSON from '../Struct/ItemJSON.js'
import ItemString from '../Struct/ItemString.js'
/**
* Try to merge all items in os with their successors.
*
* Some transformations (like delete) fragment items.
* Item(c: 'ab') + Delete(1,1) + Delete(0, 1) -> Item(c: 'a',deleted);Item(c: 'b',deleted)
*
* This functions merges the fragmented nodes together:
* Item(c: 'a',deleted);Item(c: 'b',deleted) -> Item(c: 'ab', deleted)
*
* TODO: The Tree implementation does not support deletions in-spot.
* This is why all deletions must be performed after the traversal.
*
*/
export function defragmentItemContent (y) {
const os = y.os
if (os.length < 2) {
return
}
let deletes = []
let node = os.findSmallestNode()
let next = node.next()
while (next !== null) {
let a = node.val
let b = next.val
if (
(a instanceof ItemJSON || a instanceof ItemString) &&
a.constructor === b.constructor &&
a._deleted === b._deleted &&
a._right === b &&
(new ID(a._id.user, a._id.clock + a._length)).equals(b._id)
) {
a._right = b._right
if (a instanceof ItemJSON) {
a._content = a._content.concat(b._content)
} else if (a instanceof ItemString) {
a._content += b._content
}
// delete b later
deletes.push(b._id)
// do not iterate node!
// !(node = next)
} else {
// not able to merge node, get next node
node = next
}
// update next
next = next.next()
}
for (let i = deletes.length - 1; i >= 0; i--) {
os.delete(deletes[i])
}
}

Some files were not shown because too many files have changed in this diff Show More