0 && allowUpdateComplete) { allowUpdateComplete = false; updateBoxFromSlider = true; updateBox(); this.visible = true; } } /** * Simple parsing function to convert the date strings in our dataset to the equivalent Date object. */ private function dateParse(value:String):Date { var dateArray:Array = value.split(' '); var datePart:Array = dateArray[0].split('-'); var hourPart:Array = dateArray[1].split(':'); return new Date(datePart[0], datePart[1] - 1, datePart[2], hourPart[0],hourPart[1]); } /** * Formats a date object from the DateTimeAxis into a label string */ private function formatDateLabel(value:Number, prevValue:Number, axis:DateTimeAxis):String { var dateValue:Date = new Date(); dateValue.setTime(value + ((dateValue.timezoneOffset + 60) * 60 * 1000)); switch(axis.labelUnits) { case "months": return labelMonthFormatter.format(dateValue); break; case "days": return labelDayFormatter.format(dateValue); case "hours": return labelHourFormatter.format(dateValue); default: return labelDefaultFormatter.format(dateValue); break; } } /*Step 3 */ /** * Called throughout use to update the mainData range of data that is displayed by slicing the * range data to the left and right values. */ private function updateMainData():void { trace("Updating mainData"); trace("lIndicator: " + leftIndicator.x) trace("rIndicator: " + rightIndicator.x) var lI:Number = indexMap(leftIndicator.x) var rI:Number = indexMap(rightIndicator.x) trace("lI: " + lI) trace("rI: " + rI) mainData.source = rangeData.source.slice(lI, rI); chartMouseOut(); } /** * Called from the slider value changes. It is filtered to only change when the slider calling it * directly. The updateBoxFromSlider value is set to false when the moveSlider function effect is * playing because the box widths have already been set by the dividerRelease calling * updateIndicatorValuesWithEffect. */ private function updateBox():void { if(updateBoxFromSlider) { trace("updateBoxFromSlider"); //setting the box width value to the slider value times the ratio (to decrease //it to the equivalent width percentage //eg. full divided box width = 500, rangeDataRatio = 1/5 would equal 100 for the //proper left box width equal to range index value leftBox.width = slider.values[0] * rangeDataRatio; rightBox.width = dividedBox.width - ( slider.values[1] * rangeDataRatio ); leftIndicator.x = slider.values[0]; rightIndicator.x = slider.values[1]; //leftIndicator.x = indexMap(slider.values[0]); //rightIndicator.x = indexMap(slider.values[1]); trace("S[0]: " + slider.values[0] + "--> " + leftIndicator.x); trace("S[1]: " + slider.values[1] + "--> " + rightIndicator.x); updateMainData(); } } private function indexMap(x:Number):int { trace("indexMap"); var pm:Number = rangeData.length trace("pm =" + pm) var x1:Number = (26/27)*pm var x2:Number = pm var y1:Number = 24*26 var y2:Number = 24*26+24*60 if (x < x1){ trace(x + " --->" + (y1/x1)*x) return (y1/x1)*x } else { var m:Number = (y2-y1)/(x2-x1); var b:Number = (x2*y1-x1*y2)/(x2-x1) trace(x + " --->" + m*x+b) return m*x+b } } /** * Updates the range by moving the entire range left or right by a fixed number of units */ private function clickUpdate(value:int):void { leftIndicator.x += value; rightIndicator.x += value; slider.dispatchEvent(new SliderEvent('change')); } /** * Called from the divided box dividerRelease. Calls a Move for the left and right Indicator * x values which has an easing function * applied. */ private function updateIndicatorValuesWithEffect():void { //setting indicator positions to the box width divided by the ratio (to increase //it to the equivalent range value) //eg. left box width = 100, rangeDataRation = 1/5 would equal 500 for the range index value moveSlider(leftIndicator, (leftBox.width / rangeDataRatio), false); moveSlider(rightIndicator, ((dividedBox.width - rightBox.width) / rangeDataRatio), false); } /** * Called from the thumbRelease on the slider instance, as well as creationComplete * to set the initial range values. * Updates the left and right indicator x values without the move effect. */ private function updateIndicatorsQuietly():void { //these two values are mapped 1:1 as the slider values and indicator values equal the rangeData length exactly leftIndicator.x = slider.values[0]; rightIndicator.x = slider.values[1]; } /** * Moves the left and right indicator x values with an easing transition applied. update * dictates whether this should update the divided box range measurements (false if we're calling this * from the divided box release) callbackFunc can be passed to get called when the move is finished. */ private function moveSlider(target:VRule, xTo:Number, update:Boolean, callbackFunc:Function = null, ... rest):void { var moveIndicator:Move = new Move(); moveIndicator.end(); moveIndicator.easingFunction = Cubic.easeOut; moveIndicator.duration = 750; moveIndicator.target = target; moveIndicator.xTo = xTo; moveIndicator.addEventListener(EffectEvent.EFFECT_START, function():void {updateBoxFromSlider = update}); moveIndicator.addEventListener(TweenEvent.TWEEN_UPDATE, function():void { updateMainData(); }); moveIndicator.addEventListener(EffectEvent.EFFECT_END, function():void {updateBoxFromSlider = true; if(callbackFunc != null) callbackFunc.call(this, rest)}); moveIndicator.play(); } /** * Called from range chart or main chart and determines the position of the mouse as well as left * and right indicators (for static comparison when moving) and adds systemManager events * to capture mouse movement. The values set here are used in the moveChart function to calculate * new position differences with start position */ private function setMouseDown(theChart:CartesianChart):void { //don't capture for drag if we're viewing the entire range of data if(!(leftIndicator.x == 0 && rightIndicator.x == rangeData.length)) { mouseXRef = this.mouseX; staticLeftBoundary = leftIndicator.x; staticRightBoundary = rightIndicator.x; if(theChart == mainChart) mainDrag = true; if(theChart == rangeChart) rangeDrag = true; this.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, moveChart); this.systemManager.addEventListener(MouseEvent.MOUSE_UP, stopDragging); } } /** * Called when systemManager receives mouseUp event. Sets the indicators for which range is * being dragged to false, and removes the system manager event listeners for drag movement. */ private function stopDragging(event:MouseEvent):void { if(mainData.length < 2) { if(leftIndicator.x == rangeData.length) { leftIndicator.x = rangeData.length - 5; rightIndicator.x = rangeData.length; } else if(rightIndicator.x == 0) { leftIndicator.x = 0; rightIndicator.x = 5; } updateBox(); } rangeDrag = false; mainDrag = false; this.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, moveChart); this.systemManager.removeEventListener(MouseEvent.MOUSE_UP, stopDragging); } /** * Determines which chart instance is being dragged, and updates the left and right indicator x values */ private function moveChart(event:MouseEvent):void { if(mainDrag) { leftIndicator.x = staticLeftBoundary + (mouseXRef - this.mouseX) / (mainChartArea.width / mainData.length); rightIndicator.x = staticRightBoundary + (mouseXRef - this.mouseX) / (mainChartArea.width / mainData.length); } else if(rangeDrag) { leftIndicator.x = staticLeftBoundary - (mouseXRef - this.mouseX) / rangeDataRatio; rightIndicator.x = staticRightBoundary - (mouseXRef - this.mouseX) / rangeDataRatio; } } /* Step 4 */ /** * Finds the DateTimeAxis value (the date) of the mouseover position */ private function getChartDataPoint():void { //filtering to only run if the full dataset is present in the chart... //this value is false if the indicator move effect is playing if(updateBoxFromSlider) { var chartPoint:Object = getChartCoordinates(new Point(mainChart.mouseX, mainChart.mouseY), mainChart); var formattedDate:String = fullDateFormat.format(new Date(chartPoint.x)); for(var i:int = 0; i < mainData.length; i++) { var dataItem:Object = mainData.getItemAt(i); if(dataItem.date == formattedDate) { _selectedDate = labelSummaryDateFormatter.format(dateParse(dataItem.date)); _selectedClose = 'Price: ' + dollarFormatter.format(Number(dataItem.close)); mainChart.series[0].getChildAt(i + 1).showRenderer(true); } else { mainChart.series[0].getChildAt(i + 1).showRenderer(false); } } } } /** * Called when cursor is moved off of main chart area. Clears any values that are bound * to mouseover position, and clears all * LineSeriesCustomRenderers on the chart that are showing */ private function chartMouseOut():void { if(mainData.length > 2) { for(var i:int = 0; i < mainData.length; i++) { try { mainChart.series[0].getChildAt(i + 1).showRenderer(false); mainChartVolume.series[0].getChildAt(i).showRenderer(false); } catch(e:Error) {}; } _selectedDate = labelSummaryDateFormatter.format(dateParse(mainData.getItemAt(0).date)) + ' - ' + labelSummaryDateFormatter.format(dateParse(mainData.getItemAt(mainData.length - 1).date)); _selectedClose = percentageFormatter.format((Number(mainData.getItemAt(mainData.length - 1).close) / Number(mainData.getItemAt(0).close) - 1) * 100) + '%'; } else { _selectedDate = ''; _selectedClose = ''; } } /** * Finds the DateTimeAxis value (the date) of the mouseover position * invertTransform takes a point in stage space (x and y coordinate) and transforms it into the * relative point in data space, giving appropriate values along x axis (first item in return array), * and y axis (second item in return array) */ private function getChartCoordinates(thePos:Point, theChart:CartesianChart):Object { var tmpArray:Array; if(theChart.series[0] != null) { tmpArray = theChart.series[0].dataTransform.invertTransform(thePos.x, thePos.y); return {x:tmpArray[0], y:tmpArray[1]}; } else { return null; } } /** * Updates the date range display to reflect the current position of the divided box drag */ private function setDividerDragDate():void { var tmpLeftIndex:int = leftBox.width / rangeDataRatio; var tmpRightIndex:int = ((dividedBox.width - rightBox.width) / rangeDataRatio) - 1; if(tmpLeftIndex >= 0 && tmpRightIndex <= rangeData.length) { _selectedDate = labelSummaryDateFormatter.format(dateParse(rangeData.getItemAt(tmpLeftIndex).date)) + ' - ' + labelSummaryDateFormatter.format(dateParse(rangeData.getItemAt(tmpRightIndex).date)); _selectedClose = percentageFormatter.format((Number(rangeData.getItemAt(tmpRightIndex).close) / Number(rangeData.getItemAt(tmpLeftIndex).close) - 1) * 100) + '%'; } } /*Step 5 */ /** * Prevents rollover or selection effects in the list control */ private function myEasingFunction(t:Number, b:Number, c:Number, d:Number):Number { return 0; } ]]>