// for Painter Object ========================================================================================================================= var dashSize = 2; function Painter(oPaintImplement, oData) { this.Canvas = document.getElementById(oPaintImplement.Parms.CanvasID); if (!this.Canvas.getContext) return; this.ctx = this.Canvas.getContext('2d'); this.PaintImplement = oPaintImplement; this.width = this.Canvas.width; this.height = this.Canvas.height; this.Data = oData; } Painter.prototype = { Paint: function () { var pctx = this.PaintImplement; var Data = this.Data; var ctx = this.ctx; //設定背景顏色 if (pctx.ChartOptions.BackgroundColor) { this.Canvas.style.background = pctx.ChartOptions.BackgroundColor; } if (typeof pctx.Initialize == 'function') pctx.Initialize(this); if (pctx.Start) pctx.Start.call(this); if (typeof pctx.PaintItems == 'function') { pctx.PaintItems.call(this); } else { var dataLength = ((typeof pctx.getDataLength == 'function') ? pctx.getDataLength.call(this) : this.Data.length); for (var i = 0; i < dataLength; i++) { var x = pctx.getX ? pctx.getX.call(this, i) : undefined; var y = pctx.getY ? pctx.getY.call(this, i) : undefined; pctx.PaintItem.call(this, i, x, y); } } if (pctx.End) pctx.End.call(this); }, drawHLine: function (color, x0, y0, w, lineWidth, lineStyle) { var ctx = this.ctx; ctx.strokeStyle = color; ctx.lineWidth = lineWidth; if (lineStyle && lineStyle == 'dashed') { var width = 0; do { this.drawHLine(color, width, y0, dashSize, 1, 'solid'); width += dashSize * 2; } while (width < w); } else { ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x0 + w, y0); ctx.stroke(); ctx.closePath(); } }, drawVLine: function (color, x0, y0, h, lineWidth, lineStyle) { var ctx = this.ctx; ctx.strokeStyle = color; ctx.lineWidth = lineWidth; if (lineStyle && lineStyle == 'dashed') { var height = 0; do { this.drawVLine(color, x0, height, dashSize, 1); height += dashSize * 2; } while (height < h); } else { ctx.beginPath(); ctx.moveTo(x0, y0); ctx.lineTo(x0, y0 + h); ctx.stroke(); ctx.closePath(); } }, drawRect: function (x0, y0, w, h, lineWidth, lineColor, fillColor) { var ctx = this.ctx; ctx.strokeStyle = lineColor; ctx.lineWidth = lineWidth; ctx.fillStyle = (fillColor?fillColor:'rgb(255,255,255)'); ctx.beginPath(); ctx.rect(x0, y0, w, h); ctx.fill(); ctx.stroke(); ctx.closePath(); }, drawText: function (str, x, y, font, color, textBaseLine, align) { var ctx = this.ctx; ctx.font = font; ctx.fillStyle = color; ctx.textBaseline = textBaseLine; ctx.textAlign = align; ctx.fillText (str, x, y); }, setData: function (Data) { this.Data = Data; }, setPainterImplement: function (implement) { this.PaintImplement = implement; } }; // ============================================================================================================================================ // for Controller Object ====================================================================================================================== function Controller(canvasId, options) { this.Canvas = $id(canvasId); this.ctx = this.Canvas.getContext('2d'); this.Region = options.Region; this.Bar = options.Bar; this.Value = options.Value; //if (this.Value) {console.log('this.Value is{left:' + this.Value.left + ',right:' + this.Value.right + ')');} this.preValue = options.Value; this.MinBarDistance = options.MinBarDistance || 1; this.onPositionChanged = options.onPositionChanged; this.prePaint = options.prePaint; this.isTouchDevice = isTouchDevice(); this.touchFaultTolerance = options.touchFaultTolerance; } Controller.prototype = { calcPositions: function () { var width = (this.Region.width - this.Bar.width * 2); this.leftBarPosition = this.Value.left * width / 100 + this.Bar.width; this.rightBarPosition = this.Value.right * width / 100 + this.Bar.width; }, drawControllerPart: function () { var Canvas = this.Canvas; var ctx = this.ctx; ctx.save(); var Region = this.Region; var Bar = this.Bar; this.calcPositions(); var leftBarPosition = this.leftBarPosition; var rightBarPosition = this.rightBarPosition; ctx.clearRect(Region.x - 1, Region.y - 1, Region.width + 1, Region.height + 1); if (typeof this.prePaint == 'function') { this.prePaint(ctx); } //設定外框線寬及顏色 ctx.lineWidth = 1; ctx.strokeStyle = Region.BorderColor; //左外框 ctx.beginPath(); ctx.moveTo(Region.x + Bar.width, Region.y); ctx.lineTo(Region.x + Bar.width, Region.y + Region.height); ctx.lineTo(Region.x + leftBarPosition, Region.y + Region.height); ctx.lineTo(Region.x + leftBarPosition, Region.y); ctx.lineTo(Region.x + Bar.width, Region.y); ctx.stroke(); //右外框 ctx.beginPath(); ctx.moveTo(Region.x + rightBarPosition, Region.y); ctx.lineTo(Region.x + rightBarPosition, Region.y + Region.height); ctx.lineTo(Region.x + Region.width - Bar.width, Region.y + Region.height); ctx.lineTo(Region.x + Region.width - Bar.width, Region.y); ctx.lineTo(Region.x + rightBarPosition, Region.y); ctx.stroke(); //中外框 ctx.beginPath(); ctx.moveTo(Region.x + leftBarPosition, Region.y); ctx.lineTo(Region.x + rightBarPosition, Region.y); ctx.moveTo(Region.x + leftBarPosition, Region.y + Region.height); ctx.lineTo(Region.x + rightBarPosition, Region.y + Region.height); ctx.stroke(); //內框底色 ctx.beginPath(); ctx.fillStyle = 'blue'; ctx.globalAlpha = .25; ctx.rect(Region.x + leftBarPosition, Region.y, rightBarPosition - leftBarPosition, Region.height); ctx.closePath(); ctx.fill(); ctx.globalAlpha = 1; //左拉Bar var fBarProportion = 7/10; //左右拉bar的面積占比 ctx.strokeStyle = Bar.BorderColor; ctx.fillStyle = Bar.FillColor; ctx.beginPath(); var leftBarRegion = { x: Region.x + leftBarPosition - Bar.width + 1, y: Region.y + Region.height / 2 - Bar.height / 2, width: Bar.width * fBarProportion - 1, height: Bar.height }; ctx.rect(leftBarRegion.x, leftBarRegion.y, leftBarRegion.width, leftBarRegion.height); this.leftBarRegion = leftBarRegion; ctx.closePath(); ctx.stroke(); ctx.fill(); //右拉Bar ctx.beginPath(); var rightBarRegion = { x: Region.x + rightBarPosition + Bar.width * (1 - fBarProportion), y: Region.y + Region.height / 2 - Bar.height / 2, width: Bar.width * fBarProportion - 1, height: Bar.height }; ctx.rect(rightBarRegion.x, rightBarRegion.y, rightBarRegion.width, rightBarRegion.height); this.rightBarRegion = rightBarRegion; ctx.closePath(); ctx.stroke(); ctx.fill(); //左拉Bar右側部分的顏色 ctx.strokeStyle = Bar.BorderColor; ctx.fillStyle = '#AFAFEE'; ctx.beginPath(); var leftBarMiddleRegion = { x: Region.x + leftBarPosition - Bar.width * (1 - fBarProportion) + 1, y: Region.y + Region.height / 2 - Bar.height / 2, width: Bar.width * (1 - fBarProportion) - 1, height: Bar.height }; ctx.rect(leftBarMiddleRegion.x, leftBarMiddleRegion.y, leftBarMiddleRegion.width, leftBarMiddleRegion.height); this.leftBarMiddleRegion = leftBarMiddleRegion; ctx.closePath(); ctx.stroke(); ctx.fill(); //右拉Bar左側部分的顏色 ctx.beginPath(); var rightBarMiddleRegion = { x: Region.x + rightBarPosition, y: Region.y + Region.height / 2 - Bar.height / 2, width: Bar.width * (1 - fBarProportion) - 1, height: Bar.height }; ctx.rect(rightBarMiddleRegion.x, rightBarMiddleRegion.y, rightBarMiddleRegion.width, rightBarMiddleRegion.height); this.rightBarMiddleRegion = rightBarMiddleRegion; ctx.closePath(); ctx.stroke(); ctx.fill(); /* //左拉Bar右側線的顏色 ctx.lineWidth = 1.5; ctx.strokeStyle = '#AFAFEE'; ctx.beginPath(); ctx.moveTo(Region.x + leftBarPosition + 1, Region.y + Region.height / 2 - Bar.height / 2); ctx.lineTo(Region.x + leftBarPosition + 1, Region.y + Bar.height + 2); ctx.stroke(); //右拉Bar左側線的顏色 ctx.beginPath(); ctx.moveTo(Region.x + rightBarPosition, Region.y + Region.height / 2 - Bar.height / 2); ctx.lineTo(Region.x + rightBarPosition, Region.y + Bar.height + 2); ctx.stroke(); */ ctx.restore(); }, setLeftBarPosition: function (x) { if (x < this.Bar.width) this.leftBarPosition = this.Bar.width; else if (this.rightBarPosition - x - this.MinBarDistance > 0) this.leftBarPosition = x; else this.leftBarPosition = this.rightBarPosition - this.MinBarDistance; this.Value = this.getValue(); }, setRightBarPosition: function (x) { if (x < this.leftBarPosition + this.MinBarDistance) this.rightBarPosition = this.leftBarPosition + this.MinBarDistance; else if (x > this.Region.width - this.Bar.width) this.rightBarPosition = this.Region.width - this.Bar.width; else this.rightBarPosition = x; this.Value = this.getValue(); }, addControllerEvents: function () { var me = this; if (me.isTouchDevice) { var Canvas = me.Canvas; addEvent(Canvas, 'touchmove', function (e) { e = e || event; var src = e.srcElement || e.target || e.relatedTarget; var touches = e.touches; if (!touches || !touches.length) return; var changed = false; var canvasPosition = getPageCoord(me.Canvas); if (me.fingers && me.fingers.length) { for (var i = 0; i < me.fingers.length; i++) { var finger = me.fingers[i]; for (var j = 0; j < touches.length; j++) { var touch = touches[j]; if (touch.identifier == finger.id) { var currentX = touch.pageX - canvasPosition.x; var moveLength = (currentX - finger.startX); if (moveLength != 0) { if (finger.type == 'left') { me.setLeftBarPosition(finger.leftPosition + moveLength); } else if (finger.type == 'right') { me.setRightBarPosition(finger.rightPosition + moveLength); } else { me.setLeftBarPosition(finger.leftPosition + moveLength); me.setRightBarPosition(finger.rightPosition + moveLength); } changed = true; } break; } } } } if (changed) { me.drawControllerPart(); //setDebugMsg('changed='+changed+',me.isValueChanged()=' + me.isValueChanged() + ',me.Value=' + me.getValue()); if (typeof me.onPositionChanged == 'function' && me.isValueChanged()) { me.Value = me.getValue(); me.onPositionChanged(me.Value); } } disableBubbleAndPreventDefault(e); }); addEvent(Canvas, 'touchend', function (e) { e = e||event; //setDebugMsg('enter touchend me.fingers.length='+me.fingers.length); if(me.fingers&&me.fingers.length){ if (typeof me.onPositionChanged == 'function' && me.isValueChanged()) { me.Value = me.getValue(); me.onPositionChanged(me.Value); } }else{ var timeSpan = new Date().getTime() - me.touchstartTime.getTime(); //setDebugMsg('timeSpan='+timeSpan); if(timeSpan < window.tapTimeLimit && me.startTouch){ var canvasPosition = getPageCoord(me.Canvas); var evt = me.startTouch; var point = { offsetX: evt.pageX - canvasPosition.x, offsetY: evt.pageY - canvasPosition.y }; var centerX = (me.rightBarPosition+me.leftBarPosition)/2; var moveLength = point.offsetX - centerX; /* setDebugMsg('evt.pageX='+evt.pageX+',centerX='+centerX+',point.offsetX='+point.offsetX+',moveLength='+moveLength); setDebugMsg('me.leftBarPosition+moveLength=' +(me.leftBarPosition+moveLength) + ',me.rightBarPosition+moveLength=' +(me.rightBarPosition+moveLength)); */ me.setLeftBarPosition(me.leftBarPosition+moveLength); me.setRightBarPosition(me.rightBarPosition+moveLength); me.drawControllerPart(); //setDebugMsg('changed='+changed+',me.isValueChanged()=' + me.isValueChanged() + ',me.Value=' + me.getValue()); if (typeof me.onPositionChanged == 'function' && me.isValueChanged()) { me.Value = me.getValue(); me.onPositionChanged(me.Value); } me.startTouch = null; } } me.fingers=null; disableBubbleAndPreventDefault(e); }); addEvent(Canvas, 'touchstart', function (e) { var touches = e.touches; if (!touches || !touches.length) touches = e.changedTouches; me.touchstartTime = new Date(); me.startTouch = touches[0]; var src = e.srcElement || e.target || e.relatedTarget; var canvasPosition = getPageCoord(me.Canvas); function getTouchType(point) { if (me.isOnLeftBar(point)) return 'left'; if (me.isOnRightBar(point)) return 'right'; if (me._isBetweenLeftAndRight(point)) return 'middle'; return false; } me.fingers = []; if (touches.length) { for (var i = 0; i < touches.length; i++) { var touch = touches[i]; var point = { offsetX: touch.pageX - canvasPosition.x, offsetY: touch.pageY - canvasPosition.y }; var touchSection = getTouchType(point); if (!touchSection) continue; var finger = { id: touch.identifier, startX: touch.pageX - canvasPosition.x, type: touchSection, leftPosition: me.leftBarPosition, rightPosition: me.rightBarPosition }; me.fingers.push(finger); } } disableBubbleAndPreventDefault(e); return false; }); } else { var moveHandle = function (ev) { var isOnLeftBar = me.isOnLeftBar(ev); var isOnRightBar = me.isOnRightBar(ev); if (me._isBetweenLeftAndRight(ev)) { document.body.style.cursor = 'pointer'; } else if (isOnLeftBar || isOnRightBar || me.triggerBar) { document.body.style.cursor = 'col-resize'; } else { document.body.style.cursor = 'default'; } if (me.triggerBar) { me.triggerBar.targetX = ev.offsetX; var moveLength = (me.triggerBar.targetX - me.triggerBar.x); if (me.triggerBar.type == 'left') { document.body.style.cursor = 'col-resize'; me.setLeftBarPosition(me.triggerBar.position + moveLength); } else if (me.triggerBar.type == 'right') { me.setRightBarPosition(me.triggerBar.position + moveLength); } else { me.setLeftBarPosition(me.triggerBar.leftPosition + moveLength); me.setRightBarPosition(me.triggerBar.rightPosition + moveLength); } if (typeof me.onPositionChanged == 'function' && me.isValueChanged()) { me.Value = me.getValue(); me.onPositionChanged(me.Value); } me.drawControllerPart(); } }; var endMove = function (ev) { if (me.triggerBar) { } me.triggerBar = null; document.body.style.cursor = 'default'; if (typeof me.onPositionChanged == 'function' && me.isValueChanged()) { me.Value = me.getValue(); me.onPositionChanged(me.Value); //console.log('me.onPositionChanged(me.Value) me.Value is {left:' + me.Value.left + ',right:' + me.Value.right + '}'); } }; var startHandle = function (ev) { var isOnLeftBar = me.isOnLeftBar(ev); var isOnRightBar = me.isOnRightBar(ev); var isOnMiddle = me._isBetweenLeftAndRight(ev); if (isOnMiddle) { document.body.style.cursor = 'pointer'; } else if (isOnLeftBar || isOnRightBar) { document.body.style.cursor = 'col-resize'; } else { document.body.style.cursor = 'default'; } if (isOnLeftBar) me.triggerBar = { type: 'left', x: ev.offsetX, position: me.leftBarPosition }; else if (isOnRightBar) me.triggerBar = { type: 'right', x: ev.offsetX, position: me.rightBarPosition }; else if (isOnMiddle) me.triggerBar = { type: 'middle', x: ev.offsetX, leftPosition: me.leftBarPosition, rightPosition: me.rightBarPosition }; else me.triggerBar = null; }; addEvent(me.Canvas, 'mouseup', endMove); addEvent(me.Canvas, 'mouseout', endMove); addEvent(me.Canvas, 'mousemove', function (ev) { ev = ev || event; if (ev.preventDefault) ev.preventDefault(); else ev.returnValue = false; var point = getOffset(ev); moveHandle(point); }); addEvent(me.Canvas, 'mousedown', function (ev) { ev = ev || event; var point = getOffset(ev); startHandle(point); }); } }, isValueChanged: function () { if (typeof this.preValue == 'undefined') { this.preValue = this.getValue(); return false; } if (isTouchDevice() && this.latestChangeTime) { var now = new Date(); if (now.getTime() - this.latestChangeTime.getTime() < 50) { return false; } } var preValue = this.preValue; var Value = this.getValue(); var changed = Math.abs(Value.left - preValue.left) + Math.abs(Value.right - preValue.right); this.preValue = Value; var result = changed != 0; if (result) { this.latestChangeTime = new Date(); } return changed != 0; }, _isInRegion: function (ev, Region) { return ev.offsetX > Region.x && ev.offsetX < Region.x + Region.width && ev.offsetY > Region.y && ev.offsetY < Region.y + Region.height; }, _isBetweenLeftAndRight: function (ev) { var Region = this.Region; var middleRegion = { x: Region.x + this.leftBarPosition, y: Region.y, width: this.rightBarPosition - this.leftBarPosition, height: this.Region.height }; return this._isInRegion(ev, middleRegion) || this._isInRegion(ev, this.leftBarMiddleRegion) || this._isInRegion(ev, this.rightBarMiddleRegion); }, _getTouchFaultToleranceRegion: function (Region) { var me = this; if (me.isTouchDevice) { Region.x -= me.touchFaultTolerance / 2; Region.width += me.touchFaultTolerance / 2; } return Region; }, isOnLeftBar: function (ev) { var Region = this._getTouchFaultToleranceRegion(this.leftBarRegion); return this._isInRegion(ev, Region); }, isOnRightBar: function (ev) { var Region = this._getTouchFaultToleranceRegion(this.rightBarRegion); return this._isInRegion(ev, Region); }, getValue: function () { var totalLength = this.Region.width - this.Bar.width * 2; return { left: (this.leftBarPosition - this.Bar.width) * 100 / totalLength, right: (this.rightBarPosition - this.Bar.width) * 100 / totalLength }; } }; // ============================================================================================================================================ // for ChartEventHelper ======================================================================================================================= function disableBubbleAndPreventDefault(e) { if (e.preventDefault) e.preventDefault(); e.cancelBubble = true; } function setTouchEventOffsetPosition(e, relativePoint) { e = e || event; if (e.touches && e.touches.length) { e = e.touches[0]; } else if (e.changedTouches && e.changedTouches.length) { e = e.changedTouches[0]; } var offsetX, offsetY; offsetX = e.pageX - relativePoint.x; offsetY = e.pageY - relativePoint.y; return { offsetX: offsetX, offsetY: offsetY }; } function CrossLinesAndTipMgr(oPainter, options) { if (typeof Tip != 'function') { window.Tip = function () { }; window.Tip.prototype = { show: function () { }, hide: function () { }, update: function () { } }; } this.Canvas = oPainter.Canvas; this.ChartOptions = oPainter.ChartOptions; this.options = options; } CrossLinesAndTipMgr.prototype._removeTipAndCrossLines = function () { //var Canvas = this.Canvas; var me = this; if (me.tip) me.tip.hide(); if (me.clsMgr) me.clsMgr.removeCrossLines(); }; CrossLinesAndTipMgr.prototype.updateOptions = function (options) { this.options = options; }; CrossLinesAndTipMgr.prototype._onMouseOrTouchMove = function (ev) { ev = ev || event; ev = getOffset(ev); var me = this; var options = me.options; var Canvas = me.Canvas; var canvasPosition = getPageCoord(Canvas); var range = options.TriggerEventRanges; var Browser = GetBrowserInfo(); if (ev.offsetX < range.x || ev.offsetX > range.x + range.width || ev.offsetY < range.y || ev.offsetY > range.y + range.height) { me._removeTipAndCrossLines(); return; } var crossPoint = options.GetCrossPoint(ev); var crossLinesOptions = { crossPoint: crossPoint, verticalRange: { y1: range.y, y2: range.y + range.height }, horizontalRange: { x1: range.x, x2: range.x + range.width }, color: options.CrossLineOptions.Color, LineWidth: options.CrossLineOptions.LineWidth, Canvas: Canvas }; if (!me.clsMgr) { var clsMgr = new CrossLines(crossLinesOptions); clsMgr.setMouseEvents(function (evHLine) { evHLine = evHLine || event; evHLine = getOffset(evHLine); var translatedEv = { offsetX: evHLine.offsetX + range.x, offsetY: Math.round((parseFloat(me.clsMgr.getHLine().style.top) - canvasPosition.y + options.CrossLineOptions.LineWidth / 2 - (Browser.Name=='Firefox'?1:0))*2)/2 }; var point = options.GetCrossPoint(translatedEv); clsMgr.updateCrossPoint(point); if (me.tip) { me.tip.update(point, options.TipOptions.GetTipHtml(translatedEv)); } }, function (evl) { evl = evl || event; evl = getOffset(evl); var translatedEv = { offsetX: Math.round((parseFloat(me.clsMgr.getVLine().style.left) - canvasPosition.x + options.CrossLineOptions.LineWidth / 2 - (Browser.Name=='Chrome'?1:0.5))*2)/2, offsetY: evl.offsetY + range.y }; var point = options.GetCrossPoint(translatedEv); clsMgr.updateCrossPoint(point); if (me.tip) { me.tip.update(point, options.TipOptions.GetTipHtml(translatedEv)); } }); me.clsMgr = clsMgr; } else { me.clsMgr.updateOptions(crossLinesOptions); } me.clsMgr.drawCrossLines(); if (options.TipOptions) { var tipOp = options.TipOptions; var TipHtml = tipOp.GetTipHtml(ev) if (!me.tip) { var tip = new Tip({ //tip setting position: { x: tipOp.Position.x || false, y: tipOp.Position.y || false }, size: tipOp.Size, opacity: tipOp.Opacity || 80, cssClass: tipOp.CssClass, offsetToPoint: tipOp.OffsetToPoint || 30, relativePoint: { x: crossPoint.x, y: crossPoint.y }, Canvas: Canvas, canvasRange: options.TriggerEventRanges, innerHtml: TipHtml.Tip, innerHtmlAxisL: TipHtml.TipAxisL, innerHtmlAxisR: TipHtml.TipAxisR, TipAxisL: tipOp.TipAxisL, TipAxisR: tipOp.TipAxisR }); me.tip = tip; } else { me.tip.canvasRange = options.TriggerEventRanges; } // 依據user選項修正Tip顯示位置 if (options.TipMgrPosition == 'FixedLeft' && true) { me.tip.position.x = -parseInt(tipOp.Size.width) - 6; me.tip.opacity = 100; } else if (options.TipMgrPosition == 'FixedRight' && true) { me.tip.position.x = parseInt(Canvas.width); me.tip.opacity = 100; } else { me.tip.position.x = tipOp.Position.x || false; me.tip.opacity = tipOp.Opacity || 80; } me.tip.show(crossPoint, TipHtml); } }; CrossLinesAndTipMgr.prototype._touchstart = function (e) { e = e || event; // disableBubbleAndPreventDefault(e); var src = e.srcElement || e.target || e.relatedTarget; this.touchstartTime = new Date(); }; CrossLinesAndTipMgr.prototype._touchmove = function (e) { e = e || event; // disableBubbleAndPreventDefault(e); var Canvas = this.Canvas; var relativePoint = getPageCoord(Canvas); var src = e.srcElement || e.target || e.relatedTarget; var fixedEvt = setTouchEventOffsetPosition(e, relativePoint); this._onMouseOrTouchMove(fixedEvt); }; CrossLinesAndTipMgr.prototype._touchend = function (e) { e = e || event; // disableBubbleAndPreventDefault(e); var src = e.srcElement || e.target || e.relatedTarget; var Canvas = this.Canvas; var fixedEvt = setTouchEventOffsetPosition(e, getPageCoord(Canvas)); this._removeTipAndCrossLines(); var time = new Date(); var ts = time.getTime() - this.touchstartTime.getTime(); if (ts < 200) { if (typeof this.options.onClick == 'function') this.options.onClick(); } }; CrossLinesAndTipMgr.prototype._mouseout = function (ev) { var e = ev || event; ev = getOffset(e); var me = this; var range = me.options.TriggerEventRanges; if (ev.offsetX <= range.x || ev.offsetX >= range.x + range.width || ev.offsetY <= range.y || ev.offsetY >= range.y + range.height) { me._removeTipAndCrossLines(); return; } var toEle = e.toElement || e.relatedTarget || e.target; if (toEle) { if (toEle == me.Canvas) return; if (toEle == me.clsMgr.getHLine() || toEle == me.clsMgr.getVLine()) return; me._removeTipAndCrossLines(); } }; CrossLinesAndTipMgr.prototype.addCrossLinesAndTipEvents = function () { var Canvas = this.Canvas; var options = this.options; var ChartOptions = this.ChartOptions; var canvasPosition = getPageCoord(Canvas); if (Canvas.addCrossLinesAndTipEvents == true) return; Canvas.addCrossLinesAndTipEvents = true; var touchable = isTouchDevice(); var me = this; if (touchable) { addEvent(Canvas, 'touchstart', function (ev) { me._touchstart.call(me, ev); }); addEvent(Canvas, 'touchmove', function (ev) { me._touchmove.call(me, ev); }); addEvent(Canvas, 'touchend', function (ev) { me._touchend.call(me, ev); }); } else { addEvent(Canvas, 'mouseout', function (ev) { me._mouseout.call(me, ev); }); addEvent(Canvas, 'mousemove', function (ev) { me._onMouseOrTouchMove.call(me, ev); }); if (typeof options.onClick == 'function') { addEvent(Canvas, 'click', options.onClick); } // add event for Legend for (var item in ChartOptions.ChartItems) { var s = 'ChartLegend_' + item; if (s in window) addEvent($id(s), 'mousemove', function (ev) { me._touchmove.call(me, ev); }); } } }; function addCrossLinesAndTipEvents(Canvas, options) { if(!Canvas.crossLineAndTipMgrInstance){ Canvas.crossLineAndTipMgrInstance = new CrossLinesAndTipMgr(Canvas, options); Canvas.crossLineAndTipMgrInstance.addCrossLinesAndTipEvents(); } } // ============================================================================================================================================ // for Tip object ============================================================================================================================= function Tip(options) { extendObject(options, this); } Tip.prototype = { getElementId: function (s) { return this.Canvas.id + '_tip' + s; }, _getRightLimit: function () { return this.canvasRange.x + this.canvasRange.width; }, _getLeftLimit: function () { return this.canvasRange.x; }, _getTopLimit: function () { return this.canvasRange.y; }, _getBottomLimit: function () { return this.canvasRange.y + this.canvasRange.height; }, show: function (relativePoint, html) { if (relativePoint) this.relativePoint = relativePoint; if (html) { this.innerHtml = html.Tip; this.innerHtmlAxisL = html.TipAxisL; this.innerHtmlAxisR = html.TipAxisR; } var otip = $id(this.getElementId('')); var oTipAxisL = $id(this.getElementId('AxisL')); var oTipAxisR = $id(this.getElementId('AxisR')); var size = this.size; var offset = this.offsetToPoint; var position = this.position; var relativePoint = this.relativePoint; var canvasRange = this.canvasRange; var canvasPosition = getPageCoord(this.Canvas); var y = position.y || relativePoint.y; var x = position.x || relativePoint.x; var tipX = 0; var tipY = 0; if (position.x) { tipX = position.x; } else { if (otip) { var currentX = parseInt(otip.style.left) - canvasPosition.x; // 左移 if (currentX > x) { if (offset + x + size.width > this._getRightLimit()) { currentX = x - offset - size.width; } else { currentX = x + offset; } } else { if (x - offset - size.width > this._getLeftLimit() || x + offset + size.width > this._getRightLimit()) { currentX = x - offset - size.width; } else { currentX = x + offset; } } tipX = currentX; } else { tipX = x - offset - size.width; if (tipX < this._getLeftLimit() && x + offset + size.width < this._getRightLimit()) { tipX = x + offset; } } } //y position if (position.y) tipY = position.y; else { if (otip) { var currentY = parseInt(otip.style.top) - canvasPosition.y; if (currentY > y) { if (offset + y + size.height > this._getBottomLimit()) { currentY = y - offset - size.height; } else { currentY = y + offset; } } else { if (y - offset - size.height > this._getTopLimit()) { currentY = y - offset - size.height; } else { currentY = y + offset; } } tipY = currentY; } else { tipY = y + offset; if (tipY > this._getBottomLimit()) { tipY = y - offset - size.height; } } } // 繪製主Tip if (this.innerHtml != null && this.innerHtml.trim() != '') { if (!otip) { otip = document.createElement('DIV'); otip.id = this.getElementId(''); var opacity = this.opacity || 100; otip.style.cssText = '-moz-opacity:.' + opacity + '; filter:alpha(opacity='+ opacity + ');' + 'opacity:' + (opacity / 100) + ';line-height:' + conLineHeight18 + 'px;font-family:Arial,"新細明體";' + 'font-size:' + conFontSize09 + 'pt;padding:4px;'; otip.style.position = 'absolute'; otip.style.zIndex = parseInt(this.Canvas.style.zIndex || 1) + 20; otip.style.backgroundColor = 'white'; otip.style.border = '1px solid gray'; otip.style.borderRadius = '10px'; otip.style.width = (this.size.width!=null?this.size.width+'px':null); otip.style.height = (this.size.height!=null?this.size.height+'px':null); otip.onmouseover = function() { this.style.display = 'none'; }; if (this.cssClass) otip.className = this.cssClass; document.body.appendChild(otip); } tipX = canvasPosition.x + tipX; otip.style.opacity = (this.opacity || 100) / 100; tipY = canvasPosition.y + tipY; otip.style.left = tipX + 'px'; otip.style.top = tipY + 'px'; otip.style.display = 'block'; otip.innerHTML = this.innerHtml; } else { if (otip) otip.style.display = 'none'; } // 繪製左軸Tip if (this.innerHtmlAxisL != null && this.innerHtmlAxisL.trim() != '') { if (!oTipAxisL) { oTipAxisL = document.createElement('DIV'); oTipAxisL.id = this.getElementId('AxisL'); var opacity = this.TipAxisL.Opacity; oTipAxisL.style.cssText = '-moz-opacity:.' + opacity + '; filter:alpha(opacity=' + opacity + '); opacity:' + (opacity / 100) + ';font-family:Arial,"新細明體";font-size:10pt;' + 'padding:0px;text-align:right;color:' + this.TipAxisL.Color + ';'; oTipAxisL.style.position = 'absolute'; oTipAxisL.style.zIndex = 3 + parseInt(this.Canvas.style.zIndex || 1); oTipAxisL.style.backgroundColor = this.TipAxisL.BackgroundColor || 'white'; oTipAxisL.style.textAlign = 'right'; oTipAxisL.style.width = (canvasRange.x - 1) + 'px'; if (this.cssClass) oTipAxisL.className = this.cssClass; document.body.appendChild(oTipAxisL); } oTipAxisL.style.left = (canvasPosition.x + 1) + 'px'; oTipAxisL.style.top = (canvasPosition.y + relativePoint.y - 6) + 'px'; oTipAxisL.style.display = 'block'; oTipAxisL.innerHTML = this.innerHtmlAxisL + ' '; } else { if (oTipAxisL) oTipAxisL.style.display = 'none'; } // 繪製右軸Tip if (this.innerHtmlAxisR != null && this.innerHtmlAxisR.trim() != '') { if (!oTipAxisR) { oTipAxisR = document.createElement('DIV'); oTipAxisR.id = this.getElementId('AxisR'); var opacity = this.TipAxisR.Opacity; oTipAxisR.style.cssText = '-moz-opacity:.' + opacity + '; filter:alpha(opacity=' + opacity + '); opacity:' + (opacity / 100) + ';font-family:Arial,"新細明體";font-size:10pt;' + 'padding:0px;text-align:left;color:' + this.TipAxisR.Color + ';'; oTipAxisR.style.position = 'absolute'; oTipAxisR.style.zIndex = 3 + parseInt(this.Canvas.style.zIndex || 1); oTipAxisR.style.backgroundColor = this.TipAxisR.BackgroundColor || 'white'; oTipAxisR.style.textAlign = 'left'; oTipAxisR.style.width = (this.Canvas.width - canvasRange.x - canvasRange.width - 1) + 'px'; if (this.cssClass) oTipAxisR.className = this.cssClass; document.body.appendChild(oTipAxisR); } oTipAxisR.style.left = (canvasPosition.x + canvasRange.x + canvasRange.width + 2) + 'px'; oTipAxisR.style.top = (canvasPosition.y + relativePoint.y - 6) + 'px'; oTipAxisR.style.display = 'block'; oTipAxisR.innerHTML = ' ' + this.innerHtmlAxisR; } else { if (oTipAxisR) oTipAxisR.style.display = 'none'; } }, hide: function () { var o = $id(this.getElementId('')); if (o) o.style.display = 'none'; var oLeft = $id(this.getElementId('AxisL')); if (oLeft) oLeft.style.display = 'none'; var oRight = $id(this.getElementId('AxisR')); if (oRight) oRight.style.display = 'none'; }, update: function (relativePoint, html) { this.relativePoint = relativePoint; this.innerHtml = html.Tip; this.innerHtmlAxisL = html.TipAxisL; this.innerHtmlAxisR = html.TipAxisR; this.show(); } }; // ============================================================================================================================================ // for Cross Lines object ===================================================================================================================== function CrossLines(options) { this.updateOptions(options); } CrossLines.prototype = { updateOptions: function (options) { this.Canvas = options.Canvas; this.canvasId = this.Canvas.id; this.horizontalDivId = this.canvasId + '_crossLines_H'; this.verticalDivId = this.canvasId + '_crossLines_V'; this.verticalRange = options.verticalRange || { y1: 0, y2: this.Canvas.height }; this.horizontalRange = options.horizontalRange || { x1: 0, x2: this.Canvas.width }; this.canvasPosition = getPageCoord(this.Canvas); this.crossPoint = options.crossPoint; this.color = options.color || 'black'; this.LineWidth = options.LineWidth || 1; }, removeCrossLines: function () { var Canvas = this.Canvas; var canvasId = Canvas.id; var horizontalDivId = canvasId + '_crossLines_H'; var verticalDivId = canvasId + '_crossLines_V'; var lineX = $id(horizontalDivId); if (lineX) lineX.style.display = 'none'; var lineY = $id(verticalDivId); if (lineY) lineY.style.display = 'none'; }, getHLine: function () { return $id(this.horizontalDivId); }, getVLine: function () { return $id(this.verticalDivId); }, setMouseEvents: function (evtForHLine, evtForVLine) { this.hLineMouseEvt = evtForHLine; this.vLineMouseEvt = evtForVLine; }, updateCrossPoint: function (point) { this.crossPoint = point; this.drawCrossLines(); }, drawCrossLines: function () { var Canvas = this.Canvas; var canvasId = this.Canvas.id; var horizontalDivId = canvasId + '_crossLines_H'; var verticalDivId = canvasId + '_crossLines_V'; var vertialRange = this.verticalRange || { y1: 0, y2: Canvas.height }; var horizontalRange = this.horizontalRange || { x1: 0, x2: Canvas.width }; var canvasPosition = this.canvasPosition; var LineWidth = Number(this.LineWidth); var Browser = GetBrowserInfo(); if (this.crossPoint.x < horizontalRange.x1 || this.crossPoint.x > horizontalRange.x2 || this.crossPoint.y < vertialRange.y1 || this.crossPoint.y > vertialRange.y2) { this.removeCrossLines(); return; } var zIndex = (Canvas.style.zIndex || 1) + 1; var exists = false; var hLine; if ($id(horizontalDivId)) { exists = true; hLine = $id(horizontalDivId); } else { hLine = document.createElement('DIV'); hLine.id = horizontalDivId; } hLine.className = 'disable_select'; hLine.style.display = 'block'; hLine.style.position = 'absolute'; hLine.style.width = (Math.round((horizontalRange.x2 - horizontalRange.x1) * 2) / 2) + 'px'; hLine.style.height = LineWidth + 'px'; hLine.style.left = (Math.round((canvasPosition.x + horizontalRange.x1) * 2) / 2) + 'px'; hLine.style.top = (Math.round((this.crossPoint.y + canvasPosition.y - LineWidth / 2) * 2) / 2) + 'px'; hLine.style.backgroundColor = this.color; hLine.style.zIndex = zIndex; if (!exists) { document.body.appendChild(hLine); if (typeof this.hLineMouseEvt == 'function') { addEvent(hLine, 'mouseover', function() { DisableSelect(); this.hLineMouseEvt; }); addEvent(hLine, 'mousemove', this.hLineMouseEvt); } } exists = false; var vLine; if ($id(verticalDivId)) { exists = true; vLine = $id(verticalDivId); } else { vLine = document.createElement('DIV'); vLine.id = verticalDivId; } vLine.className = 'disable_select'; vLine.style.display = 'block'; vLine.style.position = 'absolute'; vLine.style.height = (Math.round((vertialRange.y2 - vertialRange.y1) * 2) / 2) + 'px'; vLine.style.width = LineWidth + 'px'; vLine.style.left = (Math.round((this.crossPoint.x + canvasPosition.x - LineWidth / 2 + (Browser.Name=='Chrome'?1:0.5)) * 2) / 2) + 'px'; vLine.style.top = (Math.round((vertialRange.y1 + canvasPosition.y) * 2) / 2) + 'px'; vLine.style.backgroundColor = this.color; vLine.style.index = zIndex; if (!exists) { document.body.appendChild(vLine); if (typeof this.vLineMouseEvt == 'function') { addEvent(vLine, 'mouseover', function() { DisableSelect(); this.vLineMouseEvt; }); addEvent(vLine, 'mousemove', this.vLineMouseEvt); } } } }; // ============================================================================================================================================