Update sencha touch
This commit is contained in:
parent
66f4cc1086
commit
6b18a65ab4
|
@ -7,6 +7,10 @@
|
|||
}
|
||||
|
||||
&.x-list-round.x-list-grouped {
|
||||
.x-list-footer-wrap.x-list-item-tpl {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.x-list-header-wrap {
|
||||
.x-dock-horizontal {
|
||||
padding-top: 0;
|
||||
|
|
|
@ -104,15 +104,15 @@
|
|||
}
|
||||
|
||||
.round {
|
||||
@include border-radius($panel-border-radius);
|
||||
@include border-radius($form-fieldset-radius);
|
||||
}
|
||||
|
||||
// Hide group header
|
||||
.x-list-header-wrap {
|
||||
@include border-top-radius($panel-border-radius !important);
|
||||
@include border-top-radius($form-fieldset-radius !important);
|
||||
|
||||
.x-innerhtml {
|
||||
@include border-top-radius($panel-border-radius !important);
|
||||
@include border-top-radius($form-fieldset-radius !important);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
40
vendor/touch/license.txt
vendored
40
vendor/touch/license.txt
vendored
|
@ -1,4 +1,4 @@
|
|||
Sencha Touch - JavaScript Library
|
||||
Sencha Touch & Sencha Touch Charts - JavaScript Libraries
|
||||
Copyright (c) 2010-2015, Sencha, Inc.
|
||||
All rights reserved.
|
||||
licensing@sencha.com
|
||||
|
@ -6,23 +6,43 @@ licensing@sencha.com
|
|||
http://www.sencha.com/products/touch/license.php
|
||||
|
||||
|
||||
Commercial License
|
||||
Open Source License
|
||||
------------------------------------------------------------------------------------------
|
||||
This version of Sencha Touch is licensed commercially.
|
||||
This version of Sencha Touch and Sencha Touch Charts is licensed under the terms of the Open
|
||||
Source GPL 3.0 license.
|
||||
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
|
||||
There are several FLOSS exceptions available for use with this release for
|
||||
open source applications that are distributed under a license other than the GPL.
|
||||
|
||||
* Open Source License Exception for Applications
|
||||
|
||||
http://www.sencha.com/products/floss-exception.php
|
||||
|
||||
* Open Source License Exception for Development
|
||||
|
||||
http://www.sencha.com/products/ux-exception.php
|
||||
|
||||
|
||||
Alternate Licensing for Sencha Touch
|
||||
------------------------------------------------------------------------------------------
|
||||
Commercial and OEM Licenses are available for an alternate download of Sencha Touch.
|
||||
This is the appropriate option if you are creating proprietary applications and you are
|
||||
not prepared to distribute and share the source code of your application under the
|
||||
GPL v3 license. Please visit http://www.sencha.com/store/touch/license.php for more details.
|
||||
|
||||
|
||||
OEM / Reseller License
|
||||
Alternate Licensing for Sencha Touch Charts
|
||||
------------------------------------------------------------------------------------------
|
||||
For more details, please visit: http://www.sencha.com/products/touch/license.php
|
||||
Commercial and OEM Licenses are available for an alternate download of Sencha Touch Charts.
|
||||
This is the appropriate option if you are creating proprietary applications and you are
|
||||
not prepared to distribute and share the source code of your application under the
|
||||
GPL v3 license.
|
||||
|
||||
|
||||
Open Source Licensing
|
||||
------------------------------------------------------------------------------------------
|
||||
Open Source Licensing is available for an alternate download of Sencha Touch.
|
||||
For more details, please visit http://www.sencha.com/store/touch/license.php for more details.
|
||||
Sencha Touch Charts is available commercially only as a part of Sencha Complete or Sencha
|
||||
Complete Team. Please visit http://www.sencha.com/products/complete/license or
|
||||
http://www.sencha.com/products/complete-team/license for more details.
|
||||
|
||||
|
||||
Third Party Content
|
||||
|
|
11
vendor/touch/resources/css/base.css
vendored
11
vendor/touch/resources/css/base.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/bb10.css
vendored
11
vendor/touch/resources/css/bb10.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/bb103.css
vendored
11
vendor/touch/resources/css/bb103.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/cupertino-classic.css
vendored
11
vendor/touch/resources/css/cupertino-classic.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/cupertino.css
vendored
11
vendor/touch/resources/css/cupertino.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/mountainview.css
vendored
11
vendor/touch/resources/css/mountainview.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/sencha-touch.css
vendored
11
vendor/touch/resources/css/sencha-touch.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/tizen.css
vendored
11
vendor/touch/resources/css/tizen.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
11
vendor/touch/resources/css/wp.css
vendored
11
vendor/touch/resources/css/wp.css
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
|
23410
vendor/touch/sencha-touch-all-debug.js
vendored
23410
vendor/touch/sencha-touch-all-debug.js
vendored
File diff suppressed because it is too large
Load diff
13
vendor/touch/sencha-touch-all.js
vendored
13
vendor/touch/sencha-touch-all.js
vendored
File diff suppressed because one or more lines are too long
343
vendor/touch/sencha-touch-debug.js
vendored
343
vendor/touch/sencha-touch-debug.js
vendored
|
@ -5,10 +5,13 @@ Copyright (c) 2011-2015 Sencha Inc
|
|||
|
||||
Contact: http://www.sencha.com/contact
|
||||
|
||||
Commercial Usage
|
||||
Licensees holding valid commercial licenses may use this file in accordance with the Commercial
|
||||
Software License Agreement provided with the Software or, alternatively, in accordance with the
|
||||
terms contained in a written agreement between you and Sencha.
|
||||
GNU General Public License Usage
|
||||
This file may be used under the terms of the GNU General Public License version 3.0 as
|
||||
published by the Free Software Foundation and appearing in the file LICENSE included in the
|
||||
packaging of this file.
|
||||
|
||||
Please review the following information to ensure the GNU General Public License version 3.0
|
||||
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
|
||||
|
||||
If you are unsure which license is appropriate for your use, please contact the sales department
|
||||
at http://www.sencha.com/contact.
|
||||
|
@ -9290,6 +9293,11 @@ Ext.apply(Ext, {
|
|||
elementSize: {
|
||||
xclass: 'Ext.event.publisher.ElementSize'
|
||||
}
|
||||
//<feature charts>
|
||||
,seriesItemEvents: {
|
||||
xclass: 'Ext.chart.series.ItemPublisher'
|
||||
}
|
||||
//</feature>
|
||||
},
|
||||
|
||||
//<feature logger>
|
||||
|
@ -15168,6 +15176,68 @@ Ext.ClassManager.addNameAlternateMappings({
|
|||
],
|
||||
"Ext.carousel.Infinite": [],
|
||||
"Ext.carousel.Item": [],
|
||||
"Ext.chart.AbstractChart": [],
|
||||
"Ext.chart.CartesianChart": [
|
||||
"Ext.chart.Chart"
|
||||
],
|
||||
"Ext.chart.Legend": [],
|
||||
"Ext.chart.MarkerHolder": [],
|
||||
"Ext.chart.Markers": [],
|
||||
"Ext.chart.PolarChart": [],
|
||||
"Ext.chart.SpaceFillingChart": [],
|
||||
"Ext.chart.axis.Axis": [],
|
||||
"Ext.chart.axis.Category": [],
|
||||
"Ext.chart.axis.Numeric": [],
|
||||
"Ext.chart.axis.Time": [],
|
||||
"Ext.chart.axis.layout.CombineDuplicate": [],
|
||||
"Ext.chart.axis.layout.Continuous": [],
|
||||
"Ext.chart.axis.layout.Discrete": [],
|
||||
"Ext.chart.axis.layout.Layout": [],
|
||||
"Ext.chart.axis.segmenter.Names": [],
|
||||
"Ext.chart.axis.segmenter.Numeric": [],
|
||||
"Ext.chart.axis.segmenter.Segmenter": [],
|
||||
"Ext.chart.axis.segmenter.Time": [],
|
||||
"Ext.chart.axis.sprite.Axis": [],
|
||||
"Ext.chart.grid.CircularGrid": [],
|
||||
"Ext.chart.grid.HorizontalGrid": [],
|
||||
"Ext.chart.grid.RadialGrid": [],
|
||||
"Ext.chart.grid.VerticalGrid": [],
|
||||
"Ext.chart.interactions.Abstract": [],
|
||||
"Ext.chart.interactions.CrossZoom": [],
|
||||
"Ext.chart.interactions.Crosshair": [],
|
||||
"Ext.chart.interactions.ItemHighlight": [],
|
||||
"Ext.chart.interactions.ItemInfo": [],
|
||||
"Ext.chart.interactions.PanZoom": [],
|
||||
"Ext.chart.interactions.Rotate": [],
|
||||
"Ext.chart.interactions.RotatePie3D": [],
|
||||
"Ext.chart.label.Callout": [],
|
||||
"Ext.chart.label.Label": [],
|
||||
"Ext.chart.series.Area": [],
|
||||
"Ext.chart.series.Bar": [],
|
||||
"Ext.chart.series.CandleStick": [],
|
||||
"Ext.chart.series.Cartesian": [],
|
||||
"Ext.chart.series.Gauge": [],
|
||||
"Ext.chart.series.ItemPublisher": [],
|
||||
"Ext.chart.series.Line": [],
|
||||
"Ext.chart.series.Pie": [],
|
||||
"Ext.chart.series.Pie3D": [],
|
||||
"Ext.chart.series.Polar": [],
|
||||
"Ext.chart.series.Radar": [],
|
||||
"Ext.chart.series.Scatter": [],
|
||||
"Ext.chart.series.Series": [],
|
||||
"Ext.chart.series.StackedCartesian": [],
|
||||
"Ext.chart.series.sprite.Aggregative": [],
|
||||
"Ext.chart.series.sprite.Area": [],
|
||||
"Ext.chart.series.sprite.Bar": [],
|
||||
"Ext.chart.series.sprite.CandleStick": [],
|
||||
"Ext.chart.series.sprite.Cartesian": [],
|
||||
"Ext.chart.series.sprite.Line": [],
|
||||
"Ext.chart.series.sprite.Pie3DPart": [],
|
||||
"Ext.chart.series.sprite.PieSlice": [],
|
||||
"Ext.chart.series.sprite.Polar": [],
|
||||
"Ext.chart.series.sprite.Radar": [],
|
||||
"Ext.chart.series.sprite.Scatter": [],
|
||||
"Ext.chart.series.sprite.StackedCartesian": [],
|
||||
"Ext.data.ArrayStore": [],
|
||||
"Ext.data.Batch": [],
|
||||
"Ext.data.Connection": [],
|
||||
|
@ -15445,6 +15515,47 @@ Ext.ClassManager.addNameAlternateMappings({
|
|||
"Ext.dom.CompositeElement": [
|
||||
"Ext.CompositeElement"
|
||||
],
|
||||
"Ext.draw.Animator": [],
|
||||
"Ext.draw.Color": [],
|
||||
"Ext.draw.Component": [],
|
||||
"Ext.draw.Draw": [],
|
||||
"Ext.draw.LimitedCache": [],
|
||||
"Ext.draw.Matrix": [],
|
||||
"Ext.draw.Path": [],
|
||||
"Ext.draw.SegmentTree": [],
|
||||
"Ext.draw.Solver": [],
|
||||
"Ext.draw.Surface": [],
|
||||
"Ext.draw.TextMeasurer": [],
|
||||
"Ext.draw.TimingFunctions": [],
|
||||
"Ext.draw.engine.Canvas": [],
|
||||
"Ext.draw.engine.Svg": [],
|
||||
"Ext.draw.engine.SvgContext": [],
|
||||
"Ext.draw.engine.SvgContext.Gradient": [],
|
||||
"Ext.draw.engine.SvgExporter": [],
|
||||
"Ext.draw.gradient.Gradient": [],
|
||||
"Ext.draw.gradient.GradientDefinition": [],
|
||||
"Ext.draw.gradient.Linear": [],
|
||||
"Ext.draw.gradient.Radial": [],
|
||||
"Ext.draw.modifier.Animation": [],
|
||||
"Ext.draw.modifier.Highlight": [],
|
||||
"Ext.draw.modifier.Modifier": [],
|
||||
"Ext.draw.modifier.Target": [],
|
||||
"Ext.draw.sprite.AnimationParser": [],
|
||||
"Ext.draw.sprite.Arc": [],
|
||||
"Ext.draw.sprite.AttributeDefinition": [],
|
||||
"Ext.draw.sprite.AttributeParser": [],
|
||||
"Ext.draw.sprite.Circle": [],
|
||||
"Ext.draw.sprite.Composite": [],
|
||||
"Ext.draw.sprite.Ellipse": [],
|
||||
"Ext.draw.sprite.EllipticalArc": [],
|
||||
"Ext.draw.sprite.Image": [],
|
||||
"Ext.draw.sprite.Instancing": [],
|
||||
"Ext.draw.sprite.Line": [],
|
||||
"Ext.draw.sprite.Path": [],
|
||||
"Ext.draw.sprite.Rect": [],
|
||||
"Ext.draw.sprite.Sector": [],
|
||||
"Ext.draw.sprite.Sprite": [],
|
||||
"Ext.draw.sprite.Text": [],
|
||||
"Ext.event.Controller": [],
|
||||
"Ext.event.Dispatcher": [],
|
||||
"Ext.event.Dom": [],
|
||||
|
@ -15835,6 +15946,153 @@ Ext.ClassManager.addNameAliasMappings({
|
|||
],
|
||||
"Ext.carousel.Infinite": [],
|
||||
"Ext.carousel.Item": [],
|
||||
"Ext.chart.AbstractChart": [],
|
||||
"Ext.chart.CartesianChart": [
|
||||
"Ext.chart.Chart",
|
||||
"widget.chart"
|
||||
],
|
||||
"Ext.chart.Legend": [
|
||||
"widget.legend"
|
||||
],
|
||||
"Ext.chart.MarkerHolder": [],
|
||||
"Ext.chart.Markers": [],
|
||||
"Ext.chart.PolarChart": [
|
||||
"widget.polar"
|
||||
],
|
||||
"Ext.chart.SpaceFillingChart": [
|
||||
"widget.spacefilling"
|
||||
],
|
||||
"Ext.chart.axis.Axis": [
|
||||
"widget.axis"
|
||||
],
|
||||
"Ext.chart.axis.Category": [
|
||||
"axis.category"
|
||||
],
|
||||
"Ext.chart.axis.Numeric": [
|
||||
"axis.numeric"
|
||||
],
|
||||
"Ext.chart.axis.Time": [
|
||||
"axis.time"
|
||||
],
|
||||
"Ext.chart.axis.layout.CombineDuplicate": [
|
||||
"axisLayout.combineDuplicate"
|
||||
],
|
||||
"Ext.chart.axis.layout.Continuous": [
|
||||
"axisLayout.continuous"
|
||||
],
|
||||
"Ext.chart.axis.layout.Discrete": [
|
||||
"axisLayout.discrete"
|
||||
],
|
||||
"Ext.chart.axis.layout.Layout": [],
|
||||
"Ext.chart.axis.segmenter.Names": [
|
||||
"segmenter.names"
|
||||
],
|
||||
"Ext.chart.axis.segmenter.Numeric": [
|
||||
"segmenter.numeric"
|
||||
],
|
||||
"Ext.chart.axis.segmenter.Segmenter": [],
|
||||
"Ext.chart.axis.segmenter.Time": [
|
||||
"segmenter.time"
|
||||
],
|
||||
"Ext.chart.axis.sprite.Axis": [],
|
||||
"Ext.chart.grid.CircularGrid": [
|
||||
"grid.circular"
|
||||
],
|
||||
"Ext.chart.grid.HorizontalGrid": [
|
||||
"grid.horizontal"
|
||||
],
|
||||
"Ext.chart.grid.RadialGrid": [
|
||||
"grid.radial"
|
||||
],
|
||||
"Ext.chart.grid.VerticalGrid": [
|
||||
"grid.vertical"
|
||||
],
|
||||
"Ext.chart.interactions.Abstract": [
|
||||
"widget.interaction"
|
||||
],
|
||||
"Ext.chart.interactions.CrossZoom": [
|
||||
"interaction.crosszoom"
|
||||
],
|
||||
"Ext.chart.interactions.Crosshair": [
|
||||
"interaction.crosshair"
|
||||
],
|
||||
"Ext.chart.interactions.ItemHighlight": [
|
||||
"interaction.itemhighlight"
|
||||
],
|
||||
"Ext.chart.interactions.ItemInfo": [
|
||||
"interaction.iteminfo"
|
||||
],
|
||||
"Ext.chart.interactions.PanZoom": [
|
||||
"interaction.panzoom"
|
||||
],
|
||||
"Ext.chart.interactions.Rotate": [
|
||||
"interaction.rotate"
|
||||
],
|
||||
"Ext.chart.interactions.RotatePie3D": [
|
||||
"interaction.rotatePie3d"
|
||||
],
|
||||
"Ext.chart.label.Callout": [],
|
||||
"Ext.chart.label.Label": [],
|
||||
"Ext.chart.series.Area": [
|
||||
"series.area"
|
||||
],
|
||||
"Ext.chart.series.Bar": [
|
||||
"series.bar"
|
||||
],
|
||||
"Ext.chart.series.CandleStick": [
|
||||
"series.candlestick"
|
||||
],
|
||||
"Ext.chart.series.Cartesian": [],
|
||||
"Ext.chart.series.Gauge": [
|
||||
"series.gauge"
|
||||
],
|
||||
"Ext.chart.series.ItemPublisher": [],
|
||||
"Ext.chart.series.Line": [
|
||||
"series.line"
|
||||
],
|
||||
"Ext.chart.series.Pie": [
|
||||
"series.pie"
|
||||
],
|
||||
"Ext.chart.series.Pie3D": [
|
||||
"series.pie3d"
|
||||
],
|
||||
"Ext.chart.series.Polar": [],
|
||||
"Ext.chart.series.Radar": [
|
||||
"series.radar"
|
||||
],
|
||||
"Ext.chart.series.Scatter": [
|
||||
"series.scatter"
|
||||
],
|
||||
"Ext.chart.series.Series": [],
|
||||
"Ext.chart.series.StackedCartesian": [],
|
||||
"Ext.chart.series.sprite.Aggregative": [],
|
||||
"Ext.chart.series.sprite.Area": [
|
||||
"sprite.areaSeries"
|
||||
],
|
||||
"Ext.chart.series.sprite.Bar": [
|
||||
"sprite.barSeries"
|
||||
],
|
||||
"Ext.chart.series.sprite.CandleStick": [
|
||||
"sprite.candlestickSeries"
|
||||
],
|
||||
"Ext.chart.series.sprite.Cartesian": [],
|
||||
"Ext.chart.series.sprite.Line": [
|
||||
"sprite.lineSeries"
|
||||
],
|
||||
"Ext.chart.series.sprite.Pie3DPart": [
|
||||
"sprite.pie3dPart"
|
||||
],
|
||||
"Ext.chart.series.sprite.PieSlice": [
|
||||
"sprite.pieslice"
|
||||
],
|
||||
"Ext.chart.series.sprite.Polar": [],
|
||||
"Ext.chart.series.sprite.Radar": [
|
||||
"sprite.radar"
|
||||
],
|
||||
"Ext.chart.series.sprite.Scatter": [
|
||||
"sprite.scatterSeries"
|
||||
],
|
||||
"Ext.chart.series.sprite.StackedCartesian": [],
|
||||
"Ext.data.ArrayStore": [
|
||||
"store.array"
|
||||
],
|
||||
|
@ -16094,6 +16352,83 @@ Ext.ClassManager.addNameAliasMappings({
|
|||
"direct.transaction"
|
||||
],
|
||||
"Ext.dom.CompositeElement": [],
|
||||
"Ext.draw.Animator": [],
|
||||
"Ext.draw.Color": [],
|
||||
"Ext.draw.Component": [
|
||||
"widget.draw"
|
||||
],
|
||||
"Ext.draw.Draw": [],
|
||||
"Ext.draw.LimitedCache": [],
|
||||
"Ext.draw.Matrix": [],
|
||||
"Ext.draw.Path": [],
|
||||
"Ext.draw.SegmentTree": [],
|
||||
"Ext.draw.Solver": [],
|
||||
"Ext.draw.Surface": [
|
||||
"widget.surface"
|
||||
],
|
||||
"Ext.draw.TextMeasurer": [],
|
||||
"Ext.draw.TimingFunctions": [],
|
||||
"Ext.draw.engine.Canvas": [],
|
||||
"Ext.draw.engine.Svg": [],
|
||||
"Ext.draw.engine.SvgContext": [],
|
||||
"Ext.draw.engine.SvgContext.Gradient": [],
|
||||
"Ext.draw.engine.SvgExporter": [],
|
||||
"Ext.draw.gradient.Gradient": [],
|
||||
"Ext.draw.gradient.GradientDefinition": [],
|
||||
"Ext.draw.gradient.Linear": [],
|
||||
"Ext.draw.gradient.Radial": [],
|
||||
"Ext.draw.modifier.Animation": [
|
||||
"modifier.animation"
|
||||
],
|
||||
"Ext.draw.modifier.Highlight": [
|
||||
"modifier.highlight"
|
||||
],
|
||||
"Ext.draw.modifier.Modifier": [],
|
||||
"Ext.draw.modifier.Target": [
|
||||
"modifier.target"
|
||||
],
|
||||
"Ext.draw.sprite.AnimationParser": [],
|
||||
"Ext.draw.sprite.Arc": [
|
||||
"sprite.arc"
|
||||
],
|
||||
"Ext.draw.sprite.AttributeDefinition": [],
|
||||
"Ext.draw.sprite.AttributeParser": [],
|
||||
"Ext.draw.sprite.Circle": [
|
||||
"sprite.circle"
|
||||
],
|
||||
"Ext.draw.sprite.Composite": [
|
||||
"sprite.composite"
|
||||
],
|
||||
"Ext.draw.sprite.Ellipse": [
|
||||
"sprite.ellipse"
|
||||
],
|
||||
"Ext.draw.sprite.EllipticalArc": [
|
||||
"sprite.ellipticalArc"
|
||||
],
|
||||
"Ext.draw.sprite.Image": [
|
||||
"sprite.image"
|
||||
],
|
||||
"Ext.draw.sprite.Instancing": [
|
||||
"sprite.instancing"
|
||||
],
|
||||
"Ext.draw.sprite.Line": [
|
||||
"sprite.line"
|
||||
],
|
||||
"Ext.draw.sprite.Path": [
|
||||
"sprite.path"
|
||||
],
|
||||
"Ext.draw.sprite.Rect": [
|
||||
"sprite.rect"
|
||||
],
|
||||
"Ext.draw.sprite.Sector": [
|
||||
"sprite.sector"
|
||||
],
|
||||
"Ext.draw.sprite.Sprite": [
|
||||
"sprite.sprite"
|
||||
],
|
||||
"Ext.draw.sprite.Text": [
|
||||
"sprite.text"
|
||||
],
|
||||
"Ext.event.Controller": [],
|
||||
"Ext.event.Dispatcher": [],
|
||||
"Ext.event.Dom": [],
|
||||
|
|
13
vendor/touch/sencha-touch.js
vendored
13
vendor/touch/sencha-touch.js
vendored
File diff suppressed because one or more lines are too long
1313
vendor/touch/src/chart/AbstractChart.js
vendored
Normal file
1313
vendor/touch/src/chart/AbstractChart.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
304
vendor/touch/src/chart/CartesianChart.js
vendored
Normal file
304
vendor/touch/src/chart/CartesianChart.js
vendored
Normal file
|
@ -0,0 +1,304 @@
|
|||
/**
|
||||
* @class Ext.chart.CartesianChart
|
||||
* @extends Ext.chart.AbstractChart
|
||||
*
|
||||
* Represents a chart that uses cartesian coordinates.
|
||||
* A cartesian chart have two directions, X direction and Y direction.
|
||||
* The series and axes are coordinated along these directions.
|
||||
* By default the x direction is horizontal and y direction is vertical,
|
||||
* You can swap the by setting {@link #flipXY} config to `true`.
|
||||
*
|
||||
* Cartesian series often treats x direction an y direction differently.
|
||||
* In most cases, data on x direction are assumed to be monotonically increasing.
|
||||
* Based on this property, cartesian series can be trimmed and summarized properly
|
||||
* to gain a better performance.
|
||||
*
|
||||
* @xtype chart
|
||||
*/
|
||||
|
||||
Ext.define('Ext.chart.CartesianChart', {
|
||||
extend: 'Ext.chart.AbstractChart',
|
||||
alternateClassName: 'Ext.chart.Chart',
|
||||
requires: ['Ext.chart.grid.HorizontalGrid', 'Ext.chart.grid.VerticalGrid'],
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Boolean} flipXY Flip the direction of X and Y axis.
|
||||
* If flipXY is true, the X axes will be vertical and Y axes will be horizontal.
|
||||
*/
|
||||
flipXY: false,
|
||||
|
||||
innerRegion: [0, 0, 1, 1]
|
||||
},
|
||||
xtype: 'chart',
|
||||
alias: 'Ext.chart.Chart',
|
||||
|
||||
getDirectionForAxis: function (position) {
|
||||
var flipXY = this.getFlipXY();
|
||||
if (position === 'left' || position === 'right') {
|
||||
if (flipXY) {
|
||||
return 'X';
|
||||
} else {
|
||||
return 'Y';
|
||||
}
|
||||
} else {
|
||||
if (flipXY) {
|
||||
return 'Y';
|
||||
} else {
|
||||
return 'X';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Layout the axes and series.
|
||||
*/
|
||||
performLayout: function () {
|
||||
try {
|
||||
this.resizing++;
|
||||
this.callSuper();
|
||||
this.suspendThicknessChanged();
|
||||
var me = this,
|
||||
axes = me.getAxes(), axis,
|
||||
seriesList = me.getSeries(), series,
|
||||
axisSurface, thickness,
|
||||
size = me.element.getSize(),
|
||||
width = size.width,
|
||||
height = size.height,
|
||||
insetPadding = me.getInsetPadding(),
|
||||
innerPadding = me.getInnerPadding(),
|
||||
surface,
|
||||
shrinkBox = {
|
||||
top: insetPadding.top,
|
||||
left: insetPadding.left,
|
||||
right: insetPadding.right,
|
||||
bottom: insetPadding.bottom
|
||||
},
|
||||
gridSurface,
|
||||
mainRegion, innerWidth, innerHeight,
|
||||
elements, floating, matrix, i, ln,
|
||||
flipXY = me.getFlipXY();
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
axis = axes[i];
|
||||
axisSurface = axis.getSurface();
|
||||
floating = axis.getStyle && axis.getStyle() && axis.getStyle().floating;
|
||||
thickness = axis.getThickness();
|
||||
switch (axis.getPosition()) {
|
||||
case 'top':
|
||||
axisSurface.setRegion([0, shrinkBox.top, width, thickness]);
|
||||
break;
|
||||
case 'bottom':
|
||||
axisSurface.setRegion([0, height - (shrinkBox.bottom + thickness), width, thickness]);
|
||||
break;
|
||||
case 'left':
|
||||
axisSurface.setRegion([shrinkBox.left, 0, thickness, height]);
|
||||
break;
|
||||
case 'right':
|
||||
axisSurface.setRegion([width - (shrinkBox.right + thickness), 0, thickness, height]);
|
||||
break;
|
||||
}
|
||||
if (!floating) {
|
||||
shrinkBox[axis.getPosition()] += thickness;
|
||||
}
|
||||
}
|
||||
|
||||
width -= shrinkBox.left + shrinkBox.right;
|
||||
height -= shrinkBox.top + shrinkBox.bottom;
|
||||
|
||||
mainRegion = [shrinkBox.left, shrinkBox.top, width, height];
|
||||
|
||||
shrinkBox.left += innerPadding.left;
|
||||
shrinkBox.top += innerPadding.top;
|
||||
shrinkBox.right += innerPadding.right;
|
||||
shrinkBox.bottom += innerPadding.bottom;
|
||||
|
||||
innerWidth = width - innerPadding.left - innerPadding.right;
|
||||
innerHeight = height - innerPadding.top - innerPadding.bottom;
|
||||
|
||||
me.setInnerRegion([shrinkBox.left, shrinkBox.top, innerWidth, innerHeight]);
|
||||
|
||||
if (innerWidth <= 0 || innerHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
me.setMainRegion(mainRegion);
|
||||
me.getSurface('main').setRegion(mainRegion);
|
||||
|
||||
for (i = 0, ln = me.surfaceMap.grid && me.surfaceMap.grid.length; i < ln; i++) {
|
||||
gridSurface = me.surfaceMap.grid[i];
|
||||
gridSurface.setRegion(mainRegion);
|
||||
gridSurface.matrix.set(1, 0, 0, 1, innerPadding.left, innerPadding.top);
|
||||
gridSurface.matrix.inverse(gridSurface.inverseMatrix);
|
||||
}
|
||||
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
axis = axes[i];
|
||||
axisSurface = axis.getSurface();
|
||||
matrix = axisSurface.matrix;
|
||||
elements = matrix.elements;
|
||||
switch (axis.getPosition()) {
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
elements[4] = shrinkBox.left;
|
||||
axis.setLength(innerWidth);
|
||||
break;
|
||||
case 'left':
|
||||
case 'right':
|
||||
elements[5] = shrinkBox.top;
|
||||
axis.setLength(innerHeight);
|
||||
break;
|
||||
}
|
||||
axis.updateTitleSprite();
|
||||
matrix.inverse(axisSurface.inverseMatrix);
|
||||
}
|
||||
|
||||
for (i = 0, ln = seriesList.length; i < ln; i++) {
|
||||
series = seriesList[i];
|
||||
surface = series.getSurface();
|
||||
surface.setRegion(mainRegion);
|
||||
if (flipXY) {
|
||||
surface.matrix.set(0, -1, 1, 0, innerPadding.left, innerHeight + innerPadding.top);
|
||||
} else {
|
||||
surface.matrix.set(1, 0, 0, -1, innerPadding.left, innerHeight + innerPadding.top);
|
||||
}
|
||||
surface.matrix.inverse(surface.inverseMatrix);
|
||||
series.getOverlaySurface().setRegion(mainRegion);
|
||||
}
|
||||
me.redraw();
|
||||
me.onPlaceWatermark();
|
||||
} finally {
|
||||
this.resizing--;
|
||||
this.resumeThicknessChanged();
|
||||
}
|
||||
},
|
||||
|
||||
redraw: function () {
|
||||
var me = this,
|
||||
series = me.getSeries(),
|
||||
axes = me.getAxes(),
|
||||
region = me.getMainRegion(),
|
||||
innerWidth, innerHeight,
|
||||
innerPadding = me.getInnerPadding(),
|
||||
left, right, top, bottom, i, j,
|
||||
sprites, xRange, yRange, isSide, attr,
|
||||
axisX, axisY, range, visibleRange,
|
||||
flipXY = me.getFlipXY(),
|
||||
sprite, zIndex, zBase = 1000,
|
||||
markers, markerCount, markerIndex, markerSprite, markerZIndex;
|
||||
|
||||
if (!region) {
|
||||
return;
|
||||
}
|
||||
|
||||
innerWidth = region[2] - innerPadding.left - innerPadding.right;
|
||||
innerHeight = region[3] - innerPadding.top - innerPadding.bottom;
|
||||
for (i = 0; i < series.length; i++) {
|
||||
if ((axisX = series[i].getXAxis())) {
|
||||
visibleRange = axisX.getVisibleRange();
|
||||
xRange = axisX.getRange();
|
||||
xRange = [xRange[0] + (xRange[1] - xRange[0]) * visibleRange[0], xRange[0] + (xRange[1] - xRange[0]) * visibleRange[1]];
|
||||
} else {
|
||||
xRange = series[i].getXRange();
|
||||
}
|
||||
|
||||
if ((axisY = series[i].getYAxis())) {
|
||||
visibleRange = axisY.getVisibleRange();
|
||||
yRange = axisY.getRange();
|
||||
yRange = [yRange[0] + (yRange[1] - yRange[0]) * visibleRange[0], yRange[0] + (yRange[1] - yRange[0]) * visibleRange[1]];
|
||||
} else {
|
||||
yRange = series[i].getYRange();
|
||||
}
|
||||
|
||||
left = xRange[0];
|
||||
right = xRange[1];
|
||||
top = yRange[0];
|
||||
bottom = yRange[1];
|
||||
|
||||
attr = {
|
||||
visibleMinX: xRange[0],
|
||||
visibleMaxX: xRange[1],
|
||||
visibleMinY: yRange[0],
|
||||
visibleMaxY: yRange[1],
|
||||
innerWidth: innerWidth,
|
||||
innerHeight: innerHeight,
|
||||
flipXY: flipXY
|
||||
};
|
||||
|
||||
sprites = series[i].getSprites();
|
||||
for (j = 0; j < sprites.length; j++) {
|
||||
|
||||
// All the series now share the same surface, so we must assign
|
||||
// the sprites a zIndex that depends on the index of their series.
|
||||
sprite = sprites[j];
|
||||
zIndex = (sprite.attr.zIndex || 0);
|
||||
if (zIndex < zBase) {
|
||||
// Set the sprite's zIndex
|
||||
zIndex += (i+1) * 100 + zBase;
|
||||
sprite.attr.zIndex = zIndex;
|
||||
// Iterate through its marker sprites to do the same.
|
||||
markers = sprite.boundMarkers;
|
||||
if (markers) {
|
||||
markerCount = (markers.items ? markers.items.length : 0);
|
||||
if (markerCount) {
|
||||
for (markerIndex = 0; markerIndex < markerCount; markerIndex++) {
|
||||
markerSprite = markers.items[markerIndex];
|
||||
markerZIndex = (markerSprite.attr.zIndex || 0);
|
||||
if (markerZIndex == Number.MAX_VALUE) {
|
||||
markerSprite.attr.zIndex = zIndex;
|
||||
} else {
|
||||
if (markerZIndex < zBase) {
|
||||
markerSprite.attr.zIndex = zIndex + markerZIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sprite.setAttributes(attr, true);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
isSide = axes[i].isSide();
|
||||
sprites = axes[i].getSprites();
|
||||
range = axes[i].getRange();
|
||||
visibleRange = axes[i].getVisibleRange();
|
||||
attr = {
|
||||
dataMin: range[0],
|
||||
dataMax: range[1],
|
||||
visibleMin: visibleRange[0],
|
||||
visibleMax: visibleRange[1]
|
||||
};
|
||||
if (isSide) {
|
||||
attr.length = innerHeight;
|
||||
attr.startGap = innerPadding.bottom;
|
||||
attr.endGap = innerPadding.top;
|
||||
} else {
|
||||
attr.length = innerWidth;
|
||||
attr.startGap = innerPadding.left;
|
||||
attr.endGap = innerPadding.right;
|
||||
}
|
||||
for (j = 0; j < sprites.length; j++) {
|
||||
sprites[j].setAttributes(attr, true);
|
||||
}
|
||||
}
|
||||
me.renderFrame();
|
||||
me.callSuper(arguments);
|
||||
},
|
||||
|
||||
onPlaceWatermark: function () {
|
||||
var region0 = this.element.getBox(),
|
||||
region = this.getSurface ? this.getSurface('main').getRegion() : this.getItems().get(0).getRegion();
|
||||
if (region) {
|
||||
this.watermarkElement.setStyle({
|
||||
right: Math.round(region0.width - (region[2] + region[0])) + 'px',
|
||||
bottom: Math.round(region0.height - (region[3] + region[1])) + 'px'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
147
vendor/touch/src/chart/Legend.js
vendored
Normal file
147
vendor/touch/src/chart/Legend.js
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* @class Ext.chart.Legend
|
||||
* @extends Ext.dataview.DataView
|
||||
*
|
||||
* A default legend for charts.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.Chart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* legend: {
|
||||
* position: 'bottom'
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'area',
|
||||
* title: ['Data1', 'Data2', 'Data3'],
|
||||
* subStyle: {
|
||||
* fill: ['blue', 'green', 'red']
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: ['data1', 'data2', 'data3']
|
||||
*
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define("Ext.chart.Legend", {
|
||||
xtype: 'legend',
|
||||
extend: "Ext.dataview.DataView",
|
||||
config: {
|
||||
itemTpl: [
|
||||
"<span class=\"x-legend-item-marker {[values.disabled?\'x-legend-inactive\':\'\']}\" style=\"background:{mark};\"></span>{name}"
|
||||
],
|
||||
baseCls: 'x-legend',
|
||||
padding: 5,
|
||||
disableSelection: true,
|
||||
inline: true,
|
||||
/**
|
||||
* @cfg {String} position
|
||||
* @deprecated Use `docked` instead.
|
||||
* Delegates to `docked`
|
||||
*/
|
||||
position: null,
|
||||
/**
|
||||
* @cfg {Boolean} toggleable 'true' if the series items in the legend can be toggled on and off.
|
||||
*/
|
||||
toggleable: true,
|
||||
docked: 'top',
|
||||
horizontalHeight: 48,
|
||||
verticalWidth: 150
|
||||
},
|
||||
|
||||
constructor: function () {
|
||||
this.callSuper(arguments);
|
||||
|
||||
var scroller = this.getScrollable().getScroller(),
|
||||
onDrag = scroller.onDrag;
|
||||
scroller.onDrag = function (e) {
|
||||
e.stopPropagation();
|
||||
onDrag.call(this, e);
|
||||
};
|
||||
},
|
||||
|
||||
doSetDocked: function (docked) {
|
||||
this.callSuper(arguments);
|
||||
if (docked === 'top' || docked === 'bottom') {
|
||||
this.setLayout({type: 'hbox', pack: 'center'});
|
||||
this.setInline(true);
|
||||
// TODO: Remove this when possible
|
||||
this.setWidth(null);
|
||||
this.setHeight(this.getHorizontalHeight());
|
||||
if (this.getScrollable()) {
|
||||
this.setScrollable({direction: 'horizontal'});
|
||||
}
|
||||
} else {
|
||||
this.setLayout({pack: 'center'});
|
||||
this.setInline(false);
|
||||
// TODO: Remove this when possible
|
||||
this.setWidth(this.getVerticalWidth());
|
||||
this.setHeight(null);
|
||||
if (this.getScrollable()) {
|
||||
this.setScrollable({direction: 'vertical'});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setScrollable: function (scrollable) {
|
||||
this.callSuper(arguments);
|
||||
if (scrollable === true) {
|
||||
if (this.getDocked() === 'top' || this.getDocked() === 'bottom') {
|
||||
this.setScrollable({direction: 'horizontal'});
|
||||
} else if (this.getDocked() === 'left' || this.getDocked() === 'right') {
|
||||
this.setScrollable({direction: 'vertical'});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
setPosition: function (position) {
|
||||
this.setDocked(position);
|
||||
},
|
||||
|
||||
getPosition: function () {
|
||||
return this.getDocked();
|
||||
},
|
||||
|
||||
onItemTap: function (container, target, index, e) {
|
||||
this.callSuper(arguments);
|
||||
if(this.getToggleable()) {
|
||||
var me = this,
|
||||
store = me.getStore(),
|
||||
record = store && store.getAt(index);
|
||||
record.beginEdit();
|
||||
record.set('disabled', !record.get('disabled'));
|
||||
record.commit();
|
||||
}
|
||||
}
|
||||
});
|
109
vendor/touch/src/chart/MarkerHolder.js
vendored
Normal file
109
vendor/touch/src/chart/MarkerHolder.js
vendored
Normal file
|
@ -0,0 +1,109 @@
|
|||
/**
|
||||
* @class Ext.chart.MarkerHolder
|
||||
* @extends Ext.mixin.Mixin
|
||||
*
|
||||
* Mixin that provides the functionality to place markers.
|
||||
*/
|
||||
Ext.define('Ext.chart.MarkerHolder', {
|
||||
extend: 'Ext.mixin.Mixin',
|
||||
mixinConfig: {
|
||||
id: 'markerHolder',
|
||||
hooks: {
|
||||
constructor: 'constructor',
|
||||
preRender: 'preRender'
|
||||
}
|
||||
},
|
||||
|
||||
isMarkerHolder: true,
|
||||
|
||||
constructor: function () {
|
||||
this.boundMarkers = {};
|
||||
this.cleanRedraw = false;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Ext.chart.Markers} marker
|
||||
*/
|
||||
bindMarker: function (name, marker) {
|
||||
if (marker) {
|
||||
if (!this.boundMarkers[name]) {
|
||||
this.boundMarkers[name] = [];
|
||||
}
|
||||
Ext.Array.include(this.boundMarkers[name], marker);
|
||||
}
|
||||
},
|
||||
|
||||
getBoundMarker: function (name) {
|
||||
return this.boundMarkers[name];
|
||||
},
|
||||
|
||||
preRender: function () {
|
||||
var boundMarkers = this.boundMarkers, boundMarkersItem,
|
||||
name, i, ln, id = this.getId(),
|
||||
parent = this.getParent(),
|
||||
matrix = this.surfaceMatrix ? this.surfaceMatrix.set(1, 0, 0, 1, 0, 0) : (this.surfaceMatrix = new Ext.draw.Matrix());
|
||||
|
||||
this.cleanRedraw = !this.attr.dirty;
|
||||
if (!this.cleanRedraw) {
|
||||
for (name in this.boundMarkers) {
|
||||
if (boundMarkers[name]) {
|
||||
for (boundMarkersItem = boundMarkers[name], i = 0, ln = boundMarkersItem.length; i < ln; i++) {
|
||||
boundMarkersItem[i].clear(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (parent && parent.attr && parent.attr.matrix) {
|
||||
matrix.prependMatrix(parent.attr.matrix);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
matrix.prependMatrix(parent.matrix);
|
||||
this.surfaceMatrix = matrix;
|
||||
this.inverseSurfaceMatrix = matrix.inverse(this.inverseSurfaceMatrix);
|
||||
},
|
||||
|
||||
putMarker: function (name, markerAttr, index, canonical, keepRevision) {
|
||||
var boundMarkersItem, i, ln, id = this.getId();
|
||||
if (this.boundMarkers[name]) {
|
||||
for (boundMarkersItem = this.boundMarkers[name], i = 0, ln = boundMarkersItem.length; i < ln; i++) {
|
||||
boundMarkersItem[i].putMarkerFor(id, markerAttr, index, canonical);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getMarkerBBox: function (name, index, isWithoutTransform) {
|
||||
var id = this.getId(),
|
||||
left = Infinity,
|
||||
right = -Infinity,
|
||||
top = Infinity,
|
||||
bottom = -Infinity,
|
||||
bbox, boundMarker, i, ln;
|
||||
|
||||
if (this.boundMarkers[name]) {
|
||||
for (boundMarker = this.boundMarkers[name], i = 0, ln = boundMarker.length; i < ln; i++) {
|
||||
bbox = boundMarker[i].getMarkerBBoxFor(id, index, isWithoutTransform);
|
||||
if (left > bbox.x) {
|
||||
left = bbox.x;
|
||||
}
|
||||
if (right < bbox.x + bbox.width) {
|
||||
right = bbox.x + bbox.width;
|
||||
}
|
||||
if (top > bbox.y) {
|
||||
top = bbox.y;
|
||||
}
|
||||
if (bottom < bbox.y + bbox.height) {
|
||||
bottom = bbox.y + bbox.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: left,
|
||||
y: top,
|
||||
width: right - left,
|
||||
height: bottom - top
|
||||
};
|
||||
}
|
||||
});
|
103
vendor/touch/src/chart/Markers.js
vendored
Normal file
103
vendor/touch/src/chart/Markers.js
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* @class Ext.chart.Markers
|
||||
* @extends Ext.draw.sprite.Instancing
|
||||
*
|
||||
* Marker sprite. A specialized version of instancing sprite that groups instances.
|
||||
* Putting a marker is grouped by its category id. Clearing removes that category.
|
||||
*/
|
||||
Ext.define('Ext.chart.Markers', {
|
||||
extend: 'Ext.draw.sprite.Instancing',
|
||||
revisions: 0,
|
||||
|
||||
constructor: function () {
|
||||
this.callSuper(arguments);
|
||||
this.map = {};
|
||||
this.revisions = {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the markers in the category
|
||||
* @param {String} category
|
||||
*/
|
||||
clear: function (category) {
|
||||
category = category || 'default';
|
||||
if (!(category in this.revisions)) {
|
||||
this.revisions[category] = 1;
|
||||
} else {
|
||||
this.revisions[category]++;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Put a marker in the category with additional
|
||||
* attributes.
|
||||
* @param {String} category
|
||||
* @param {Object} markerAttr
|
||||
* @param {String|Number} index
|
||||
* @param {Boolean} [canonical]
|
||||
* @param {Boolean} [keepRevision]
|
||||
*/
|
||||
putMarkerFor: function (category, markerAttr, index, canonical, keepRevision) {
|
||||
category = category || 'default';
|
||||
|
||||
var me = this,
|
||||
map = me.map[category] || (me.map[category] = {});
|
||||
if (index in map) {
|
||||
me.setAttributesFor(map[index], markerAttr, canonical);
|
||||
} else {
|
||||
map[index] = me.instances.length;
|
||||
me.createInstance(markerAttr, null, canonical);
|
||||
}
|
||||
me.instances[map[index]].category = category;
|
||||
if (!keepRevision) {
|
||||
me.instances[map[index]].revision = me.revisions[category] || (me.revisions[category] = 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} category
|
||||
* @param {Mixed} index
|
||||
* @param {Boolean} [isWithoutTransform]
|
||||
*/
|
||||
getMarkerBBoxFor: function (category, index, isWithoutTransform) {
|
||||
if (category in this.map) {
|
||||
if (index in this.map[category]) {
|
||||
return this.getBBoxFor(this.map[category][index], isWithoutTransform);
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: Infinity,
|
||||
y: Infinity,
|
||||
width: -Infinity,
|
||||
height: -Infinity
|
||||
};
|
||||
},
|
||||
|
||||
getBBox: function () { return null; },
|
||||
|
||||
render: function (surface, ctx, clipRegion) {
|
||||
var me = this,
|
||||
revisions = me.revisions,
|
||||
mat = me.attr.matrix,
|
||||
template = me.getTemplate(),
|
||||
originalAttr = template.attr,
|
||||
instances = me.instances,
|
||||
i, ln = me.instances.length;
|
||||
mat.toContext(ctx);
|
||||
template.preRender(surface, ctx, clipRegion);
|
||||
template.useAttributes(ctx);
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (instances[i].hidden || instances[i].revision !== revisions[instances[i].category]) {
|
||||
continue;
|
||||
}
|
||||
ctx.save();
|
||||
template.attr = instances[i];
|
||||
template.applyTransformations();
|
||||
template.useAttributes(ctx);
|
||||
template.render(surface, ctx, clipRegion);
|
||||
ctx.restore();
|
||||
}
|
||||
template.attr = originalAttr;
|
||||
}
|
||||
});
|
164
vendor/touch/src/chart/PolarChart.js
vendored
Normal file
164
vendor/touch/src/chart/PolarChart.js
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
/**
|
||||
* @class Ext.chart.PolarChart
|
||||
* @extends Ext.chart.AbstractChart
|
||||
*
|
||||
* Creates a chart that uses polar coordinates.
|
||||
*/
|
||||
Ext.define('Ext.chart.PolarChart', {
|
||||
|
||||
requires: [
|
||||
'Ext.chart.grid.CircularGrid',
|
||||
'Ext.chart.grid.RadialGrid'
|
||||
],
|
||||
|
||||
extend: 'Ext.chart.AbstractChart',
|
||||
xtype: 'polar',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Array} center Determines the center of the polar chart.
|
||||
* Updated when the chart performs layout.
|
||||
*/
|
||||
center: [0, 0],
|
||||
/**
|
||||
* @cfg {Number} radius Determines the radius of the polar chart.
|
||||
* Updated when the chart performs layout.
|
||||
*/
|
||||
radius: 0
|
||||
},
|
||||
|
||||
getDirectionForAxis: function (position) {
|
||||
if (position === 'radial') {
|
||||
return 'Y';
|
||||
} else {
|
||||
return 'X';
|
||||
}
|
||||
},
|
||||
|
||||
applyCenter: function (center, oldCenter) {
|
||||
if (oldCenter && center[0] === oldCenter[0] && center[1] === oldCenter[1]) {
|
||||
return;
|
||||
}
|
||||
return [+center[0], +center[1]];
|
||||
},
|
||||
|
||||
updateCenter: function (center) {
|
||||
var me = this,
|
||||
axes = me.getAxes(), axis,
|
||||
series = me.getSeries(), seriesItem,
|
||||
i, ln;
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
axis.setCenter(center);
|
||||
}
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
seriesItem.setCenter(center);
|
||||
}
|
||||
},
|
||||
|
||||
updateRadius: function (radius) {
|
||||
var me = this,
|
||||
axes = me.getAxes(), axis,
|
||||
series = me.getSeries(), seriesItem,
|
||||
i, ln;
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
axis.setMinimum(0);
|
||||
axis.setLength(radius);
|
||||
axis.getSprites();
|
||||
}
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
seriesItem.setRadius(radius);
|
||||
}
|
||||
},
|
||||
|
||||
doSetSurfaceRegion: function (surface, region) {
|
||||
var mainRegion = this.getMainRegion();
|
||||
surface.setRegion(region);
|
||||
surface.matrix.set(1, 0, 0, 1, mainRegion[0] - region[0], mainRegion[1] - region[1]);
|
||||
surface.inverseMatrix.set(1, 0, 0, 1, region[0] - mainRegion[0], region[1] - mainRegion[1]);
|
||||
},
|
||||
|
||||
performLayout: function () {
|
||||
try {
|
||||
this.resizing++;
|
||||
this.callSuper();
|
||||
var me = this,
|
||||
size = me.element.getSize(),
|
||||
fullRegion = [0, 0, size.width, size.height],
|
||||
|
||||
inset = me.getInsetPadding(),
|
||||
inner = me.getInnerPadding(),
|
||||
|
||||
left = inset.left,
|
||||
top = inset.top,
|
||||
width = size.width - left - inset.right,
|
||||
height = size.height - top - inset.bottom,
|
||||
region = [inset.left, inset.top, width, height],
|
||||
|
||||
innerWidth = width - inner.left - inner.right,
|
||||
innerHeight = height - inner.top - inner.bottom,
|
||||
|
||||
center = [innerWidth * 0.5 + inner.left, innerHeight * 0.5 + inner.top],
|
||||
radius = Math.min(innerWidth, innerHeight) * 0.5,
|
||||
axes = me.getAxes(), axis,
|
||||
series = me.getSeries(), seriesItem,
|
||||
i, ln;
|
||||
|
||||
me.setMainRegion(region);
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
me.doSetSurfaceRegion(seriesItem.getSurface(), region);
|
||||
me.doSetSurfaceRegion(seriesItem.getOverlaySurface(), fullRegion);
|
||||
}
|
||||
|
||||
me.doSetSurfaceRegion(me.getSurface(), fullRegion);
|
||||
for (i = 0, ln = me.surfaceMap.grid && me.surfaceMap.grid.length; i < ln; i++) {
|
||||
me.doSetSurfaceRegion(me.surfaceMap.grid[i], fullRegion);
|
||||
}
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
me.doSetSurfaceRegion(axis.getSurface(), fullRegion);
|
||||
}
|
||||
|
||||
me.setRadius(radius);
|
||||
me.setCenter(center);
|
||||
me.redraw();
|
||||
} finally {
|
||||
this.resizing--;
|
||||
}
|
||||
},
|
||||
|
||||
getEventXY: function (e) {
|
||||
e = (e.changedTouches && e.changedTouches[0]) || e.event || e.browserEvent || e;
|
||||
var me = this,
|
||||
xy = me.element.getXY(),
|
||||
padding = me.getInsetPadding();
|
||||
return [e.pageX - xy[0] - padding.left, e.pageY - xy[1] - padding.top];
|
||||
},
|
||||
|
||||
redraw: function () {
|
||||
var me = this,
|
||||
axes = me.getAxes(), axis,
|
||||
series = me.getSeries(), seriesItem,
|
||||
i, ln;
|
||||
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
axis.getSprites();
|
||||
}
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
seriesItem.getSprites();
|
||||
}
|
||||
|
||||
me.renderFrame();
|
||||
me.callSuper(arguments);
|
||||
}
|
||||
});
|
58
vendor/touch/src/chart/SpaceFillingChart.js
vendored
Normal file
58
vendor/touch/src/chart/SpaceFillingChart.js
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* @class Ext.chart.SpaceFillingChart
|
||||
* @extends Ext.chart.AbstractChart
|
||||
*
|
||||
* Creates a chart that fills the entire area of the chart.
|
||||
* e.g. Gauge Charts
|
||||
*/
|
||||
Ext.define('Ext.chart.SpaceFillingChart', {
|
||||
|
||||
extend: 'Ext.chart.AbstractChart',
|
||||
xtype: 'spacefilling',
|
||||
|
||||
config: {
|
||||
|
||||
},
|
||||
|
||||
performLayout: function () {
|
||||
try {
|
||||
this.resizing++;
|
||||
this.callSuper();
|
||||
var me = this,
|
||||
size = me.element.getSize(),
|
||||
series = me.getSeries(), seriesItem,
|
||||
padding = me.getInsetPadding(),
|
||||
width = size.width - padding.left - padding.right,
|
||||
height = size.height - padding.top - padding.bottom,
|
||||
region = [padding.left, padding.top, width, height],
|
||||
fullRegion = [0, 0, size.width, size.height],
|
||||
i, ln;
|
||||
me.getSurface().setRegion(region);
|
||||
me.setMainRegion(region);
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
seriesItem.getSurface().setRegion(region);
|
||||
seriesItem.setRegion(region);
|
||||
|
||||
seriesItem.getOverlaySurface().setRegion(fullRegion);
|
||||
}
|
||||
me.redraw();
|
||||
} finally {
|
||||
this.resizing--;
|
||||
}
|
||||
},
|
||||
|
||||
redraw: function () {
|
||||
var me = this,
|
||||
series = me.getSeries(), seriesItem,
|
||||
i, ln;
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
seriesItem.getSprites();
|
||||
}
|
||||
|
||||
me.renderFrame();
|
||||
me.callSuper(arguments);
|
||||
}
|
||||
});
|
890
vendor/touch/src/chart/axis/Axis.js
vendored
Normal file
890
vendor/touch/src/chart/axis/Axis.js
vendored
Normal file
|
@ -0,0 +1,890 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.Axis
|
||||
*
|
||||
* Defines axis for charts.
|
||||
*
|
||||
* Using the current model, the type of axis can be easily extended. By default, Sencha Touch provides three different
|
||||
* types of axis:
|
||||
*
|
||||
* * **numeric** - the data attached with this axes are considered to be numeric and continuous.
|
||||
* * **time** - the data attached with this axes are considered (or get converted into) date/time and they are continuous.
|
||||
* * **category** - the data attached with this axes conforms a finite set. They will be evenly placed on the axis and displayed in the same form they were provided.
|
||||
*
|
||||
* The behavior of an axis can be easily changed by setting different types of axis layout and axis segmenter to the axis.
|
||||
*
|
||||
* Axis layout defines how the data points are placed. Using continuous layout, the data points will be distributed by
|
||||
* the numeric value. Using discrete layout the data points will be spaced evenly. Furthermore, if you want to combine
|
||||
* the data points with the duplicate values in a discrete layout, you should use combineDuplicate layout.
|
||||
*
|
||||
* Segmenter defines the way to segment data range. For example, if you have a Date-type data range from Jan 1, 1997 to
|
||||
* Jan 1, 2017, the segmenter will segement the data range into years, months or days based on the current zooming
|
||||
* level.
|
||||
*
|
||||
* It is possible to write custom axis layouts and segmenters to extends this behavior by simply implementing interfaces
|
||||
* {@link Ext.chart.axis.layout.Layout} and {@link Ext.chart.axis.segmenter.Segmenter}.
|
||||
*
|
||||
* Here's an example for the axes part of a chart definition:
|
||||
* An example of axis for a series (in this case for an area chart that has multiple layers of yFields) could be:
|
||||
*
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* title: 'Number of Hits',
|
||||
* grid: {
|
||||
* odd: {
|
||||
* opacity: 1,
|
||||
* fill: '#ddd',
|
||||
* stroke: '#bbb',
|
||||
* lineWidth: 1
|
||||
* }
|
||||
* },
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* title: 'Month of the Year',
|
||||
* grid: true,
|
||||
* label: {
|
||||
* rotate: {
|
||||
* degrees: 315
|
||||
* }
|
||||
* }
|
||||
* }]
|
||||
*
|
||||
* In this case we use a `numeric` axis for displaying the values of the Area series and a `category` axis for displaying the names of
|
||||
* the store elements. The numeric axis is placed on the left of the screen, while the category axis is placed at the bottom of the chart.
|
||||
* Both the category and numeric axes have `grid` set, which means that horizontal and vertical lines will cover the chart background. In the
|
||||
* category axis the labels will be rotated so they can fit the space better.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.Axis', {
|
||||
xtype: 'axis',
|
||||
|
||||
mixins: {
|
||||
observable: 'Ext.mixin.Observable'
|
||||
},
|
||||
|
||||
requires: [
|
||||
'Ext.chart.axis.sprite.Axis',
|
||||
'Ext.chart.axis.segmenter.*',
|
||||
'Ext.chart.axis.layout.*'
|
||||
],
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} position
|
||||
* Where to set the axis. Available options are `left`, `bottom`, `right`, `top`, `radial` and `angular`.
|
||||
*/
|
||||
position: 'bottom',
|
||||
|
||||
/**
|
||||
* @cfg {Array} fields
|
||||
* An array containing the names of the record fields which should be mapped along the axis.
|
||||
* This is optional if the binding between series and fields is clear.
|
||||
*/
|
||||
fields: [],
|
||||
|
||||
/**
|
||||
* @cfg {Object} label
|
||||
*
|
||||
* The label configuration object for the Axis. This object may include style attributes
|
||||
* like `spacing`, `padding`, `font` that receives a string or number and
|
||||
* returns a new string with the modified values.
|
||||
*
|
||||
* For more supported values, see the configurations for {@link Ext.chart.label.Label}.
|
||||
*/
|
||||
label: { x: 0, y: 0, textBaseline: 'middle', textAlign: 'center', fontSize: 12, fontFamily: 'Helvetica' },
|
||||
|
||||
/**
|
||||
* @cfg {Object} grid
|
||||
* The grid configuration object for the Axis style. Can contain `stroke` or `fill` attributes.
|
||||
* Also may contain an `odd` or `even` property in which you only style things on odd or even rows.
|
||||
* For example:
|
||||
*
|
||||
*
|
||||
* grid {
|
||||
* odd: {
|
||||
* stroke: '#555'
|
||||
* },
|
||||
* even: {
|
||||
* stroke: '#ccc'
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
grid: false,
|
||||
|
||||
/**
|
||||
* @cfg {Function} renderer Allows direct customisation of rendered axis sprites.
|
||||
* @param {String} label The label.
|
||||
* @param {Object|Ext.chart.axis.layout.Layout} layout The layout configuration used by the axis.
|
||||
* @param {String} lastLabel The last label.
|
||||
* @return {String} The label to display.
|
||||
*/
|
||||
renderer: null,
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @cfg {Ext.chart.AbstractChart} chart The Chart that the Axis is bound.
|
||||
*/
|
||||
chart: null,
|
||||
|
||||
/**
|
||||
* @cfg {Object} style
|
||||
* The style for the axis line and ticks.
|
||||
* Refer to the {@link Ext.chart.axis.sprite.Axis}
|
||||
*/
|
||||
style: null,
|
||||
|
||||
/**
|
||||
* @cfg {Number} titleMargin
|
||||
* The margin around the axis title. Unlike CSS where the margin is added on all 4
|
||||
* sides of an element, the `titleMargin` is the total space that is added horizontally
|
||||
* for a vertical title and vertically for an horizontal title, with half the `titleMargin`
|
||||
* being added on either side.
|
||||
*/
|
||||
titleMargin: 4,
|
||||
|
||||
/**
|
||||
* @cfg {Object} background
|
||||
* The background config for the axis surface.
|
||||
*/
|
||||
background: null,
|
||||
|
||||
/**
|
||||
* @cfg {Number} minimum
|
||||
* The minimum value drawn by the axis. If not set explicitly, the axis
|
||||
* minimum will be calculated automatically.
|
||||
*/
|
||||
minimum: NaN,
|
||||
|
||||
/**
|
||||
* @cfg {Number} maximum
|
||||
* The maximum value drawn by the axis. If not set explicitly, the axis
|
||||
* maximum will be calculated automatically.
|
||||
*/
|
||||
maximum: NaN,
|
||||
|
||||
/**
|
||||
* @cfg {Number} minZoom
|
||||
* The minimum zooming level for axis.
|
||||
*/
|
||||
minZoom: 1,
|
||||
|
||||
/**
|
||||
* @cfg {Number} maxZoom
|
||||
* The maximum zooming level for axis
|
||||
*/
|
||||
maxZoom: 10000,
|
||||
|
||||
/**
|
||||
* @cfg {Object|Ext.chart.axis.layout.Layout} layout
|
||||
* The axis layout config. See {@link Ext.chart.axis.layout.Layout}
|
||||
*/
|
||||
layout: 'continuous',
|
||||
|
||||
/**
|
||||
* @cfg {Object|Ext.chart.axis.segmenter.Segmenter} segmenter
|
||||
* The segmenter config. See {@link Ext.chart.axis.segmenter.Segmenter}
|
||||
*/
|
||||
segmenter: 'numeric',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} hidden
|
||||
* Indicate whether to hide the axis.
|
||||
* If the axis is hidden, one of the axis line, ticks, labels or the title will be shown and
|
||||
* no margin will be taken.
|
||||
* The coordination mechanism works fine no matter if the axis is hidden.
|
||||
*/
|
||||
hidden: false,
|
||||
|
||||
/**
|
||||
* @cfg {Number} majorTickSteps
|
||||
* If `minimum` and `maximum` are specified it forces the number of major ticks to the specified value.
|
||||
*/
|
||||
majorTickSteps: false,
|
||||
|
||||
/**
|
||||
* @cfg {Number} [minorTickSteps=0]
|
||||
* The number of small ticks between two major ticks.
|
||||
*/
|
||||
minorTickSteps: false,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Boolean} adjustMaximumByMajorUnit
|
||||
* Will be supported soon.
|
||||
*/
|
||||
adjustMaximumByMajorUnit: false,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Boolean} adjustMinimumByMajorUnit
|
||||
* Will be supported soon.
|
||||
*
|
||||
*/
|
||||
adjustMinimumByMajorUnit: false,
|
||||
|
||||
/**
|
||||
* @cfg {String|Object} title
|
||||
* The title for the Axis.
|
||||
* If given a String, the text style of the title sprite will be set,
|
||||
* otherwise the style will be set.
|
||||
*/
|
||||
title: { fontSize: 18, fontFamily: 'Helvetica'},
|
||||
|
||||
/**
|
||||
* @cfg {Number} increment
|
||||
* Given a minimum and maximum bound for the series to be rendered (that can be obtained
|
||||
* automatically or by manually setting `minimum` and `maximum`) tick marks will be added
|
||||
* on each `increment` from the minimum value to the maximum one.
|
||||
*/
|
||||
increment: 0.5,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} length
|
||||
* Length of the axis position. Equals to the size of inner region on the docking side of this axis.
|
||||
* WARNING: Meant to be set automatically by chart. Do not set it manually.
|
||||
*/
|
||||
length: 0,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Array} center
|
||||
* Center of the polar axis.
|
||||
* WARNING: Meant to be set automatically by chart. Do not set it manually.
|
||||
*/
|
||||
center: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} radius
|
||||
* Radius of the polar axis.
|
||||
* WARNING: Meant to be set automatically by chart. Do not set it manually.
|
||||
*/
|
||||
radius: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} rotation
|
||||
* Rotation of the polar axis.
|
||||
* WARNING: Meant to be set automatically by chart. Do not set it manually.
|
||||
*/
|
||||
rotation: null,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} [labelInSpan]
|
||||
* Draws the labels in the middle of the spans.
|
||||
*/
|
||||
labelInSpan: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array} visibleRange
|
||||
* Specify the proportion of the axis to be rendered. The series bound to
|
||||
* this axis will be synchronized and transformed.
|
||||
*/
|
||||
visibleRange: [0, 1],
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} needHighPrecision
|
||||
* Indicates that the axis needs high precision surface implementation.
|
||||
* See {@link Ext.draw.engine.Canvas#highPrecision}
|
||||
*/
|
||||
needHighPrecision: false
|
||||
},
|
||||
|
||||
observableType: 'component',
|
||||
|
||||
titleOffset: 0,
|
||||
|
||||
animating: 0,
|
||||
|
||||
prevMin: 0,
|
||||
|
||||
prevMax: 1,
|
||||
|
||||
boundSeries: [],
|
||||
|
||||
sprites: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @property {Array} The full data range of the axis. Should not be set directly, clear it to `null` and use
|
||||
* `getRange` to update.
|
||||
*/
|
||||
range: null,
|
||||
|
||||
xValues: [],
|
||||
|
||||
yValues: [],
|
||||
|
||||
applyRotation: function (rotation) {
|
||||
var twoPie = Math.PI * 2;
|
||||
return (rotation % twoPie + Math.PI) % twoPie - Math.PI;
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
var sprites = this.getSprites(),
|
||||
position = this.getPosition();
|
||||
if (!this.getHidden() && position === 'angular' && sprites[0]) {
|
||||
sprites[0].setAttributes({
|
||||
baseRotation: rotation
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
applyTitle: function (title, oldTitle) {
|
||||
var surface;
|
||||
|
||||
if (Ext.isString(title)) {
|
||||
title = { text: title };
|
||||
}
|
||||
|
||||
if (!oldTitle) {
|
||||
oldTitle = Ext.create('sprite.text', title);
|
||||
if ((surface = this.getSurface())) {
|
||||
surface.add(oldTitle);
|
||||
}
|
||||
} else {
|
||||
oldTitle.setAttributes(title);
|
||||
}
|
||||
return oldTitle;
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
var me = this;
|
||||
me.sprites = [];
|
||||
this.labels = [];
|
||||
this.initConfig(config);
|
||||
me.getId();
|
||||
me.mixins.observable.constructor.apply(me, arguments);
|
||||
Ext.ComponentManager.register(me);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
getAlignment: function () {
|
||||
switch (this.getPosition()) {
|
||||
case 'left':
|
||||
case 'right':
|
||||
return 'vertical';
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
return 'horizontal';
|
||||
case 'radial':
|
||||
return 'radial';
|
||||
case 'angular':
|
||||
return 'angular';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @return {String}
|
||||
*/
|
||||
getGridAlignment: function () {
|
||||
switch (this.getPosition()) {
|
||||
case 'left':
|
||||
case 'right':
|
||||
return 'horizontal';
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
return 'vertical';
|
||||
case 'radial':
|
||||
return 'circular';
|
||||
case 'angular':
|
||||
return "radial";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Get the surface for drawing the series sprites
|
||||
*/
|
||||
getSurface: function () {
|
||||
if (!this.surface) {
|
||||
var chart = this.getChart();
|
||||
if (!chart) {
|
||||
return null;
|
||||
}
|
||||
var surface = this.surface = chart.getSurface(this.getId(), 'axis'),
|
||||
gridSurface = this.gridSurface = chart.getSurface('main'),
|
||||
sprites = this.getSprites(),
|
||||
sprite = sprites[0],
|
||||
grid = this.getGrid(),
|
||||
gridAlignment = this.getGridAlignment(),
|
||||
gridSprite;
|
||||
if (grid) {
|
||||
gridSprite = this.gridSpriteEven = new Ext.chart.Markers();
|
||||
gridSprite.setTemplate({xclass: 'grid.' + gridAlignment});
|
||||
if (Ext.isObject(grid)) {
|
||||
gridSprite.getTemplate().setAttributes(grid);
|
||||
if (Ext.isObject(grid.even)) {
|
||||
gridSprite.getTemplate().setAttributes(grid.even);
|
||||
}
|
||||
}
|
||||
gridSurface.add(gridSprite);
|
||||
sprite.bindMarker(gridAlignment + '-even', gridSprite);
|
||||
|
||||
gridSprite = this.gridSpriteOdd = new Ext.chart.Markers();
|
||||
gridSprite.setTemplate({xclass: 'grid.' + gridAlignment});
|
||||
if (Ext.isObject(grid)) {
|
||||
gridSprite.getTemplate().setAttributes(grid);
|
||||
if (Ext.isObject(grid.odd)) {
|
||||
gridSprite.getTemplate().setAttributes(grid.odd);
|
||||
}
|
||||
}
|
||||
gridSurface.add(gridSprite);
|
||||
sprite.bindMarker(gridAlignment + '-odd', gridSprite);
|
||||
|
||||
gridSurface.waitFor(surface);
|
||||
}
|
||||
}
|
||||
return this.surface;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* Mapping data value into coordinate.
|
||||
*
|
||||
* @param {*} value
|
||||
* @param {String} field
|
||||
* @param {Number} [idx]
|
||||
* @param {Ext.util.MixedCollection} [items]
|
||||
* @return {Number}
|
||||
*/
|
||||
getCoordFor: function (value, field, idx, items) {
|
||||
return this.getLayout().getCoordFor(value, field, idx, items);
|
||||
},
|
||||
|
||||
applyPosition: function (pos) {
|
||||
return pos.toLowerCase();
|
||||
},
|
||||
|
||||
applyLabel: function (newText, oldText) {
|
||||
if (!oldText) {
|
||||
oldText = new Ext.draw.sprite.Text({});
|
||||
}
|
||||
oldText.setAttributes(newText);
|
||||
return oldText;
|
||||
},
|
||||
|
||||
applyLayout: function (layout, oldLayout) {
|
||||
// TODO: finish this
|
||||
layout = Ext.factory(layout, null, oldLayout, 'axisLayout');
|
||||
layout.setAxis(this);
|
||||
return layout;
|
||||
},
|
||||
|
||||
applySegmenter: function (segmenter, oldSegmenter) {
|
||||
// TODO: finish this
|
||||
segmenter = Ext.factory(segmenter, null, oldSegmenter, 'segmenter');
|
||||
segmenter.setAxis(this);
|
||||
return segmenter;
|
||||
},
|
||||
|
||||
updateMinimum: function () {
|
||||
this.range = null;
|
||||
},
|
||||
|
||||
updateMaximum: function () {
|
||||
this.range = null;
|
||||
},
|
||||
|
||||
hideLabels: function () {
|
||||
this.getSprites()[0].setDirty(true);
|
||||
this.setLabel({hidden: true});
|
||||
},
|
||||
|
||||
showLabels: function () {
|
||||
this.getSprites()[0].setDirty(true);
|
||||
this.setLabel({hidden: false});
|
||||
},
|
||||
|
||||
/**
|
||||
* Invokes renderFrame on this axis's surface(s)
|
||||
*/
|
||||
renderFrame: function () {
|
||||
this.getSurface().renderFrame();
|
||||
},
|
||||
|
||||
updateChart: function (newChart, oldChart) {
|
||||
var me = this, surface;
|
||||
if (oldChart) {
|
||||
oldChart.un('serieschanged', me.onSeriesChanged, me);
|
||||
}
|
||||
if (newChart) {
|
||||
newChart.on('serieschanged', me.onSeriesChanged, me);
|
||||
if (newChart.getSeries()) {
|
||||
me.onSeriesChanged(newChart);
|
||||
}
|
||||
me.surface = null;
|
||||
surface = me.getSurface();
|
||||
surface.add(me.getSprites());
|
||||
surface.add(me.getTitle());
|
||||
}
|
||||
},
|
||||
|
||||
applyBackground: function (background) {
|
||||
var rect = Ext.ClassManager.getByAlias('sprite.rect');
|
||||
return rect.def.normalize(background);
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Invoked when data has changed.
|
||||
*/
|
||||
processData: function () {
|
||||
this.getLayout().processData();
|
||||
this.range = null;
|
||||
},
|
||||
|
||||
getDirection: function () {
|
||||
return this.getChart().getDirectionForAxis(this.getPosition());
|
||||
},
|
||||
|
||||
isSide: function () {
|
||||
var position = this.getPosition();
|
||||
return position === 'left' || position === 'right';
|
||||
},
|
||||
|
||||
applyFields: function (fields) {
|
||||
return [].concat(fields);
|
||||
},
|
||||
|
||||
updateFields: function (fields) {
|
||||
this.fieldsMap = {};
|
||||
for (var i = 0; i < fields.length; i++) {
|
||||
this.fieldsMap[fields[i]] = true;
|
||||
}
|
||||
},
|
||||
|
||||
applyVisibleRange: function (visibleRange, oldVisibleRange) {
|
||||
// If it is in reversed order swap them
|
||||
if (visibleRange[0] > visibleRange[1]) {
|
||||
var temp = visibleRange[0];
|
||||
visibleRange[0] = visibleRange[1];
|
||||
visibleRange[0] = temp;
|
||||
}
|
||||
if (visibleRange[1] === visibleRange[0]) {
|
||||
visibleRange[1] += 1 / this.getMaxZoom();
|
||||
}
|
||||
if (visibleRange[1] > visibleRange[0] + 1) {
|
||||
visibleRange[0] = 0;
|
||||
visibleRange[1] = 1;
|
||||
} else if (visibleRange[0] < 0) {
|
||||
visibleRange[1] -= visibleRange[0];
|
||||
visibleRange[0] = 0;
|
||||
} else if (visibleRange[1] > 1) {
|
||||
visibleRange[0] -= visibleRange[1] - 1;
|
||||
visibleRange[1] = 1;
|
||||
}
|
||||
|
||||
if (oldVisibleRange && visibleRange[0] === oldVisibleRange[0] && visibleRange[1] === oldVisibleRange[1]) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return visibleRange;
|
||||
},
|
||||
|
||||
updateVisibleRange: function (visibleRange) {
|
||||
this.fireEvent('transformed', this, visibleRange);
|
||||
},
|
||||
|
||||
onSeriesChanged: function (chart) {
|
||||
var me = this,
|
||||
series = chart.getSeries(),
|
||||
getAxisMethod = 'get' + me.getDirection() + 'Axis',
|
||||
boundSeries = [], i, ln = series.length;
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (this === series[i][getAxisMethod]()) {
|
||||
boundSeries.push(series[i]);
|
||||
}
|
||||
}
|
||||
|
||||
me.boundSeries = boundSeries;
|
||||
me.getLayout().processData();
|
||||
},
|
||||
|
||||
applyRange: function (newRange) {
|
||||
if (!newRange) {
|
||||
return this.dataRange.slice(0);
|
||||
} else {
|
||||
return [
|
||||
newRange[0] === null ? this.dataRange[0] : newRange[0],
|
||||
newRange[1] === null ? this.dataRange[1] : newRange[1]
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the range derived from all the bound series.
|
||||
* @return {Array}
|
||||
*/
|
||||
getRange: function () {
|
||||
var me = this,
|
||||
getRangeMethod = 'get' + me.getDirection() + 'Range';
|
||||
|
||||
if (me.range) {
|
||||
return me.range;
|
||||
}
|
||||
if (!isNaN(me.getMinimum()) && !isNaN(me.getMaximum())) {
|
||||
return this.range = [me.getMinimum(), me.getMaximum()];
|
||||
}
|
||||
var min = Infinity,
|
||||
max = -Infinity,
|
||||
boundSeries = me.boundSeries,
|
||||
series, i, ln;
|
||||
|
||||
// For each series bound to this axis, ask the series for its min/max values
|
||||
// and use them to find the overall min/max.
|
||||
for (i = 0, ln = boundSeries.length; i < ln; i++) {
|
||||
series = boundSeries[i];
|
||||
var minMax = series[getRangeMethod]();
|
||||
|
||||
if (minMax) {
|
||||
if (minMax[0] < min) {
|
||||
min = minMax[0];
|
||||
}
|
||||
if (minMax[1] > max) {
|
||||
max = minMax[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isFinite(max)) {
|
||||
max = me.prevMax;
|
||||
}
|
||||
|
||||
if (!isFinite(min)) {
|
||||
min = me.prevMin;
|
||||
}
|
||||
|
||||
if (this.getLabelInSpan() || min === max) {
|
||||
max += this.getIncrement();
|
||||
min -= this.getIncrement();
|
||||
}
|
||||
|
||||
if (!isNaN(me.getMinimum())) {
|
||||
min = me.getMinimum();
|
||||
} else {
|
||||
me.prevMin = min;
|
||||
}
|
||||
|
||||
if (!isNaN(me.getMaximum())) {
|
||||
max = me.getMaximum();
|
||||
} else {
|
||||
me.prevMax = max;
|
||||
}
|
||||
|
||||
return this.range = [min, max];
|
||||
},
|
||||
|
||||
applyStyle: function (style, oldStyle) {
|
||||
var cls = Ext.ClassManager.getByAlias('sprite.' + this.seriesType);
|
||||
if (cls && cls.def) {
|
||||
style = cls.def.normalize(style);
|
||||
}
|
||||
oldStyle = Ext.apply(oldStyle || {}, style);
|
||||
return oldStyle;
|
||||
},
|
||||
|
||||
updateCenter: function (center) {
|
||||
var sprites = this.getSprites(),
|
||||
axisSprite = sprites[0],
|
||||
centerX = center[0],
|
||||
centerY = center[1];
|
||||
if (axisSprite) {
|
||||
axisSprite.setAttributes({
|
||||
centerX: centerX,
|
||||
centerY: centerY
|
||||
});
|
||||
}
|
||||
if (this.gridSpriteEven) {
|
||||
this.gridSpriteEven.getTemplate().setAttributes({
|
||||
translationX: centerX,
|
||||
translationY: centerY,
|
||||
rotationCenterX: centerX,
|
||||
rotationCenterY: centerY
|
||||
});
|
||||
}
|
||||
if (this.gridSpriteOdd) {
|
||||
this.gridSpriteOdd.getTemplate().setAttributes({
|
||||
translationX: centerX,
|
||||
translationY: centerY,
|
||||
rotationCenterX: centerX,
|
||||
rotationCenterY: centerY
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
if (!this.getChart()) {
|
||||
return;
|
||||
}
|
||||
var me = this,
|
||||
range = me.getRange(),
|
||||
position = me.getPosition(),
|
||||
chart = me.getChart(),
|
||||
animation = chart.getAnimate(),
|
||||
baseSprite, style,
|
||||
length = me.getLength();
|
||||
|
||||
// If animation is false, then stop animation.
|
||||
if (animation === false) {
|
||||
animation = {
|
||||
duration: 0
|
||||
};
|
||||
}
|
||||
if (range) {
|
||||
style = Ext.applyIf({
|
||||
position: position,
|
||||
axis: me,
|
||||
min: range[0],
|
||||
max: range[1],
|
||||
length: length,
|
||||
grid: me.getGrid(),
|
||||
hidden: me.getHidden(),
|
||||
titleOffset: me.titleOffset,
|
||||
layout: me.getLayout(),
|
||||
segmenter: me.getSegmenter(),
|
||||
label: me.getLabel()
|
||||
}, me.getStyle());
|
||||
|
||||
// If the sprites are not created.
|
||||
if (!me.sprites.length) {
|
||||
baseSprite = new Ext.chart.axis.sprite.Axis(style);
|
||||
baseSprite.fx.setCustomDuration({
|
||||
baseRotation: 0
|
||||
});
|
||||
baseSprite.fx.on("animationstart", "onAnimationStart", me);
|
||||
baseSprite.fx.on("animationend", "onAnimationEnd", me);
|
||||
me.sprites.push(baseSprite);
|
||||
me.updateTitleSprite();
|
||||
} else {
|
||||
baseSprite = me.sprites[0];
|
||||
baseSprite.fx.setConfig(animation);
|
||||
baseSprite.setAttributes(style);
|
||||
baseSprite.setLayout(me.getLayout());
|
||||
baseSprite.setSegmenter(me.getSegmenter());
|
||||
baseSprite.setLabel(me.getLabel());
|
||||
}
|
||||
|
||||
if (me.getRenderer()) {
|
||||
baseSprite.setRenderer(me.getRenderer());
|
||||
}
|
||||
}
|
||||
|
||||
return me.sprites;
|
||||
},
|
||||
|
||||
updateTitleSprite: function () {
|
||||
if (!this.sprites[0]) {
|
||||
return;
|
||||
}
|
||||
var me = this,
|
||||
thickness = this.sprites[0].thickness,
|
||||
surface = me.getSurface(),
|
||||
title = this.getTitle(),
|
||||
position = me.getPosition(),
|
||||
titleMargin = me.getTitleMargin(),
|
||||
length = me.getLength(),
|
||||
anchor = surface.roundPixel(length / 2);
|
||||
|
||||
if (title) {
|
||||
switch (position) {
|
||||
case 'top':
|
||||
title.setAttributes({
|
||||
x: anchor,
|
||||
y: titleMargin / 2,
|
||||
textBaseline: 'top',
|
||||
textAlign: 'center'
|
||||
}, true, true);
|
||||
title.applyTransformations();
|
||||
me.titleOffset = title.getBBox().height + titleMargin;
|
||||
break;
|
||||
case 'bottom':
|
||||
title.setAttributes({
|
||||
x: anchor,
|
||||
y: thickness + titleMargin / 2,
|
||||
textBaseline: 'top',
|
||||
textAlign: 'center'
|
||||
}, true, true);
|
||||
title.applyTransformations();
|
||||
me.titleOffset = title.getBBox().height + titleMargin;
|
||||
break;
|
||||
case 'left':
|
||||
title.setAttributes({
|
||||
x: titleMargin / 2,
|
||||
y: anchor,
|
||||
textBaseline: 'top',
|
||||
textAlign: 'center',
|
||||
rotationCenterX: titleMargin / 2,
|
||||
rotationCenterY: anchor,
|
||||
rotationRads: -Math.PI / 2
|
||||
}, true, true);
|
||||
title.applyTransformations();
|
||||
me.titleOffset = title.getBBox().width + titleMargin;
|
||||
break;
|
||||
case 'right':
|
||||
title.setAttributes({
|
||||
x: thickness + titleMargin / 2,
|
||||
y: anchor,
|
||||
textBaseline: 'bottom',
|
||||
textAlign: 'center',
|
||||
rotationCenterX: thickness + titleMargin / 2,
|
||||
rotationCenterY: anchor,
|
||||
rotationRads: Math.PI / 2
|
||||
}, true, true);
|
||||
title.applyTransformations();
|
||||
me.titleOffset = title.getBBox().width + titleMargin;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onThicknessChanged: function () {
|
||||
var me = this;
|
||||
me.getChart().onThicknessChanged();
|
||||
},
|
||||
|
||||
getThickness: function () {
|
||||
if (this.getHidden()) {
|
||||
return 0;
|
||||
}
|
||||
return (this.sprites[0] && this.sprites[0].thickness || 1) + this.titleOffset;
|
||||
},
|
||||
|
||||
onAnimationStart: function () {
|
||||
this.animating++;
|
||||
if (this.animating === 1) {
|
||||
this.fireEvent("animationstart");
|
||||
}
|
||||
},
|
||||
|
||||
onAnimationEnd: function () {
|
||||
this.animating--;
|
||||
if (this.animating === 0) {
|
||||
this.fireEvent("animationend");
|
||||
}
|
||||
},
|
||||
|
||||
// Methods used in ComponentQuery and controller
|
||||
getItemId: function () {
|
||||
return this.getId();
|
||||
},
|
||||
|
||||
getAncestorIds: function () {
|
||||
return [this.getChart().getId()];
|
||||
},
|
||||
|
||||
isXType: function (xtype) {
|
||||
return xtype === 'axis';
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
Ext.ComponentManager.unregister(this);
|
||||
this.callSuper();
|
||||
}
|
||||
});
|
||||
|
69
vendor/touch/src/chart/axis/Category.js
vendored
Normal file
69
vendor/touch/src/chart/axis/Category.js
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.Category
|
||||
* @extends Ext.chart.axis.Axis
|
||||
*
|
||||
* A type of axis that displays items in categories. This axis is generally used to
|
||||
* display categorical information like names of items, month names, quarters, etc.
|
||||
* but no quantitative values. For that other type of information {@link Ext.chart.axis.Numeric Numeric}
|
||||
* axis are more suitable.
|
||||
*
|
||||
* As with other axis you can set the position of the axis and its title. For example:
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* innerPadding: {
|
||||
* left: 40,
|
||||
* right: 40
|
||||
* },
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'area',
|
||||
* subStyle: {
|
||||
* fill: ['blue', 'green', 'red']
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: ['data1', 'data2', 'data3']
|
||||
*
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*
|
||||
* In this example with set the category axis to the bottom of the surface, bound the axis to
|
||||
* the `name` property and set as title "Sample Values".
|
||||
*/
|
||||
|
||||
Ext.define('Ext.chart.axis.Category', {
|
||||
requires: [
|
||||
'Ext.chart.axis.layout.CombineDuplicate',
|
||||
'Ext.chart.axis.segmenter.Names'
|
||||
],
|
||||
extend: 'Ext.chart.axis.Axis',
|
||||
alias: 'axis.category',
|
||||
type: 'category',
|
||||
|
||||
config: {
|
||||
layout: 'combineDuplicate',
|
||||
|
||||
segmenter: 'names'
|
||||
}
|
||||
});
|
72
vendor/touch/src/chart/axis/Numeric.js
vendored
Normal file
72
vendor/touch/src/chart/axis/Numeric.js
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.Numeric
|
||||
* @extends Ext.chart.axis.Axis
|
||||
*
|
||||
* An axis to handle numeric values. This axis is used for quantitative data as
|
||||
* opposed to the category axis. You can set minimum and maximum values to the
|
||||
* axis so that the values are bound to that. If no values are set, then the
|
||||
* scale will auto-adjust to the values.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':1, 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':2, 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':3, 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':4, 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':5, 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* title: 'Sample Values',
|
||||
* grid: {
|
||||
* odd: {
|
||||
* opacity: 1,
|
||||
* fill: '#ddd',
|
||||
* stroke: '#bbb',
|
||||
* 'lineWidth': 1
|
||||
* }
|
||||
* },
|
||||
* minimum: 0,
|
||||
* adjustMinimumByMajorUnit: 0
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'area',
|
||||
* subStyle: {
|
||||
* fill: ['blue', 'green', 'red']
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: ['data1', 'data2', 'data3']
|
||||
*
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
* In this example we create an axis of Numeric type. We set a minimum value so that
|
||||
* even if all series have values greater than zero, the grid starts at zero. We bind
|
||||
* the axis onto the left part of the surface by setting _position_ to _left_.
|
||||
* We bind three different store fields to this axis by setting _fields_ to an array.
|
||||
* We set the title of the axis to _Number of Hits_ by using the _title_ property.
|
||||
* We use a _grid_ configuration to set odd background rows to a certain style and even rows
|
||||
* to be transparent/ignored.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.Numeric', {
|
||||
extend: 'Ext.chart.axis.Axis',
|
||||
alias: 'axis.numeric',
|
||||
type: 'numeric',
|
||||
requires: ['Ext.chart.axis.layout.Continuous', 'Ext.chart.axis.segmenter.Numeric'],
|
||||
config: {
|
||||
layout: 'continuous',
|
||||
|
||||
segmenter: 'numeric',
|
||||
|
||||
aggregator: 'double'
|
||||
}
|
||||
});
|
140
vendor/touch/src/chart/axis/Time.js
vendored
Normal file
140
vendor/touch/src/chart/axis/Time.js
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.Time
|
||||
* @extends Ext.chart.axis.Numeric
|
||||
*
|
||||
* A type of axis whose units are measured in time values. Use this axis
|
||||
* for listing dates that you will want to group or dynamically change.
|
||||
* If you just want to display dates as categories then use the
|
||||
* Category class for axis instead.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['time', 'open', 'high', 'low', 'close'],
|
||||
* data: [
|
||||
* {'time':new Date('Jan 1 2010').getTime(), 'open':600, 'high':614, 'low':578, 'close':590},
|
||||
* {'time':new Date('Jan 2 2010').getTime(), 'open':590, 'high':609, 'low':580, 'close':580},
|
||||
* {'time':new Date('Jan 3 2010').getTime(), 'open':580, 'high':602, 'low':578, 'close':602},
|
||||
* {'time':new Date('Jan 4 2010').getTime(), 'open':602, 'high':614, 'low':586, 'close':586},
|
||||
* {'time':new Date('Jan 5 2010').getTime(), 'open':586, 'high':602, 'low':565, 'close':565}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['open', 'high', 'low', 'close'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 560,
|
||||
* maximum: 640
|
||||
* }, {
|
||||
* type: 'time',
|
||||
* position: 'bottom',
|
||||
* fields: ['time'],
|
||||
* fromDate: new Date('Dec 31 2009'),
|
||||
* toDate: new Date('Jan 6 2010'),
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* style: {
|
||||
* axisLine: false
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'candlestick',
|
||||
* xField: 'time',
|
||||
* openField: 'open',
|
||||
* highField: 'high',
|
||||
* lowField: 'low',
|
||||
* closeField: 'close',
|
||||
* style: {
|
||||
* ohlcType: 'ohlc',
|
||||
* dropStyle: {
|
||||
* fill: 'rgb(237, 123, 43)',
|
||||
* stroke: 'rgb(237, 123, 43)'
|
||||
* },
|
||||
* raiseStyle: {
|
||||
* fill: 'rgb(55, 153, 19)',
|
||||
* stroke: 'rgb(55, 153, 19)'
|
||||
* }
|
||||
* },
|
||||
* aggregator: {
|
||||
* strategy: 'time'
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.Time', {
|
||||
extend: 'Ext.chart.axis.Numeric',
|
||||
alias: 'axis.time',
|
||||
type: 'time',
|
||||
requires: ['Ext.chart.axis.layout.Continuous', 'Ext.chart.axis.segmenter.Time', 'Ext.DateExtras'],
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Boolean} calculateByLabelSize
|
||||
* The minimum value drawn by the axis. If not set explicitly, the axis
|
||||
* minimum will be calculated automatically.
|
||||
*/
|
||||
calculateByLabelSize: true,
|
||||
|
||||
/**
|
||||
* @cfg {String/Boolean} dateFormat
|
||||
* Indicates the format the date will be rendered on.
|
||||
* For example: 'M d' will render the dates as 'Jan 30', etc.
|
||||
*/
|
||||
dateFormat: null,
|
||||
|
||||
/**
|
||||
* @cfg {Date} fromDate The starting date for the time axis.
|
||||
*/
|
||||
fromDate: null,
|
||||
|
||||
/**
|
||||
* @cfg {Date} toDate The ending date for the time axis.
|
||||
*/
|
||||
toDate: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array} [step=[Ext.Date.DAY, 1]] An array with two components:
|
||||
*
|
||||
* - The unit of the step (Ext.Date.DAY, Ext.Date.MONTH, etc).
|
||||
* - The number of units for the step (1, 2, etc).
|
||||
*
|
||||
*/
|
||||
step: [Ext.Date.DAY, 1],
|
||||
|
||||
layout: 'continuous',
|
||||
|
||||
segmenter: 'time',
|
||||
|
||||
aggregator: 'time'
|
||||
},
|
||||
|
||||
updateDateFormat: function (format) {
|
||||
this.setRenderer(function (date) {
|
||||
return Ext.Date.format(new Date(date), format);
|
||||
});
|
||||
},
|
||||
|
||||
updateFromDate: function (date) {
|
||||
this.setMinimum(+date);
|
||||
},
|
||||
|
||||
updateToDate: function (date) {
|
||||
this.setMaximum(+date);
|
||||
},
|
||||
|
||||
getCoordFor: function (value) {
|
||||
if (Ext.isString(value)) {
|
||||
value = new Date(value);
|
||||
}
|
||||
return +value;
|
||||
}
|
||||
});
|
20
vendor/touch/src/chart/axis/layout/CombineDuplicate.js
vendored
Normal file
20
vendor/touch/src/chart/axis/layout/CombineDuplicate.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.layout.CombineDuplicate
|
||||
* @extends Ext.chart.axis.layout.Discrete
|
||||
*
|
||||
* Discrete processor that combines duplicate data points.
|
||||
*/
|
||||
Ext.define("Ext.chart.axis.layout.CombineDuplicate", {
|
||||
extend: 'Ext.chart.axis.layout.Discrete',
|
||||
alias: 'axisLayout.combineDuplicate',
|
||||
|
||||
getCoordFor: function (value, field, idx, items) {
|
||||
if (!(value in this.labelMap)) {
|
||||
var result = this.labelMap[value] = this.labels.length;
|
||||
this.labels.push(value);
|
||||
return result;
|
||||
}
|
||||
return this.labelMap[value];
|
||||
}
|
||||
|
||||
});
|
76
vendor/touch/src/chart/axis/layout/Continuous.js
vendored
Normal file
76
vendor/touch/src/chart/axis/layout/Continuous.js
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.layout.Continuous
|
||||
* @extends Ext.chart.axis.layout.Layout
|
||||
*
|
||||
* Processor for axis data that can be interpolated.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.layout.Continuous', {
|
||||
extend: 'Ext.chart.axis.layout.Layout',
|
||||
alias: 'axisLayout.continuous',
|
||||
config: {
|
||||
adjustMinimumByMajorUnit: false,
|
||||
adjustMaximumByMajorUnit: false
|
||||
},
|
||||
|
||||
getCoordFor: function (value, field, idx, items) {
|
||||
return +value;
|
||||
},
|
||||
|
||||
//@inheritdoc
|
||||
snapEnds: function (context, min, max, estStepSize) {
|
||||
var segmenter = context.segmenter,
|
||||
axis = this.getAxis(),
|
||||
minimum = axis.getMinimum(),
|
||||
maximum = axis.getMaximum(),
|
||||
majorTickSteps = axis.getMajorTickSteps(),
|
||||
out = majorTickSteps && Ext.isNumber(minimum) && Ext.isNumber(maximum) && segmenter.exactStep ?
|
||||
segmenter.exactStep(min, (max - min) / majorTickSteps) :
|
||||
segmenter.preferredStep(min, estStepSize),
|
||||
unit = out.unit,
|
||||
step = out.step,
|
||||
from = segmenter.align(min, step, unit),
|
||||
steps = segmenter.diff(min, max, unit) + 1;
|
||||
return {
|
||||
min: segmenter.from(min),
|
||||
max: segmenter.from(max),
|
||||
from: from,
|
||||
to: segmenter.add(from, steps * step, unit),
|
||||
step: step,
|
||||
steps: steps,
|
||||
unit: unit,
|
||||
get: function (current) {
|
||||
return segmenter.add(this.from, this.step * current, unit);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
snapMinorEnds: function (context) {
|
||||
var majorTicks = context.majorTicks,
|
||||
minorTickSteps = this.getAxis().getMinorTickSteps(),
|
||||
segmenter = context.segmenter,
|
||||
min = majorTicks.min,
|
||||
max = majorTicks.max,
|
||||
from = majorTicks.from,
|
||||
unit = majorTicks.unit,
|
||||
step = majorTicks.step / minorTickSteps,
|
||||
scaledStep = step * unit.scale,
|
||||
fromMargin = from - min,
|
||||
offset = Math.floor(fromMargin / scaledStep),
|
||||
extraSteps = offset + Math.floor((max - majorTicks.to) / scaledStep) + 1,
|
||||
steps = majorTicks.steps * minorTickSteps + extraSteps;
|
||||
return {
|
||||
min: min,
|
||||
max: max,
|
||||
from: min + fromMargin % scaledStep,
|
||||
to: segmenter.add(from, steps * step, unit),
|
||||
step: step,
|
||||
steps: steps,
|
||||
unit: unit,
|
||||
get: function (current) {
|
||||
return (current % minorTickSteps + offset + 1 !== 0) ? // don't render minor tick in major tick position
|
||||
segmenter.add(this.from, this.step * current, unit) :
|
||||
null;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
127
vendor/touch/src/chart/axis/layout/Discrete.js
vendored
Normal file
127
vendor/touch/src/chart/axis/layout/Discrete.js
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.layout.Discrete
|
||||
* @extends Ext.chart.axis.layout.Layout
|
||||
*
|
||||
* Simple processor for data that cannot be interpolated.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.layout.Discrete', {
|
||||
extend: 'Ext.chart.axis.layout.Layout',
|
||||
alias: 'axisLayout.discrete',
|
||||
|
||||
processData: function () {
|
||||
var me = this,
|
||||
axis = me.getAxis(),
|
||||
boundSeries = axis.boundSeries,
|
||||
direction = axis.getDirection(),
|
||||
i, ln, item;
|
||||
this.labels = [];
|
||||
this.labelMap = {};
|
||||
for (i = 0, ln = boundSeries.length; i < ln; i++) {
|
||||
item = boundSeries[i];
|
||||
if (item['get' + direction + 'Axis']() === axis) {
|
||||
item['coordinate' + direction]();
|
||||
}
|
||||
}
|
||||
// About the labels on Category axes (aka. axes with a Discrete layout)...
|
||||
//
|
||||
// When the data set from the store changes, series.processData() is called, which does its thing
|
||||
// at the series level and then calls series.updateLabelData() to update the labels in the sprites
|
||||
// that belong to the series. At the same time, series.processData() calls axis.processData(), which
|
||||
// also does its thing but at the axis level, and also needs to update the labels for the sprite(s)
|
||||
// that belong to the axis. This is not that simple, however. So how are the axis labels rendered?
|
||||
// First, axis.sprite.Axis.render() calls renderLabels() which obtains the majorTicks from the
|
||||
// axis.layout and iterate() through them. The majorTicks are an object returned by snapEnds() below
|
||||
// which provides a getLabel() function that returns the label from the axis.layoutContext.data array.
|
||||
// So now the question is: how are the labels transferred from the axis.layout to the axis.layoutContext?
|
||||
// The easy response is: it's in calculateLayout() below. The issue is to call calculateLayout() because
|
||||
// it takes in an axis.layoutContext that can only be created in axis.sprite.Axis.doLayout(), which is
|
||||
// a private "updater" function that is called by all the sprite's "dirtyTriggers". Of course, we don't
|
||||
// want to call doLayout() directly from here, so instead we update the sprite's data attribute, which
|
||||
// sets the dirtyTrigger which calls doLayout() which calls calculateLayout() etc...
|
||||
// Note that the sprite's data attribute could be set to any value and it would still result in the
|
||||
// dirtyTrigger we need. For consistency, however, it is set to the labels.
|
||||
axis.getSprites()[0].setAttributes({data:this.labels});
|
||||
},
|
||||
|
||||
// @inheritdoc
|
||||
calculateLayout: function (context) {
|
||||
context.data = this.labels;
|
||||
this.callSuper([context]);
|
||||
},
|
||||
|
||||
//@inheritdoc
|
||||
calculateMajorTicks: function (context) {
|
||||
var me = this,
|
||||
attr = context.attr,
|
||||
data = context.data,
|
||||
range = attr.max - attr.min,
|
||||
zoom = range / Math.max(1, attr.length) * (attr.visibleMax - attr.visibleMin),
|
||||
viewMin = attr.min + range * attr.visibleMin,
|
||||
viewMax = attr.min + range * attr.visibleMax,
|
||||
estStepSize = attr.estStepSize * zoom;
|
||||
|
||||
var out = me.snapEnds(context, Math.max(0, attr.min), Math.min(attr.max, data.length - 1), estStepSize);
|
||||
if (out) {
|
||||
me.trimByRange(context, out, viewMin, viewMax);
|
||||
context.majorTicks = out;
|
||||
}
|
||||
},
|
||||
|
||||
// @inheritdoc
|
||||
snapEnds: function (context, min, max, estStepSize) {
|
||||
estStepSize = Math.ceil(estStepSize);
|
||||
var steps = Math.floor((max - min) / estStepSize),
|
||||
data = context.data;
|
||||
return {
|
||||
min: min,
|
||||
max: max,
|
||||
from: min,
|
||||
to: steps * estStepSize + min,
|
||||
step: estStepSize,
|
||||
steps: steps,
|
||||
unit: 1,
|
||||
getLabel: function (current) {
|
||||
return data[this.from + this.step * current];
|
||||
},
|
||||
get: function (current) {
|
||||
return this.from + this.step * current;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
// @inheritdoc
|
||||
trimByRange: function (context, out, trimMin, trimMax) {
|
||||
var unit = out.unit,
|
||||
beginIdx = Math.ceil((trimMin - out.from) / unit) * unit,
|
||||
endIdx = Math.floor((trimMax - out.from) / unit) * unit,
|
||||
begin = Math.max(0, Math.ceil(beginIdx / out.step)),
|
||||
end = Math.min(out.steps, Math.floor(endIdx / out.step));
|
||||
|
||||
if (end < out.steps) {
|
||||
out.to = end;
|
||||
}
|
||||
|
||||
if (out.max > trimMax) {
|
||||
out.max = out.to;
|
||||
}
|
||||
|
||||
if (out.from < trimMin && out.step > 0) {
|
||||
out.from = out.from + begin * out.step * unit;
|
||||
while (out.from < trimMin) {
|
||||
begin++;
|
||||
out.from += out.step * unit;
|
||||
}
|
||||
}
|
||||
|
||||
if (out.min < trimMin) {
|
||||
out.min = out.from;
|
||||
}
|
||||
|
||||
out.steps = end - begin;
|
||||
},
|
||||
|
||||
getCoordFor: function (value, field, idx, items) {
|
||||
this.labels.push(value);
|
||||
return this.labels.length - 1;
|
||||
}
|
||||
});
|
139
vendor/touch/src/chart/axis/layout/Layout.js
vendored
Normal file
139
vendor/touch/src/chart/axis/layout/Layout.js
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* @abstract
|
||||
* @class Ext.chart.axis.layout.Layout
|
||||
*
|
||||
* Interface used by Axis to process its data into a meaningful layout.
|
||||
*/
|
||||
Ext.define("Ext.chart.axis.layout.Layout", {
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Ext.chart.axis.Axis} axis The axis that the Layout is bound.
|
||||
*/
|
||||
axis: null
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
this.initConfig();
|
||||
},
|
||||
|
||||
/**
|
||||
* Processes the data of the series bound to the axis.
|
||||
* @param {Ext.chart.series.Series} series The bound series.
|
||||
*/
|
||||
processData: function (series) {
|
||||
var me = this,
|
||||
axis = me.getAxis(),
|
||||
direction = axis.getDirection(),
|
||||
boundSeries = axis.boundSeries,
|
||||
i, ln, item;
|
||||
if (series) {
|
||||
series['coordinate' + direction]();
|
||||
} else {
|
||||
for (i = 0, ln = boundSeries.length; i < ln; i++) {
|
||||
item = boundSeries[i];
|
||||
if (item['get' + direction + 'Axis']() === axis) {
|
||||
item['coordinate' + direction]();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the position of major ticks for the axis.
|
||||
* @param {Object} context
|
||||
*/
|
||||
calculateMajorTicks: function (context) {
|
||||
var me = this,
|
||||
attr = context.attr,
|
||||
range = attr.max - attr.min,
|
||||
zoom = range / Math.max(1, attr.length) * (attr.visibleMax - attr.visibleMin),
|
||||
viewMin = attr.min + range * attr.visibleMin,
|
||||
viewMax = attr.min + range * attr.visibleMax,
|
||||
estStepSize = attr.estStepSize * zoom,
|
||||
out = me.snapEnds(context, attr.min, attr.max, estStepSize);
|
||||
if (out) {
|
||||
me.trimByRange(context, out, viewMin, viewMax);
|
||||
context.majorTicks = out;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the position of sub ticks for the axis.
|
||||
* @param {Object} context
|
||||
*/
|
||||
calculateMinorTicks: function (context) {
|
||||
var attr = context.attr;
|
||||
if (this.snapMinorEnds) {
|
||||
context.minorTicks = this.snapMinorEnds(context);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the position of tick marks for the axis.
|
||||
* @param {Object} context
|
||||
* @return {*}
|
||||
*/
|
||||
calculateLayout: function (context) {
|
||||
var me = this,
|
||||
attr = context.attr,
|
||||
majorTicks = attr.majorTicks,
|
||||
minorTicks = attr.minorTicks;
|
||||
if (attr.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (majorTicks) {
|
||||
this.calculateMajorTicks(context);
|
||||
if (minorTicks) {
|
||||
this.calculateMinorTicks(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Snaps the data bound to the axis to meaningful tick marks.
|
||||
* @param {Object} context
|
||||
* @param {Number} min
|
||||
* @param {Number} max
|
||||
* @param {Number} estStepSize
|
||||
*/
|
||||
snapEnds: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Trims the layout of the axis by the defined minimum and maximum.
|
||||
* @param {Object} context
|
||||
* @param {Object} out
|
||||
* @param {Number} trimMin
|
||||
* @param {Number} trimMax
|
||||
*/
|
||||
trimByRange: function (context, out, trimMin, trimMax) {
|
||||
var segmenter = context.segmenter,
|
||||
unit = out.unit,
|
||||
beginIdx = segmenter.diff(out.from, trimMin, unit),
|
||||
endIdx = segmenter.diff(out.from, trimMax, unit),
|
||||
begin = Math.max(0, Math.ceil(beginIdx / out.step)),
|
||||
end = Math.min(out.steps, Math.floor(endIdx / out.step));
|
||||
|
||||
if (end < out.steps) {
|
||||
out.to = segmenter.add(out.from, end * out.step, unit);
|
||||
}
|
||||
|
||||
if (out.max > trimMax) {
|
||||
out.max = out.to;
|
||||
}
|
||||
|
||||
if (out.from < trimMin) {
|
||||
out.from = segmenter.add(out.from, begin * out.step, unit);
|
||||
while (out.from < trimMin) {
|
||||
begin++;
|
||||
out.from = segmenter.add(out.from, out.step, unit);
|
||||
}
|
||||
}
|
||||
|
||||
if (out.min < trimMin) {
|
||||
out.min = out.from;
|
||||
}
|
||||
|
||||
out.steps = end - begin;
|
||||
}
|
||||
});
|
36
vendor/touch/src/chart/axis/segmenter/Names.js
vendored
Normal file
36
vendor/touch/src/chart/axis/segmenter/Names.js
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.segmenter.Names
|
||||
* @extends Ext.chart.axis.segmenter.Segmenter
|
||||
*
|
||||
* Names data type. Names will be calculated as their indices in the methods in this class.
|
||||
* The `preferredStep` always return `{ unit: 1, step: 1 }` to indicate "show every item".
|
||||
*
|
||||
*/
|
||||
Ext.define("Ext.chart.axis.segmenter.Names", {
|
||||
extend: 'Ext.chart.axis.segmenter.Segmenter',
|
||||
alias: 'segmenter.names',
|
||||
|
||||
renderer: function (value, context) {
|
||||
return value;
|
||||
},
|
||||
|
||||
diff: function (min, max, unit) {
|
||||
return Math.floor(max - min);
|
||||
},
|
||||
|
||||
align: function (value, step, unit) {
|
||||
return Math.floor(value);
|
||||
},
|
||||
|
||||
|
||||
add: function (value, step, unit) {
|
||||
return value + step;
|
||||
},
|
||||
|
||||
preferredStep: function (min, estStepSize, minIdx, data) {
|
||||
return {
|
||||
unit: 1,
|
||||
step: 1
|
||||
};
|
||||
}
|
||||
});
|
72
vendor/touch/src/chart/axis/segmenter/Numeric.js
vendored
Normal file
72
vendor/touch/src/chart/axis/segmenter/Numeric.js
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.segmenter.Numeric
|
||||
* @extends Ext.chart.axis.segmenter.Segmenter
|
||||
*
|
||||
* Numeric data type.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.segmenter.Numeric', {
|
||||
extend: 'Ext.chart.axis.segmenter.Segmenter',
|
||||
alias: 'segmenter.numeric',
|
||||
|
||||
renderer: function (value, context) {
|
||||
return value.toFixed(Math.max(0, context.majorTicks.unit.fixes));
|
||||
},
|
||||
|
||||
diff: function (min, max, unit) {
|
||||
return Math.floor((max - min) / unit.scale);
|
||||
},
|
||||
|
||||
align: function (value, step, unit) {
|
||||
return Math.floor(value / (unit.scale * step)) * unit.scale * step;
|
||||
},
|
||||
|
||||
|
||||
add: function (value, step, unit) {
|
||||
return value + step * unit.scale;
|
||||
},
|
||||
|
||||
preferredStep: function (min, estStepSize) {
|
||||
var logs = Math.floor(Math.log(estStepSize) * Math.LOG10E), // common logarithm of estStepSize
|
||||
scale = Math.pow(10, logs);
|
||||
estStepSize /= scale;
|
||||
if (estStepSize < 2) {
|
||||
estStepSize = 2;
|
||||
} else if (estStepSize < 5) {
|
||||
estStepSize = 5;
|
||||
} else if (estStepSize < 10) {
|
||||
estStepSize = 10;
|
||||
logs++;
|
||||
}
|
||||
return {
|
||||
unit: {
|
||||
// when estStepSize < 1, rounded down log10(estStepSize) is equal to -number_of_leading_zeros in estStepSize
|
||||
fixes: -logs, // number of fractional digits
|
||||
scale: scale
|
||||
},
|
||||
step: estStepSize
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Wraps the provided estimated step size of a range without altering it into a step size object.
|
||||
*
|
||||
* @param {*} min The start point of range.
|
||||
* @param {*} estStepSize The estimated step size.
|
||||
* @return {Object} Return the step size by an object of step x unit.
|
||||
* @return {Number} return.step The step count of units.
|
||||
* @return {Object} return.unit The unit.
|
||||
*/
|
||||
|
||||
exactStep: function (min, estStepSize) {
|
||||
var logs = Math.floor(Math.log(estStepSize) * Math.LOG10E),
|
||||
scale = Math.pow(10, logs);
|
||||
return {
|
||||
unit: {
|
||||
// add one decimal point if estStepSize is not a multiple of scale
|
||||
fixes: -logs + (estStepSize % scale === 0 ? 0 : 1),
|
||||
scale: 1
|
||||
},
|
||||
step: estStepSize
|
||||
}
|
||||
}
|
||||
});
|
84
vendor/touch/src/chart/axis/segmenter/Segmenter.js
vendored
Normal file
84
vendor/touch/src/chart/axis/segmenter/Segmenter.js
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* @abstract
|
||||
* @class Ext.chart.axis.segmenter.Segmenter
|
||||
*
|
||||
* Interface for a segmenter in an Axis. A segmenter defines the operations you can do to a specific
|
||||
* data type.
|
||||
*
|
||||
* See {@link Ext.chart.axis.Axis}.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.segmenter.Segmenter', {
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Ext.chart.axis.Axis} axis The axis that the Segmenter is bound.
|
||||
*/
|
||||
axis: null
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
this.initConfig(config);
|
||||
},
|
||||
|
||||
/**
|
||||
* This method formats the value.
|
||||
*
|
||||
* @param {*} value The value to format.
|
||||
* @param {Object} context Axis layout context.
|
||||
* @return {String}
|
||||
*/
|
||||
renderer: function (value, context) {
|
||||
return String(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert from any data into the target type.
|
||||
* @param {*} value The value to convert from
|
||||
* @return {*} The converted value.
|
||||
*/
|
||||
from: function (value) {
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the difference between the min and max value based on the given unit scale.
|
||||
*
|
||||
* @param {*} min The smaller value.
|
||||
* @param {*} max The larger value.
|
||||
* @param {*} unit The unit scale. Unit can be any type.
|
||||
* @return {Number} The number of `unit`s between min and max. It is the minimum n that min + n * unit >= max.
|
||||
*/
|
||||
diff: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Align value with step of units.
|
||||
* For example, for the date segmenter, if the unit is "Month" and step is 3, the value will be aligned by
|
||||
* seasons.
|
||||
*
|
||||
* @param {*} value The value to be aligned.
|
||||
* @param {Number} step The step of units.
|
||||
* @param {*} unit The unit.
|
||||
* @return {*} Aligned value.
|
||||
*/
|
||||
align: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Add `step` `unit`s to the value.
|
||||
* @param {*} value The value to be added.
|
||||
* @param {Number} step The step of units. Negative value are allowed.
|
||||
* @param {*} unit The unit.
|
||||
*/
|
||||
add: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Given a start point and estimated step size of a range, determine the preferred step size.
|
||||
*
|
||||
* @param {*} start The start point of range.
|
||||
* @param {*} estStepSize The estimated step size.
|
||||
* @return {Object} Return the step size by an object of step x unit.
|
||||
* @return {Number} return.step The step count of units.
|
||||
* @return {Number|Object} return.unit The unit.
|
||||
*/
|
||||
preferredStep: Ext.emptyFn
|
||||
});
|
107
vendor/touch/src/chart/axis/segmenter/Time.js
vendored
Normal file
107
vendor/touch/src/chart/axis/segmenter/Time.js
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @class Ext.chart.axis.segmenter.Time
|
||||
* @extends Ext.chart.axis.segmenter.Segmenter
|
||||
*
|
||||
* Time data type.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.segmenter.Time', {
|
||||
extend: 'Ext.chart.axis.segmenter.Segmenter',
|
||||
alias: 'segmenter.time',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Object} step
|
||||
* If specified, the will override the result of {@link #preferredStep}.
|
||||
*/
|
||||
step: null
|
||||
},
|
||||
|
||||
renderer: function (value, context) {
|
||||
var ExtDate = Ext.Date;
|
||||
switch (context.majorTicks.unit) {
|
||||
case 'y':
|
||||
return ExtDate.format(value, 'Y');
|
||||
case 'mo':
|
||||
return ExtDate.format(value, 'Y-m');
|
||||
case 'd':
|
||||
return ExtDate.format(value, 'Y-m-d');
|
||||
}
|
||||
return ExtDate.format(value, 'Y-m-d\nH:i:s');
|
||||
},
|
||||
|
||||
from: function (value) {
|
||||
return new Date(value);
|
||||
},
|
||||
|
||||
diff: function (min, max, unit) {
|
||||
var ExtDate = Ext.Date;
|
||||
if (isFinite(min)) {
|
||||
min = new Date(min);
|
||||
}
|
||||
if (isFinite(max)) {
|
||||
max = new Date(max);
|
||||
}
|
||||
return ExtDate.diff(min, max, unit);
|
||||
},
|
||||
|
||||
align: function (date, step, unit) {
|
||||
if (unit === 'd' && step >= 7) {
|
||||
date = Ext.Date.align(date, 'd', step);
|
||||
date.setDate(date.getDate() - date.getDay() + 1);
|
||||
return date;
|
||||
} else {
|
||||
return Ext.Date.align(date, unit, step);
|
||||
}
|
||||
},
|
||||
|
||||
add: function (value, step, unit) {
|
||||
return Ext.Date.add(new Date(value), unit, step);
|
||||
},
|
||||
|
||||
preferredStep: function (min, estStepSize) {
|
||||
if (this.getStep()) {
|
||||
return this.getStep();
|
||||
}
|
||||
var from = new Date(+min),
|
||||
to = new Date(+min + Math.ceil(estStepSize)),
|
||||
ExtDate = Ext.Date,
|
||||
units = [
|
||||
[ExtDate.YEAR, 1, 2, 5, 10, 20, 50, 100, 200, 500],
|
||||
[ExtDate.MONTH, 1, 3, 6],
|
||||
[ExtDate.DAY, 1, 7, 14],
|
||||
[ExtDate.HOUR, 1, 6, 12],
|
||||
[ExtDate.MINUTE, 1, 5, 15, 30],
|
||||
[ExtDate.SECOND, 1, 5, 15, 30],
|
||||
[ExtDate.MILLI, 1, 2, 5, 10, 20, 50, 100, 200, 500]
|
||||
],
|
||||
result;
|
||||
|
||||
for (var i = 0; i < units.length; i++) {
|
||||
var unit = units[i][0],
|
||||
diff = this.diff(from, to, unit);
|
||||
if (diff > 0) {
|
||||
for (var j = 1; j < units[i].length; j++) {
|
||||
if (diff <= units[i][j]) {
|
||||
result = {
|
||||
unit: unit,
|
||||
step: units[i][j]
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
i--;
|
||||
result = {
|
||||
unit: units[i][0],
|
||||
step: 1
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
result = {unit: ExtDate.DAY, step: 1}; // Default step is one Day.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
715
vendor/touch/src/chart/axis/sprite/Axis.js
vendored
Normal file
715
vendor/touch/src/chart/axis/sprite/Axis.js
vendored
Normal file
|
@ -0,0 +1,715 @@
|
|||
/**
|
||||
* @private
|
||||
* @class Ext.chart.axis.sprite.Axis
|
||||
* @extends Ext.draw.sprite.Sprite
|
||||
*
|
||||
* The axis sprite. Currently all types of the axis will be rendered with this sprite.
|
||||
* TODO(touch-2.2): Split different types of axis into different sprite classes.
|
||||
*/
|
||||
Ext.define('Ext.chart.axis.sprite.Axis', {
|
||||
extend: 'Ext.draw.sprite.Sprite',
|
||||
mixins: {
|
||||
markerHolder: 'Ext.chart.MarkerHolder'
|
||||
},
|
||||
|
||||
requires: ['Ext.draw.sprite.Text'],
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Boolean} grid 'true' if the axis has a grid.
|
||||
*/
|
||||
grid: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} axisLine 'true' if the main line of the axis is drawn.
|
||||
*/
|
||||
axisLine: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} minorTricks 'true' if the axis has sub ticks.
|
||||
*/
|
||||
minorTicks: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {Number} minorTickSize The length of the minor ticks.
|
||||
*/
|
||||
minorTickSize: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} majorTicks 'true' if the axis has major ticks.
|
||||
*/
|
||||
majorTicks: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {Number} majorTickSize The length of the major ticks.
|
||||
*/
|
||||
majorTickSize: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} length The total length of the axis.
|
||||
*/
|
||||
length: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} startGap Axis start determined by the chart inset padding.
|
||||
*/
|
||||
startGap: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} endGap Axis end determined by the chart inset padding.
|
||||
*/
|
||||
endGap: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} dataMin The minimum value of the axis data.
|
||||
*/
|
||||
dataMin: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} dataMax The maximum value of the axis data.
|
||||
*/
|
||||
dataMax: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} visibleMin The minimum value that is displayed.
|
||||
*/
|
||||
visibleMin: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} visibleMax The maximum value that is displayed.
|
||||
*/
|
||||
visibleMax: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {String} position The position of the axis on the chart.
|
||||
*/
|
||||
position: 'enums(left,right,top,bottom,angular,radial)',
|
||||
|
||||
/**
|
||||
* @cfg {Number} minStepSize The minimum step size between ticks.
|
||||
*/
|
||||
minStepSize: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} estStepSize The estimated step size between ticks.
|
||||
*/
|
||||
estStepSize: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Unused.
|
||||
*/
|
||||
titleOffset: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} textPadding The padding around axis labels to determine collision.
|
||||
*/
|
||||
textPadding: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} min The minimum value of the axis.
|
||||
*/
|
||||
min: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} max The maximum value of the axis.
|
||||
*/
|
||||
max: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} centerX The central point of the angular axis on the x-axis.
|
||||
*/
|
||||
centerX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} centerY The central point of the angular axis on the y-axis.
|
||||
*/
|
||||
centerY: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} radius
|
||||
* Unused.
|
||||
*/
|
||||
radius: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} The starting rotation of the angular axis.
|
||||
*/
|
||||
baseRotation: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Unused.
|
||||
*/
|
||||
data: 'default',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} 'true' if the estimated step size is adjusted by text size.
|
||||
*/
|
||||
enlargeEstStepSizeByText: 'bool'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
grid: false,
|
||||
axisLine: true,
|
||||
minorTicks: false,
|
||||
minorTickSize: 3,
|
||||
majorTicks: true,
|
||||
majorTickSize: 5,
|
||||
length: 0,
|
||||
startGap: 0,
|
||||
endGap: 0,
|
||||
visibleMin: 0,
|
||||
visibleMax: 1,
|
||||
dataMin: 0,
|
||||
dataMax: 1,
|
||||
position: '',
|
||||
minStepSize: 0,
|
||||
estStepSize: 20,
|
||||
min: 0,
|
||||
max: 1,
|
||||
centerX: 0,
|
||||
centerY: 0,
|
||||
radius: 1,
|
||||
baseRotation: 0,
|
||||
data: null,
|
||||
titleOffset: 0,
|
||||
textPadding: 5,
|
||||
scalingCenterY: 0,
|
||||
scalingCenterX: 0,
|
||||
// Override default
|
||||
strokeStyle: 'black',
|
||||
enlargeEstStepSizeByText: false
|
||||
},
|
||||
|
||||
dirtyTriggers: {
|
||||
minorTickSize: 'bbox',
|
||||
majorTickSize: 'bbox',
|
||||
position: 'bbox,layout',
|
||||
axisLine: 'bbox,layout',
|
||||
min: 'layout',
|
||||
max: 'layout',
|
||||
length: 'layout',
|
||||
minStepSize: 'layout',
|
||||
estStepSize: 'layout',
|
||||
data: 'layout',
|
||||
dataMin: 'layout',
|
||||
dataMax: 'layout',
|
||||
visibleMin: 'layout',
|
||||
visibleMax: 'layout',
|
||||
enlargeEstStepSizeByText: 'layout'
|
||||
},
|
||||
updaters: {
|
||||
'layout': function () {
|
||||
this.doLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
|
||||
/**
|
||||
* @cfg {Object} label
|
||||
*
|
||||
* The label configuration object for the Axis. This object may include style attributes
|
||||
* like `spacing`, `padding`, `font` that receives a string or number and
|
||||
* returns a new string with the modified values.
|
||||
*/
|
||||
label: null,
|
||||
|
||||
/**
|
||||
* @cfg {Object|Ext.chart.axis.layout.Layout} layout The layout configuration used by the axis.
|
||||
*/
|
||||
layout: null,
|
||||
|
||||
/**
|
||||
* @cfg {Object|Ext.chart.axis.segmenter.Segmenter} segmenter The method of segmenter used by the axis.
|
||||
*/
|
||||
segmenter: null,
|
||||
|
||||
/**
|
||||
* @cfg {Function} renderer Allows direct customisation of rendered axis sprites.
|
||||
*/
|
||||
renderer: null,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} layoutContext Stores the context after calculating layout.
|
||||
*/
|
||||
layoutContext: null,
|
||||
|
||||
/**
|
||||
* @cfg {Ext.chart.axis.Axis} axis The axis represented by the this sprite.
|
||||
*/
|
||||
axis: null
|
||||
},
|
||||
|
||||
thickness: 0,
|
||||
|
||||
stepSize: 0,
|
||||
|
||||
getBBox: function () { return null; },
|
||||
|
||||
doLayout: function () {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
layout = me.getLayout(),
|
||||
min = attr.dataMin + (attr.dataMax - attr.dataMin) * attr.visibleMin,
|
||||
max = attr.dataMin + (attr.dataMax - attr.dataMin) * attr.visibleMax,
|
||||
context = {
|
||||
attr: attr,
|
||||
segmenter: me.getSegmenter()
|
||||
};
|
||||
|
||||
if (attr.position === 'left' || attr.position === 'right') {
|
||||
attr.translationX = 0;
|
||||
attr.translationY = max * attr.length / (max - min);
|
||||
attr.scalingX = 1;
|
||||
attr.scalingY = -attr.length / (max - min);
|
||||
attr.scalingCenterY = 0;
|
||||
attr.scalingCenterX = 0;
|
||||
me.applyTransformations(true);
|
||||
} else if (attr.position === 'top' || attr.position === 'bottom') {
|
||||
attr.translationX = -min * attr.length / (max - min);
|
||||
attr.translationY = 0;
|
||||
attr.scalingX = attr.length / (max - min);
|
||||
attr.scalingY = 1;
|
||||
attr.scalingCenterY = 0;
|
||||
attr.scalingCenterX = 0;
|
||||
me.applyTransformations(true);
|
||||
}
|
||||
|
||||
if (layout) {
|
||||
layout.calculateLayout(context);
|
||||
me.setLayoutContext(context);
|
||||
}
|
||||
},
|
||||
|
||||
iterate: function (snaps, fn) {
|
||||
var i, position;
|
||||
if (snaps.getLabel) {
|
||||
if (snaps.min < snaps.from) {
|
||||
fn.call(this, snaps.min, snaps.getLabel(snaps.min), -1, snaps);
|
||||
}
|
||||
for (i = 0; i <= snaps.steps; i++) {
|
||||
fn.call(this, snaps.get(i), snaps.getLabel(i), i, snaps);
|
||||
}
|
||||
if (snaps.max > snaps.to) {
|
||||
fn.call(this, snaps.max, snaps.getLabel(snaps.max), snaps.steps + 1, snaps);
|
||||
}
|
||||
} else {
|
||||
if (snaps.min < snaps.from) {
|
||||
fn.call(this, snaps.min, snaps.min, -1, snaps);
|
||||
}
|
||||
for (i = 0; i <= snaps.steps; i++) {
|
||||
position = snaps.get(i);
|
||||
fn.call(this, position, position, i, snaps);
|
||||
}
|
||||
if (snaps.max > snaps.to) {
|
||||
fn.call(this, snaps.max, snaps.max, snaps.steps + 1, snaps);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderTicks: function (surface, ctx, layout, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
docked = attr.position,
|
||||
matrix = attr.matrix,
|
||||
halfLineWidth = 0.5 * attr.lineWidth,
|
||||
xx = matrix.getXX(),
|
||||
dx = matrix.getDX(),
|
||||
yy = matrix.getYY(),
|
||||
dy = matrix.getDY(),
|
||||
majorTicks = layout.majorTicks,
|
||||
majorTickSize = attr.majorTickSize,
|
||||
minorTicks = layout.minorTicks,
|
||||
minorTickSize = attr.minorTickSize;
|
||||
|
||||
if (majorTicks) {
|
||||
switch (docked) {
|
||||
case 'right':
|
||||
function getRightTickFn(size) {
|
||||
return function (position, labelText, i) {
|
||||
position = surface.roundPixel(position * yy + dy) + halfLineWidth;
|
||||
ctx.moveTo(0, position);
|
||||
ctx.lineTo(size, position);
|
||||
};
|
||||
}
|
||||
me.iterate(majorTicks, getRightTickFn(majorTickSize));
|
||||
minorTicks && me.iterate(minorTicks, getRightTickFn(minorTickSize));
|
||||
break;
|
||||
case 'left':
|
||||
function getLeftTickFn(size) {
|
||||
return function (position, labelText, i) {
|
||||
position = surface.roundPixel(position * yy + dy) + halfLineWidth;
|
||||
ctx.moveTo(clipRegion[2] - size, position);
|
||||
ctx.lineTo(clipRegion[2], position);
|
||||
};
|
||||
}
|
||||
me.iterate(majorTicks, getLeftTickFn(majorTickSize));
|
||||
minorTicks && me.iterate(minorTicks, getLeftTickFn(minorTickSize));
|
||||
break;
|
||||
case 'bottom':
|
||||
function getBottomTickFn(size) {
|
||||
return function (position, labelText, i) {
|
||||
position = surface.roundPixel(position * xx + dx) - halfLineWidth;
|
||||
ctx.moveTo(position, 0);
|
||||
ctx.lineTo(position, size);
|
||||
};
|
||||
}
|
||||
me.iterate(majorTicks, getBottomTickFn(majorTickSize));
|
||||
minorTicks && me.iterate(minorTicks, getBottomTickFn(minorTickSize));
|
||||
break;
|
||||
case 'top':
|
||||
function getTopTickFn(size) {
|
||||
return function (position, labelText, i) {
|
||||
position = surface.roundPixel(position * xx + dx) - halfLineWidth;
|
||||
ctx.moveTo(position, clipRegion[3]);
|
||||
ctx.lineTo(position, clipRegion[3] - size);
|
||||
};
|
||||
}
|
||||
me.iterate(majorTicks, getTopTickFn(majorTickSize));
|
||||
minorTicks && me.iterate(minorTicks, getTopTickFn(minorTickSize));
|
||||
break;
|
||||
case 'angular':
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
position = position / (attr.max + 1) * Math.PI * 2 + attr.baseRotation;
|
||||
ctx.moveTo(
|
||||
attr.centerX + (attr.length) * Math.cos(position),
|
||||
attr.centerY + (attr.length) * Math.sin(position)
|
||||
);
|
||||
ctx.lineTo(
|
||||
attr.centerX + (attr.length + majorTickSize) * Math.cos(position),
|
||||
attr.centerY + (attr.length + majorTickSize) * Math.sin(position)
|
||||
);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderLabels: function (surface, ctx, layout, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
halfLineWidth = 0.5 * attr.lineWidth,
|
||||
docked = attr.position,
|
||||
matrix = attr.matrix,
|
||||
textPadding = attr.textPadding,
|
||||
xx = matrix.getXX(),
|
||||
dx = matrix.getDX(),
|
||||
yy = matrix.getYY(),
|
||||
dy = matrix.getDY(),
|
||||
thickness = 0,
|
||||
majorTicks = layout.majorTicks,
|
||||
padding = Math.max(attr.majorTickSize, attr.minorTickSize) + attr.lineWidth,
|
||||
label = this.getLabel(), font,
|
||||
lastLabelText = null,
|
||||
textSize = 0, textCount = 0,
|
||||
segmenter = layout.segmenter,
|
||||
renderer = this.getRenderer(),
|
||||
labelInverseMatrix, lastBBox = null, bbox, fly, text;
|
||||
if (majorTicks && label && !label.attr.hidden) {
|
||||
font = label.attr.font;
|
||||
if (ctx.font !== font) {
|
||||
ctx.font = font;
|
||||
} // This can profoundly improve performance.
|
||||
label.setAttributes({translationX: 0, translationY: 0}, true, true);
|
||||
label.applyTransformations();
|
||||
labelInverseMatrix = label.attr.inverseMatrix.elements.slice(0);
|
||||
switch (docked) {
|
||||
case 'left':
|
||||
label.setAttributes({
|
||||
translationX: surface.roundPixel(clipRegion[2] - padding + dx) - halfLineWidth - me.thickness / 2
|
||||
}, true, true);
|
||||
break;
|
||||
case 'right':
|
||||
label.setAttributes({
|
||||
translationX: surface.roundPixel(padding + dx) - halfLineWidth + me.thickness / 2
|
||||
}, true, true);
|
||||
break;
|
||||
case 'top':
|
||||
label.setAttributes({
|
||||
translationY: surface.roundPixel(clipRegion[3] - padding) - halfLineWidth - me.thickness / 2
|
||||
}, true, true);
|
||||
break;
|
||||
case 'bottom':
|
||||
label.setAttributes({
|
||||
translationY: surface.roundPixel(padding) - halfLineWidth + me.thickness / 2
|
||||
}, true, true);
|
||||
break;
|
||||
case 'radial' :
|
||||
label.setAttributes({
|
||||
translationX: attr.centerX
|
||||
}, true, true);
|
||||
break;
|
||||
case 'angular':
|
||||
label.setAttributes({
|
||||
translationY: attr.centerY
|
||||
}, true, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: there are better ways to detect collision.
|
||||
if (docked === 'left' || docked === 'right') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
if (labelText === undefined) {
|
||||
return;
|
||||
}
|
||||
text = renderer ? renderer.call(this, labelText, layout, lastLabelText) : segmenter.renderer(labelText, layout, lastLabelText);
|
||||
lastLabelText = labelText;
|
||||
label.setAttributes({
|
||||
text: String(text),
|
||||
translationY: surface.roundPixel(position * yy + dy)
|
||||
}, true, true);
|
||||
label.applyTransformations();
|
||||
thickness = Math.max(thickness, label.getBBox().width + padding);
|
||||
if (thickness <= me.thickness) {
|
||||
fly = Ext.draw.Matrix.fly(label.attr.matrix.elements.slice(0));
|
||||
bbox = fly.prepend.apply(fly, labelInverseMatrix).transformBBox(label.getBBox(true));
|
||||
if (lastBBox && !Ext.draw.Draw.isBBoxIntersect(bbox, lastBBox, textPadding)) {
|
||||
return;
|
||||
}
|
||||
surface.renderSprite(label);
|
||||
lastBBox = bbox;
|
||||
textSize += bbox.height;
|
||||
textCount++;
|
||||
}
|
||||
});
|
||||
} else if (docked === 'top' || docked === 'bottom') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
if (labelText === undefined) {
|
||||
return;
|
||||
}
|
||||
text = renderer ? renderer.call(this, labelText, layout, lastLabelText) : segmenter.renderer(labelText, layout, lastLabelText);
|
||||
lastLabelText = labelText;
|
||||
label.setAttributes({
|
||||
text: String(text),
|
||||
translationX: surface.roundPixel(position * xx + dx)
|
||||
}, true, true);
|
||||
label.applyTransformations();
|
||||
thickness = Math.max(thickness, label.getBBox().height + padding);
|
||||
if (thickness <= me.thickness) {
|
||||
fly = Ext.draw.Matrix.fly(label.attr.matrix.elements.slice(0));
|
||||
bbox = fly.prepend.apply(fly, labelInverseMatrix).transformBBox(label.getBBox(true));
|
||||
if (lastBBox && !Ext.draw.Draw.isBBoxIntersect(bbox, lastBBox, textPadding)) {
|
||||
return;
|
||||
}
|
||||
surface.renderSprite(label);
|
||||
lastBBox = bbox;
|
||||
textSize += bbox.width;
|
||||
textCount++;
|
||||
}
|
||||
});
|
||||
} else if (docked === 'radial') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
if (labelText === undefined) {
|
||||
return;
|
||||
}
|
||||
text = renderer ? renderer.call(this, labelText, layout, lastLabelText) : segmenter.renderer(labelText, layout, lastLabelText);
|
||||
lastLabelText = labelText;
|
||||
if (typeof text !== 'undefined') {
|
||||
label.setAttributes({
|
||||
text: String(text),
|
||||
translationY: attr.centerY - surface.roundPixel(position) / attr.max * attr.length
|
||||
}, true, true);
|
||||
label.applyTransformations();
|
||||
bbox = label.attr.matrix.transformBBox(label.getBBox(true));
|
||||
if (lastBBox && !Ext.draw.Draw.isBBoxIntersect(bbox, lastBBox)) {
|
||||
return;
|
||||
}
|
||||
surface.renderSprite(label);
|
||||
lastBBox = bbox;
|
||||
textSize += bbox.width;
|
||||
textCount++;
|
||||
}
|
||||
});
|
||||
} else if (docked === 'angular') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
if (labelText === undefined) {
|
||||
return;
|
||||
}
|
||||
text = renderer ? renderer.call(this, labelText, layout, lastLabelText) : segmenter.renderer(labelText, layout, lastLabelText);
|
||||
lastLabelText = labelText;
|
||||
|
||||
if (typeof text !== 'undefined') {
|
||||
var angle = position / (attr.max + 1) * Math.PI * 2 + attr.baseRotation;
|
||||
label.setAttributes({
|
||||
text: String(text),
|
||||
translationX: attr.centerX + (attr.length + 10) * Math.cos(angle),
|
||||
translationY: attr.centerY + (attr.length + 10) * Math.sin(angle)
|
||||
}, true, true);
|
||||
label.applyTransformations();
|
||||
bbox = label.attr.matrix.transformBBox(label.getBBox(true));
|
||||
if (lastBBox && !Ext.draw.Draw.isBBoxIntersect(bbox, lastBBox)) {
|
||||
return;
|
||||
}
|
||||
surface.renderSprite(label);
|
||||
lastBBox = bbox;
|
||||
textSize += bbox.width;
|
||||
textCount++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (attr.enlargeEstStepSizeByText && textCount) {
|
||||
textSize /= textCount;
|
||||
textSize += padding;
|
||||
textSize *= 2;
|
||||
if (attr.estStepSize < textSize) {
|
||||
attr.estStepSize = textSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(me.thickness - (thickness)) > 1) {
|
||||
me.thickness = thickness;
|
||||
attr.bbox.plain.dirty = true;
|
||||
attr.bbox.transform.dirty = true;
|
||||
me.doThicknessChanged();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderAxisLine: function (surface, ctx, layout, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
halfLineWidth = attr.lineWidth * 0.5,
|
||||
docked = attr.position,
|
||||
position;
|
||||
if (attr.axisLine) {
|
||||
switch (docked) {
|
||||
case 'left':
|
||||
position = surface.roundPixel(clipRegion[2]) - halfLineWidth;
|
||||
ctx.moveTo(position, -attr.endGap);
|
||||
ctx.lineTo(position, attr.length + attr.startGap);
|
||||
break;
|
||||
case 'right':
|
||||
ctx.moveTo(halfLineWidth, -attr.endGap);
|
||||
ctx.lineTo(halfLineWidth, attr.length + attr.startGap);
|
||||
break;
|
||||
case 'bottom':
|
||||
ctx.moveTo(-attr.startGap, halfLineWidth);
|
||||
ctx.lineTo(attr.length + attr.endGap, halfLineWidth);
|
||||
break;
|
||||
case 'top':
|
||||
position = surface.roundPixel(clipRegion[3]) - halfLineWidth;
|
||||
ctx.moveTo(-attr.startGap, position);
|
||||
ctx.lineTo(attr.length + attr.endGap, position);
|
||||
break;
|
||||
case 'angular':
|
||||
ctx.moveTo(attr.centerX + attr.length, attr.centerY);
|
||||
ctx.arc(attr.centerX, attr.centerY, attr.length, 0, Math.PI * 2, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderGridLines: function (surface, ctx, layout, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
matrix = attr.matrix,
|
||||
startGap = attr.startGap,
|
||||
endGap = attr.endGap,
|
||||
xx = matrix.getXX(),
|
||||
yy = matrix.getYY(),
|
||||
dx = matrix.getDX(),
|
||||
dy = matrix.getDY(),
|
||||
position = attr.position,
|
||||
majorTicks = layout.majorTicks,
|
||||
anchor, j, lastAnchor;
|
||||
if (attr.grid) {
|
||||
if (majorTicks) {
|
||||
if (position === 'left' || position === 'right') {
|
||||
lastAnchor = attr.min * yy + dy + endGap + startGap;
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
anchor = position * yy + dy + endGap;
|
||||
me.putMarker('horizontal-' + (i % 2 ? 'odd' : 'even'), {
|
||||
y: anchor,
|
||||
height: lastAnchor - anchor
|
||||
}, j = i, true);
|
||||
lastAnchor = anchor;
|
||||
});
|
||||
j++;
|
||||
anchor = 0;
|
||||
me.putMarker('horizontal-' + (j % 2 ? 'odd' : 'even'), {
|
||||
y: anchor,
|
||||
height: lastAnchor - anchor
|
||||
}, j, true);
|
||||
} else if (position === 'top' || position === 'bottom') {
|
||||
lastAnchor = attr.min * xx + dx + startGap;
|
||||
if (startGap) {
|
||||
me.putMarker('vertical-even', {
|
||||
x: 0,
|
||||
width: lastAnchor
|
||||
}, -1, true);
|
||||
}
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
anchor = position * xx + dx + startGap;
|
||||
me.putMarker('vertical-' + (i % 2 ? 'odd' : 'even'), {
|
||||
x: anchor,
|
||||
width: lastAnchor - anchor
|
||||
}, j = i, true);
|
||||
lastAnchor = anchor;
|
||||
});
|
||||
j++;
|
||||
anchor = attr.length + attr.startGap + attr.endGap;
|
||||
me.putMarker('vertical-' + (j % 2 ? 'odd' : 'even'), {
|
||||
x: anchor,
|
||||
width: lastAnchor - anchor
|
||||
}, j, true);
|
||||
} else if (position === 'radial') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
anchor = position / attr.max * attr.length;
|
||||
me.putMarker('circular-' + (i % 2 ? 'odd' : 'even'), {
|
||||
scalingX: anchor,
|
||||
scalingY: anchor
|
||||
}, i, true);
|
||||
lastAnchor = anchor;
|
||||
});
|
||||
} else if (position === 'angular') {
|
||||
me.iterate(majorTicks, function (position, labelText, i) {
|
||||
anchor = position / (attr.max + 1) * Math.PI * 2 + attr.baseRotation;
|
||||
me.putMarker('radial-' + (i % 2 ? 'odd' : 'even'), {
|
||||
rotationRads: anchor,
|
||||
rotationCenterX: 0,
|
||||
rotationCenterY: 0,
|
||||
scalingX: attr.length,
|
||||
scalingY: attr.length
|
||||
}, i, true);
|
||||
lastAnchor = anchor;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
doThicknessChanged: function () {
|
||||
var axis = this.getAxis();
|
||||
if (axis) {
|
||||
axis.onThicknessChanged();
|
||||
}
|
||||
},
|
||||
|
||||
render: function (surface, ctx, clipRegion) {
|
||||
var me = this,
|
||||
layout = me.getLayoutContext();
|
||||
if (layout) {
|
||||
if (false === me.renderLabels(surface, ctx, layout, clipRegion)) {
|
||||
return false;
|
||||
}
|
||||
ctx.beginPath();
|
||||
me.renderTicks(surface, ctx, layout, clipRegion);
|
||||
me.renderAxisLine(surface, ctx, layout, clipRegion);
|
||||
me.renderGridLines(surface, ctx, layout, clipRegion);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
});
|
19
vendor/touch/src/chart/grid/CircularGrid.js
vendored
Normal file
19
vendor/touch/src/chart/grid/CircularGrid.js
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @class Ext.chart.grid.CircularGrid
|
||||
* @extends Ext.draw.sprite.Circle
|
||||
*
|
||||
* Circular Grid sprite. Used by Radar chart to render a series of concentric circles.
|
||||
*/
|
||||
Ext.define('Ext.chart.grid.CircularGrid', {
|
||||
extend: 'Ext.draw.sprite.Circle',
|
||||
alias: 'grid.circular',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
defaults: {
|
||||
r: 1,
|
||||
strokeStyle: '#DDD'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
43
vendor/touch/src/chart/grid/HorizontalGrid.js
vendored
Normal file
43
vendor/touch/src/chart/grid/HorizontalGrid.js
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @class Ext.chart.grid.HorizontalGrid
|
||||
* @extends Ext.draw.sprite.Sprite
|
||||
*
|
||||
* Horizontal Grid sprite. Used in Cartesian Charts.
|
||||
*/
|
||||
Ext.define("Ext.chart.grid.HorizontalGrid", {
|
||||
extend: 'Ext.draw.sprite.Sprite',
|
||||
alias: 'grid.horizontal',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
x: 'number',
|
||||
y: 'number',
|
||||
width: 'number',
|
||||
height: 'number'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
strokeStyle: '#DDD'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render: function (surface, ctx, clipRegion) {
|
||||
var attr = this.attr,
|
||||
y = surface.roundPixel(attr.y),
|
||||
halfLineWidth = ctx.lineWidth * 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.rect(clipRegion[0] - surface.matrix.getDX(), y + halfLineWidth, +clipRegion[2], attr.height);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(clipRegion[0] - surface.matrix.getDX(), y + halfLineWidth);
|
||||
ctx.lineTo(clipRegion[0] + clipRegion[2] - surface.matrix.getDX(), y + halfLineWidth);
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
44
vendor/touch/src/chart/grid/RadialGrid.js
vendored
Normal file
44
vendor/touch/src/chart/grid/RadialGrid.js
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @class Ext.chart.grid.RadialGrid
|
||||
* @extends Ext.draw.sprite.Path
|
||||
*
|
||||
* Radial Grid sprite. Used by Radar chart to render a series of radial lines.
|
||||
* Represents the scale of the radar chart on the yField.
|
||||
*/
|
||||
Ext.define('Ext.chart.grid.RadialGrid', {
|
||||
extend: 'Ext.draw.sprite.Path',
|
||||
alias: 'grid.radial',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
startRadius: 'number',
|
||||
endRadius: 'number'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
startRadius: 0,
|
||||
endRadius: 1,
|
||||
scalingCenterX: 0,
|
||||
scalingCenterY: 0,
|
||||
strokeStyle: '#DDD'
|
||||
},
|
||||
|
||||
dirtyTriggers: {
|
||||
startRadius: 'path,bbox',
|
||||
endRadius: 'path,bbox'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render: function () {
|
||||
this.callSuper(arguments);
|
||||
},
|
||||
|
||||
updatePath: function (path, attr) {
|
||||
var startRadius = attr.startRadius,
|
||||
endRadius = attr.endRadius;
|
||||
path.moveTo(startRadius, 0);
|
||||
path.lineTo(endRadius, 0);
|
||||
}
|
||||
});
|
43
vendor/touch/src/chart/grid/VerticalGrid.js
vendored
Normal file
43
vendor/touch/src/chart/grid/VerticalGrid.js
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @class Ext.chart.grid.VerticalGrid
|
||||
* @extends Ext.draw.sprite.Sprite
|
||||
*
|
||||
* Vertical Grid sprite. Used in Cartesian Charts.
|
||||
*/
|
||||
Ext.define("Ext.chart.grid.VerticalGrid", {
|
||||
extend: 'Ext.draw.sprite.Sprite',
|
||||
alias: 'grid.vertical',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
x: 'number',
|
||||
y: 'number',
|
||||
width: 'number',
|
||||
height: 'number'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
strokeStyle: '#DDD'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
render: function (surface, ctx, clipRegion) {
|
||||
var attr = this.attr,
|
||||
x = surface.roundPixel(attr.x),
|
||||
halfLineWidth = ctx.lineWidth * 0.5;
|
||||
ctx.beginPath();
|
||||
ctx.rect(x - halfLineWidth, clipRegion[1] - surface.matrix.getDY(), attr.width, clipRegion[3]);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - halfLineWidth, clipRegion[1] - surface.matrix.getDY());
|
||||
ctx.lineTo(x - halfLineWidth, clipRegion[1] + clipRegion[3] - surface.matrix.getDY());
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
246
vendor/touch/src/chart/interactions/Abstract.js
vendored
Normal file
246
vendor/touch/src/chart/interactions/Abstract.js
vendored
Normal file
|
@ -0,0 +1,246 @@
|
|||
/**
|
||||
* @class Ext.chart.interactions.Abstract
|
||||
*
|
||||
* Defines a common abstract parent class for all interactions.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.Abstract', {
|
||||
|
||||
xtype: 'interaction',
|
||||
|
||||
mixins: {
|
||||
observable: 'Ext.mixin.Observable'
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} gesture
|
||||
* Specifies which gesture type should be used for starting the interaction.
|
||||
*/
|
||||
gesture: 'tap',
|
||||
|
||||
/**
|
||||
* @cfg {Ext.chart.AbstractChart} chart The chart that the interaction is bound.
|
||||
*/
|
||||
chart: null,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} enabled 'true' if the interaction is enabled.
|
||||
*/
|
||||
enabled: true
|
||||
},
|
||||
|
||||
/**
|
||||
* Android device is emerging too many events so if we re-render every frame it will take for-ever to finish a frame.
|
||||
* This throttle technique will limit the timespan between two frames.
|
||||
*/
|
||||
throttleGap: 0,
|
||||
|
||||
stopAnimationBeforeSync: false,
|
||||
|
||||
constructor: function (config) {
|
||||
var me = this;
|
||||
me.initConfig(config);
|
||||
Ext.ComponentManager.register(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* A method to be implemented by subclasses where all event attachment should occur.
|
||||
*/
|
||||
initialize: Ext.emptyFn,
|
||||
|
||||
updateChart: function (newChart, oldChart) {
|
||||
var me = this, gestures = me.getGestures();
|
||||
if (oldChart) {
|
||||
me.removeChartListener(oldChart);
|
||||
}
|
||||
if (newChart) {
|
||||
me.addChartListener();
|
||||
}
|
||||
},
|
||||
|
||||
updateEnabled: function (enabled) {
|
||||
var me = this,
|
||||
chart = me.getChart();
|
||||
if (chart) {
|
||||
if (enabled) {
|
||||
me.addChartListener();
|
||||
} else {
|
||||
me.removeChartListener(chart);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getGestures: function () {
|
||||
var gestures = {};
|
||||
gestures[this.getGesture()] = this.onGesture;
|
||||
return gestures;
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Placeholder method.
|
||||
*/
|
||||
onGesture: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* @protected Find and return a single series item corresponding to the given event,
|
||||
* or null if no matching item is found.
|
||||
* @param {Event} e
|
||||
* @return {Object} the item object or null if none found.
|
||||
*/
|
||||
getItemForEvent: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
chartXY = chart.getEventXY(e);
|
||||
return chart.getItemForPoint(chartXY[0], chartXY[1]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected Find and return all series items corresponding to the given event.
|
||||
* @param {Event} e
|
||||
* @return {Array} array of matching item objects
|
||||
*/
|
||||
getItemsForEvent: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
chartXY = chart.getEventXY(e);
|
||||
return chart.getItemsForPoint(chartXY[0], chartXY[1]);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
addChartListener: function () {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
gestures = me.getGestures(),
|
||||
gesture;
|
||||
|
||||
if (!me.getEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
function insertGesture(name, fn) {
|
||||
chart.on(
|
||||
name,
|
||||
// wrap the handler so it does not fire if the event is locked by another interaction
|
||||
me.listeners[name] = function (e) {
|
||||
var locks = me.getLocks(), result;
|
||||
if (me.getEnabled() && (!(name in locks) || locks[name] === me)) {
|
||||
result = (Ext.isFunction(fn) ? fn : me[fn]).apply(this, arguments);
|
||||
if (result === false && e && e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
},
|
||||
me
|
||||
);
|
||||
}
|
||||
|
||||
me.listeners = me.listeners || {};
|
||||
for (gesture in gestures) {
|
||||
insertGesture(gesture, gestures[gesture]);
|
||||
}
|
||||
},
|
||||
|
||||
removeChartListener: function (chart) {
|
||||
var me = this,
|
||||
gestures = me.getGestures(),
|
||||
gesture;
|
||||
|
||||
function removeGesture(name) {
|
||||
chart.un(name, me.listeners[name]);
|
||||
delete me.listeners[name];
|
||||
}
|
||||
|
||||
for (gesture in gestures) {
|
||||
removeGesture(gesture);
|
||||
}
|
||||
},
|
||||
|
||||
lockEvents: function () {
|
||||
var me = this,
|
||||
locks = me.getLocks(),
|
||||
args = Array.prototype.slice.call(arguments),
|
||||
i = args.length;
|
||||
while (i--) {
|
||||
locks[args[i]] = me;
|
||||
}
|
||||
},
|
||||
|
||||
unlockEvents: function () {
|
||||
var locks = this.getLocks(),
|
||||
args = Array.prototype.slice.call(arguments),
|
||||
i = args.length;
|
||||
while (i--) {
|
||||
delete locks[args[i]];
|
||||
}
|
||||
},
|
||||
|
||||
getLocks: function () {
|
||||
var chart = this.getChart();
|
||||
return chart.lockedEvents || (chart.lockedEvents = {});
|
||||
},
|
||||
|
||||
isMultiTouch: function () {
|
||||
if (Ext.browser.is.IE10) {
|
||||
return true;
|
||||
}
|
||||
return !(Ext.os.is.MultiTouch === false || Ext.browser.is.AndroidStock2 || Ext.os.is.Desktop);
|
||||
},
|
||||
|
||||
initializeDefaults: Ext.emptyFn,
|
||||
|
||||
doSync: function () {
|
||||
var chart = this.getChart();
|
||||
if (this.syncTimer) {
|
||||
clearTimeout(this.syncTimer);
|
||||
this.syncTimer = null;
|
||||
}
|
||||
if (this.stopAnimationBeforeSync) {
|
||||
chart.resizing = true;
|
||||
}
|
||||
chart.redraw();
|
||||
if (this.stopAnimationBeforeSync) {
|
||||
chart.resizing = false;
|
||||
}
|
||||
this.syncThrottle = Date.now() + this.throttleGap;
|
||||
},
|
||||
|
||||
sync: function () {
|
||||
var me = this;
|
||||
if (me.throttleGap && Ext.frameStartTime < me.syncThrottle) {
|
||||
if (me.syncTimer) {
|
||||
return;
|
||||
}
|
||||
me.syncTimer = setTimeout(function () {
|
||||
me.doSync();
|
||||
}, me.throttleGap);
|
||||
} else {
|
||||
me.doSync();
|
||||
}
|
||||
},
|
||||
|
||||
getItemId: function () {
|
||||
return this.getId();
|
||||
},
|
||||
|
||||
isXType: function (xtype) {
|
||||
return xtype === 'interaction';
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
Ext.ComponentManager.unregister(this);
|
||||
this.listeners = [];
|
||||
this.callSuper();
|
||||
}
|
||||
}, function () {
|
||||
if (Ext.browser.is.AndroidStock2) {
|
||||
this.prototype.throttleGap = 20;
|
||||
} else if (Ext.os.is.Android4) {
|
||||
this.prototype.throttleGap = 40;
|
||||
}
|
||||
});
|
409
vendor/touch/src/chart/interactions/CrossZoom.js
vendored
Normal file
409
vendor/touch/src/chart/interactions/CrossZoom.js
vendored
Normal file
|
@ -0,0 +1,409 @@
|
|||
/**
|
||||
* @class Ext.chart.interactions.CrossZoom
|
||||
* @extends Ext.chart.interactions.Abstract
|
||||
*
|
||||
* The CrossZoom interaction allows the user to zoom in on a selected area of the chart.
|
||||
*
|
||||
* @example preview
|
||||
* var lineChart = new Ext.chart.CartesianChart({
|
||||
* interactions: [{
|
||||
* type: 'crosszoom'
|
||||
* }],
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* style: {
|
||||
* stroke: 'rgb(143,203,203)'
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: 'data1',
|
||||
* marker: {
|
||||
* type: 'path',
|
||||
* path: ['M', -2, 0, 0, 2, 2, 0, 0, -2, 'Z'],
|
||||
* stroke: 'blue',
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }, {
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* fill: true,
|
||||
* xField: 'name',
|
||||
* yField: 'data3',
|
||||
* marker: {
|
||||
* type: 'circle',
|
||||
* radius: 4,
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(lineChart);
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.CrossZoom', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
|
||||
type: 'crosszoom',
|
||||
alias: 'interaction.crosszoom',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Object/Array} axes
|
||||
* Specifies which axes should be made navigable. The config value can take the following formats:
|
||||
*
|
||||
* - An Object whose keys correspond to the {@link Ext.chart.axis.Axis#position position} of each
|
||||
* axis that should be made navigable. Each key's value can either be an Object with further
|
||||
* configuration options for each axis or simply `true` for a default set of options.
|
||||
* {
|
||||
* type: 'crosszoom',
|
||||
* axes: {
|
||||
* left: {
|
||||
* maxZoom: 5,
|
||||
* allowPan: false
|
||||
* },
|
||||
* bottom: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If using the full Object form, the following options can be specified for each axis:
|
||||
*
|
||||
* - minZoom (Number) A minimum zoom level for the axis. Defaults to `1` which is its natural size.
|
||||
* - maxZoom (Number) A maximum zoom level for the axis. Defaults to `10`.
|
||||
* - startZoom (Number) A starting zoom level for the axis. Defaults to `1`.
|
||||
* - allowZoom (Boolean) Whether zooming is allowed for the axis. Defaults to `true`.
|
||||
* - allowPan (Boolean) Whether panning is allowed for the axis. Defaults to `true`.
|
||||
* - startPan (Boolean) A starting panning offset for the axis. Defaults to `0`.
|
||||
*
|
||||
* - An Array of strings, each one corresponding to the {@link Ext.chart.axis.Axis#position position}
|
||||
* of an axis that should be made navigable. The default options will be used for each named axis.
|
||||
*
|
||||
* {
|
||||
* type: 'crosszoom',
|
||||
* axes: ['left', 'bottom']
|
||||
* }
|
||||
*
|
||||
* If the `axes` config is not specified, it will default to making all axes navigable with the
|
||||
* default axis options.
|
||||
*/
|
||||
axes: true,
|
||||
|
||||
gesture: 'drag',
|
||||
|
||||
undoButton: {}
|
||||
},
|
||||
|
||||
stopAnimationBeforeSync: false,
|
||||
|
||||
zoomAnimationInProgress: false,
|
||||
|
||||
constructor: function () {
|
||||
this.callSuper(arguments);
|
||||
this.zoomHistory = [];
|
||||
},
|
||||
|
||||
applyAxes: function (axesConfig) {
|
||||
var result = {};
|
||||
if (axesConfig === true) {
|
||||
return {
|
||||
top: {},
|
||||
right: {},
|
||||
bottom: {},
|
||||
left: {}
|
||||
};
|
||||
} else if (Ext.isArray(axesConfig)) {
|
||||
// array of axis names - translate to full object form
|
||||
result = {};
|
||||
Ext.each(axesConfig, function (axis) {
|
||||
result[axis] = {};
|
||||
});
|
||||
} else if (Ext.isObject(axesConfig)) {
|
||||
Ext.iterate(axesConfig, function (key, val) {
|
||||
// axis name with `true` value -> translate to object
|
||||
if (val === true) {
|
||||
result[key] = {};
|
||||
} else if (val !== false) {
|
||||
result[key] = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
applyUndoButton: function (button, oldButton) {
|
||||
var me = this;
|
||||
if (button) {
|
||||
if (oldButton) {
|
||||
oldButton.destroy();
|
||||
}
|
||||
return Ext.create('Ext.Button', Ext.apply({
|
||||
cls: [],
|
||||
iconCls: 'refresh',
|
||||
text: 'Undo Zoom',
|
||||
disabled: true,
|
||||
handler: function () {
|
||||
me.undoZoom();
|
||||
}
|
||||
}, button));
|
||||
} else if (oldButton) {
|
||||
oldButton.destroy();
|
||||
}
|
||||
},
|
||||
|
||||
getGestures: function () {
|
||||
var me = this,
|
||||
gestures = {};
|
||||
gestures[me.getGesture()] = 'onGesture';
|
||||
gestures[me.getGesture() + 'start'] = 'onGestureStart';
|
||||
gestures[me.getGesture() + 'end'] = 'onGestureEnd';
|
||||
gestures.doubletap = 'onDoubleTap';
|
||||
return gestures;
|
||||
},
|
||||
|
||||
getSurface: function () {
|
||||
return this.getChart() && this.getChart().getSurface('main');
|
||||
},
|
||||
|
||||
setSeriesOpacity: function (opacity) {
|
||||
var surface = this.getChart() && this.getChart().getSurface('series-surface', 'series');
|
||||
if (surface) {
|
||||
surface.element.setStyle('opacity', opacity);
|
||||
}
|
||||
},
|
||||
|
||||
onGestureStart: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
surface = me.getSurface(),
|
||||
region = chart.getInnerRegion(),
|
||||
chartWidth = region[2],
|
||||
chartHeight = region[3],
|
||||
xy = chart.element.getXY(),
|
||||
x = e.pageX - xy[0] - region[0],
|
||||
y = e.pageY - xy[1] - region[1];
|
||||
|
||||
if (me.zoomAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
if (x > 0 && x < chartWidth && y > 0 && y < chartHeight) {
|
||||
me.lockEvents(me.getGesture());
|
||||
me.startX = x;
|
||||
me.startY = y;
|
||||
me.selectionRect = surface.add({
|
||||
type: 'rect',
|
||||
globalAlpha: 0.5,
|
||||
fillStyle: 'rgba(80,80,140,0.5)',
|
||||
strokeStyle: 'rgba(80,80,140,1)',
|
||||
lineWidth: 2,
|
||||
x: x,
|
||||
y: y,
|
||||
width: 0,
|
||||
height: 0,
|
||||
zIndex: 10000
|
||||
});
|
||||
me.setSeriesOpacity(0.8);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGesture: function (e) {
|
||||
var me = this;
|
||||
if (me.zoomAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
if (me.getLocks()[me.getGesture()] === me) {
|
||||
var chart = me.getChart(),
|
||||
surface = me.getSurface(),
|
||||
region = chart.getInnerRegion(),
|
||||
chartWidth = region[2],
|
||||
chartHeight = region[3],
|
||||
xy = chart.element.getXY(),
|
||||
x = e.pageX - xy[0] - region[0],
|
||||
y = e.pageY - xy[1] - region[1];
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x > chartWidth) {
|
||||
x = chartWidth;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y > chartHeight) {
|
||||
y = chartHeight;
|
||||
}
|
||||
me.selectionRect.setAttributes({
|
||||
width: x - me.startX,
|
||||
height: y - me.startY
|
||||
});
|
||||
if (Math.abs(me.startX - x) < 11 || Math.abs(me.startY - y) < 11) {
|
||||
me.selectionRect.setAttributes({globalAlpha: 0.5});
|
||||
} else {
|
||||
me.selectionRect.setAttributes({globalAlpha: 1});
|
||||
}
|
||||
surface.renderFrame();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGestureEnd: function (e) {
|
||||
var me = this;
|
||||
if (me.zoomAnimationInProgress) {
|
||||
return;
|
||||
}
|
||||
if (me.getLocks()[me.getGesture()] === me) {
|
||||
var chart = me.getChart(),
|
||||
surface = me.getSurface(),
|
||||
region = chart.getInnerRegion(),
|
||||
chartWidth = region[2],
|
||||
chartHeight = region[3],
|
||||
xy = chart.element.getXY(),
|
||||
x = e.pageX - xy[0] - region[0],
|
||||
y = e.pageY - xy[1] - region[1];
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x > chartWidth) {
|
||||
x = chartWidth;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y > chartHeight) {
|
||||
y = chartHeight;
|
||||
}
|
||||
if (Math.abs(me.startX - x) < 11 || Math.abs(me.startY - y) < 11) {
|
||||
surface.remove(me.selectionRect);
|
||||
} else {
|
||||
me.zoomBy([
|
||||
Math.min(me.startX, x) / chartWidth,
|
||||
1 - Math.max(me.startY, y) / chartHeight,
|
||||
Math.max(me.startX, x) / chartWidth,
|
||||
1 - Math.min(me.startY, y) / chartHeight
|
||||
]);
|
||||
|
||||
me.selectionRect.setAttributes({
|
||||
x: Math.min(me.startX, x),
|
||||
y: Math.min(me.startY, y),
|
||||
width: Math.abs(me.startX - x),
|
||||
height: Math.abs(me.startY - y)
|
||||
});
|
||||
|
||||
me.selectionRect.fx.setConfig(chart.getAnimate() || {duration: 0});
|
||||
me.selectionRect.setAttributes({
|
||||
globalAlpha: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: chartWidth,
|
||||
height: chartHeight
|
||||
});
|
||||
|
||||
me.zoomAnimationInProgress = true;
|
||||
|
||||
chart.suspendThicknessChanged();
|
||||
me.selectionRect.fx.on('animationend', function () {
|
||||
chart.resumeThicknessChanged();
|
||||
|
||||
surface.remove(me.selectionRect);
|
||||
me.selectionRect = null;
|
||||
|
||||
me.zoomAnimationInProgress = false;
|
||||
});
|
||||
}
|
||||
|
||||
surface.renderFrame();
|
||||
me.sync();
|
||||
me.unlockEvents(me.getGesture());
|
||||
me.setSeriesOpacity(1.0);
|
||||
|
||||
if (!me.zoomAnimationInProgress) {
|
||||
surface.remove(me.selectionRect);
|
||||
me.selectionRect = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
zoomBy: function (region) {
|
||||
var me = this,
|
||||
axisConfigs = me.getAxes(),
|
||||
axes = me.getChart().getAxes(),
|
||||
config,
|
||||
zoomMap = {};
|
||||
|
||||
for (var i = 0; i < axes.length; i++) {
|
||||
var axis = axes[i];
|
||||
config = axisConfigs[axis.getPosition()];
|
||||
if (config && config.allowZoom !== false) {
|
||||
var isSide = axis.isSide(),
|
||||
oldRange = axis.getVisibleRange();
|
||||
zoomMap[axis.getId()] = oldRange.slice(0);
|
||||
if (!isSide) {
|
||||
axis.setVisibleRange([
|
||||
(oldRange[1] - oldRange[0]) * region[0] + oldRange[0],
|
||||
(oldRange[1] - oldRange[0]) * region[2] + oldRange[0]
|
||||
]);
|
||||
} else {
|
||||
axis.setVisibleRange([
|
||||
(oldRange[1] - oldRange[0]) * region[1] + oldRange[0],
|
||||
(oldRange[1] - oldRange[0]) * region[3] + oldRange[0]
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
me.zoomHistory.push(zoomMap);
|
||||
me.getUndoButton().setDisabled(false);
|
||||
},
|
||||
|
||||
undoZoom: function () {
|
||||
var zoomMap = this.zoomHistory.pop(),
|
||||
axes = this.getChart().getAxes();
|
||||
if (zoomMap) {
|
||||
for (var i = 0; i < axes.length; i++) {
|
||||
var axis = axes[i];
|
||||
if (zoomMap[axis.getId()]) {
|
||||
axis.setVisibleRange(zoomMap[axis.getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.getUndoButton().setDisabled(this.zoomHistory.length === 0);
|
||||
this.sync();
|
||||
},
|
||||
|
||||
onDoubleTap: function (e) {
|
||||
this.undoZoom();
|
||||
}
|
||||
});
|
410
vendor/touch/src/chart/interactions/Crosshair.js
vendored
Normal file
410
vendor/touch/src/chart/interactions/Crosshair.js
vendored
Normal file
|
@ -0,0 +1,410 @@
|
|||
/**
|
||||
* The Crosshair interaction allows the user to get precise values for a specific point on the chart.
|
||||
* The values are obtained by single-touch dragging on the chart.
|
||||
*
|
||||
* @example preview
|
||||
* var lineChart = Ext.create('Ext.chart.CartesianChart', {
|
||||
* innerPadding: 20,
|
||||
* interactions: [{
|
||||
* type: 'crosshair',
|
||||
* axes: {
|
||||
* left: {
|
||||
* label: {
|
||||
* fillStyle: 'white'
|
||||
* },
|
||||
* rect: {
|
||||
* fillStyle: 'brown',
|
||||
* radius: 6
|
||||
* }
|
||||
* },
|
||||
* bottom: {
|
||||
* label: {
|
||||
* fontSize: '14px',
|
||||
* fontWeight: 'bold'
|
||||
* }
|
||||
* }
|
||||
* },
|
||||
* lines: {
|
||||
* horizontal: {
|
||||
* strokeStyle: 'brown',
|
||||
* lineWidth: 2,
|
||||
* lineDash: [20, 2, 2, 2, 2, 2, 2, 2]
|
||||
* }
|
||||
* }
|
||||
* }],
|
||||
* store: {
|
||||
* fields: ['name', 'data'],
|
||||
* data: [
|
||||
* {name: 'apple', data: 300},
|
||||
* {name: 'orange', data: 900},
|
||||
* {name: 'banana', data: 800},
|
||||
* {name: 'pear', data: 400},
|
||||
* {name: 'grape', data: 500}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data'],
|
||||
* title: {
|
||||
* text: 'Value',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* label: {
|
||||
* rotationRads: -Math.PI / 4
|
||||
* }
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Category',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'line',
|
||||
* style: {
|
||||
* strokeStyle: 'black'
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: 'data',
|
||||
* marker: {
|
||||
* type: 'circle',
|
||||
* radius: 5,
|
||||
* fillStyle: 'lightblue'
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(lineChart);
|
||||
*/
|
||||
|
||||
Ext.define('Ext.chart.interactions.Crosshair', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
requires: [
|
||||
'Ext.chart.grid.HorizontalGrid',
|
||||
'Ext.chart.grid.VerticalGrid',
|
||||
'Ext.chart.CartesianChart',
|
||||
'Ext.chart.axis.layout.Discrete'
|
||||
],
|
||||
|
||||
type: 'crosshair',
|
||||
alias: 'interaction.crosshair',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Object} axes
|
||||
* Specifies label text and label rect configs on per axis basis or as a single config for all axes.
|
||||
*
|
||||
* {
|
||||
* type: 'crosshair',
|
||||
* axes: {
|
||||
* label: { fillStyle: 'white' },
|
||||
* rect: { fillStyle: 'maroon'}
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* In case per axis configuration is used, an object with keys corresponding
|
||||
* to the {@link Ext.chart.axis.Axis#position position} must be provided.
|
||||
*
|
||||
* {
|
||||
* type: 'crosshair',
|
||||
* axes: {
|
||||
* left: {
|
||||
* label: { fillStyle: 'white' },
|
||||
* rect: {
|
||||
* fillStyle: 'maroon',
|
||||
* radius: 4
|
||||
* }
|
||||
* },
|
||||
* bottom: {
|
||||
* label: {
|
||||
* fontSize: '14px',
|
||||
* fontWeight: 'bold'
|
||||
* },
|
||||
* rect: { fillStyle: 'white' }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If the `axes` config is not specified, the following defaults will be used:
|
||||
* - `label` will use values from the {@link Ext.chart.axis.Axis#label label} config.
|
||||
* - `rect` will use the 'white' fillStyle.
|
||||
*/
|
||||
axes: {
|
||||
top: {label: {}, rect: {}},
|
||||
right: {label: {}, rect: {}},
|
||||
bottom: {label: {}, rect: {}},
|
||||
left: {label: {}, rect: {}}
|
||||
},
|
||||
|
||||
/**
|
||||
* @cfg {Object} lines
|
||||
* Specifies attributes of horizontal and vertical lines that make up the crosshair.
|
||||
* If this config is missing, black dashed lines will be used.
|
||||
*
|
||||
* {
|
||||
* horizontal: {
|
||||
* strokeStyle: 'red',
|
||||
* lineDash: [] // solid line
|
||||
* },
|
||||
* vertical: {
|
||||
* lineWidth: 2,
|
||||
* lineDash: [15, 5, 5, 5]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
lines: {
|
||||
horizontal: {
|
||||
strokeStyle: 'black',
|
||||
lineDash: [5, 5]
|
||||
},
|
||||
vertical: {
|
||||
strokeStyle: 'black',
|
||||
lineDash: [5, 5]
|
||||
}
|
||||
},
|
||||
gesture: 'drag'
|
||||
},
|
||||
|
||||
applyAxes: function (axesConfig, oldAxesConfig) {
|
||||
return Ext.merge(oldAxesConfig || {}, axesConfig);
|
||||
},
|
||||
|
||||
applyLines: function (linesConfig, oldLinesConfig) {
|
||||
return Ext.merge(oldLinesConfig || {}, linesConfig);
|
||||
},
|
||||
|
||||
updateChart: function (chart) {
|
||||
if (!(chart instanceof Ext.chart.CartesianChart)) {
|
||||
throw 'Crosshair interaction can only be used on cartesian charts.';
|
||||
}
|
||||
this.callParent(arguments);
|
||||
},
|
||||
|
||||
getGestures: function () {
|
||||
var me = this,
|
||||
gestures = {};
|
||||
gestures[me.getGesture()] = 'onGesture';
|
||||
gestures[me.getGesture() + 'start'] = 'onGestureStart';
|
||||
gestures[me.getGesture() + 'end'] = 'onGestureEnd';
|
||||
return gestures;
|
||||
},
|
||||
|
||||
onGestureStart: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
surface = chart.getSurface('overlay-surface'),
|
||||
region = chart.getInnerRegion(),
|
||||
chartWidth = region[2],
|
||||
chartHeight = region[3],
|
||||
xy = chart.element.getXY(),
|
||||
x = e.pageX - xy[0] - region[0],
|
||||
y = e.pageY - xy[1] - region[1],
|
||||
axes = chart.getAxes(),
|
||||
axesConfig = me.getAxes(),
|
||||
linesConfig = me.getLines(),
|
||||
axis, axisSurface, axisRegion, axisWidth, axisHeight, axisPosition,
|
||||
axisLabel, labelPadding,
|
||||
axisSprite, attr, axisThickness, lineWidth, halfLineWidth,
|
||||
i;
|
||||
|
||||
if (x > 0 && x < chartWidth && y > 0 && y < chartHeight) {
|
||||
me.lockEvents(me.getGesture());
|
||||
me.horizontalLine = surface.add(Ext.apply({
|
||||
xclass: 'Ext.chart.grid.HorizontalGrid',
|
||||
x: 0,
|
||||
y: y,
|
||||
width: chartWidth
|
||||
}, linesConfig.horizontal));
|
||||
me.verticalLine = surface.add(Ext.apply({
|
||||
xclass: 'Ext.chart.grid.VerticalGrid',
|
||||
x: x,
|
||||
y: 0,
|
||||
height: chartHeight
|
||||
}, linesConfig.vertical));
|
||||
me.axesLabels = me.axesLabels || {};
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
axis = axes[i];
|
||||
axisSurface = axis.getSurface();
|
||||
axisRegion = axisSurface.getRegion();
|
||||
axisSprite = axis.getSprites()[0];
|
||||
axisWidth = axisRegion[2];
|
||||
axisHeight = axisRegion[3];
|
||||
axisPosition = axis.getPosition();
|
||||
attr = axisSprite.attr;
|
||||
axisThickness = axisSprite.thickness;
|
||||
lineWidth = attr.axisLine ? attr.lineWidth : 0;
|
||||
halfLineWidth = lineWidth / 2;
|
||||
labelPadding = Math.max(attr.majorTickSize, attr.minorTickSize) + lineWidth;
|
||||
|
||||
axisLabel = me.axesLabels[axisPosition] = axisSurface.add({type: 'composite'});
|
||||
axisLabel.labelRect = axisLabel.add(Ext.apply({
|
||||
type: 'rect',
|
||||
fillStyle: 'white',
|
||||
x: axisPosition === 'right' ? lineWidth : axisSurface.roundPixel(axisWidth - axisThickness - labelPadding) - halfLineWidth,
|
||||
y: axisPosition === 'bottom' ? lineWidth : axisSurface.roundPixel(axisHeight - axisThickness - labelPadding) - lineWidth,
|
||||
width: axisPosition === 'left' ? axisThickness - halfLineWidth + labelPadding : axisThickness + labelPadding,
|
||||
height: axisPosition === 'top' ? axisThickness + labelPadding : axisThickness + labelPadding
|
||||
}, axesConfig.rect || axesConfig[axisPosition].rect));
|
||||
axisLabel.labelText = axisLabel.add(Ext.apply(Ext.Object.chain(axis.config.label), axesConfig.label || axesConfig[axisPosition].label, {
|
||||
type: 'text',
|
||||
x: (function () {
|
||||
switch (axisPosition) {
|
||||
case 'left':
|
||||
return axisWidth - labelPadding - halfLineWidth - axisThickness / 2;
|
||||
case 'right':
|
||||
return axisThickness / 2 + labelPadding - halfLineWidth;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
})(),
|
||||
y: (function () {
|
||||
switch (axisPosition) {
|
||||
case 'top':
|
||||
return axisHeight - labelPadding - halfLineWidth - axisThickness / 2;
|
||||
case 'bottom':
|
||||
return axisThickness / 2 + labelPadding;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
})()
|
||||
}));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
onGesture: function (e) {
|
||||
var me = this;
|
||||
if (me.getLocks()[me.getGesture()] !== me) {
|
||||
return;
|
||||
}
|
||||
var chart = me.getChart(),
|
||||
surface = chart.getSurface('overlay-surface'),
|
||||
region = Ext.Array.slice(chart.getInnerRegion()),
|
||||
padding = chart.getInnerPadding(),
|
||||
px = padding.left,
|
||||
py = padding.top,
|
||||
chartWidth = region[2],
|
||||
chartHeight = region[3],
|
||||
xy = chart.element.getXY(),
|
||||
x = e.pageX - xy[0] - region[0],
|
||||
y = e.pageY - xy[1] - region[1],
|
||||
axes = chart.getAxes(),
|
||||
axis, axisPosition, axisAlignment, axisSurface, axisSprite, axisMatrix,
|
||||
axisLayoutContext, axisSegmenter,
|
||||
axisLabel, labelBBox, textPadding,
|
||||
xx, yy, dx, dy,
|
||||
xValue, yValue,
|
||||
text,
|
||||
i;
|
||||
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x > chartWidth) {
|
||||
x = chartWidth;
|
||||
}
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y > chartHeight) {
|
||||
y = chartHeight;
|
||||
}
|
||||
x += px;
|
||||
y += py;
|
||||
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
axis = axes[i];
|
||||
axisPosition = axis.getPosition();
|
||||
axisAlignment = axis.getAlignment();
|
||||
axisSurface = axis.getSurface();
|
||||
axisSprite = axis.getSprites()[0];
|
||||
axisMatrix = axisSprite.attr.matrix;
|
||||
textPadding = axisSprite.attr.textPadding * 2;
|
||||
axisLabel = me.axesLabels[axisPosition];
|
||||
axisLayoutContext = axisSprite.getLayoutContext();
|
||||
axisSegmenter = axis.getSegmenter();
|
||||
|
||||
if (axisLabel) {
|
||||
if (axisAlignment === 'vertical') {
|
||||
yy = axisMatrix.getYY();
|
||||
dy = axisMatrix.getDY();
|
||||
yValue = (y - dy - py) / yy;
|
||||
if (axis.getLayout() instanceof Ext.chart.axis.layout.Discrete) {
|
||||
y = Math.round(yValue) * yy + dy + py;
|
||||
yValue = axisSegmenter.from(Math.round(yValue));
|
||||
yValue = axisSprite.attr.data[yValue];
|
||||
} else {
|
||||
yValue = axisSegmenter.from(yValue);
|
||||
}
|
||||
text = axisSegmenter.renderer(yValue, axisLayoutContext);
|
||||
|
||||
axisLabel.setAttributes({translationY: y - py});
|
||||
axisLabel.labelText.setAttributes({text: text});
|
||||
labelBBox = axisLabel.labelText.getBBox();
|
||||
axisLabel.labelRect.setAttributes({
|
||||
height: labelBBox.height + textPadding,
|
||||
y: -(labelBBox.height + textPadding) / 2
|
||||
});
|
||||
axisSurface.renderFrame();
|
||||
} else {
|
||||
xx = axisMatrix.getXX();
|
||||
dx = axisMatrix.getDX();
|
||||
xValue = (x - dx - px) / xx;
|
||||
if (axis.getLayout() instanceof Ext.chart.axis.layout.Discrete) {
|
||||
x = Math.round(xValue) * xx + dx + px;
|
||||
xValue = axisSegmenter.from(Math.round(xValue));
|
||||
xValue = axisSprite.attr.data[xValue];
|
||||
} else {
|
||||
xValue = axisSegmenter.from(xValue);
|
||||
}
|
||||
text = axisSegmenter.renderer(xValue, axisLayoutContext);
|
||||
|
||||
axisLabel.setAttributes({translationX: x - px});
|
||||
axisLabel.labelText.setAttributes({text: text});
|
||||
labelBBox = axisLabel.labelText.getBBox();
|
||||
axisLabel.labelRect.setAttributes({
|
||||
width: labelBBox.width + textPadding,
|
||||
x: -(labelBBox.width + textPadding) / 2
|
||||
});
|
||||
axisSurface.renderFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
me.horizontalLine.setAttributes({y: y});
|
||||
me.verticalLine.setAttributes({x: x});
|
||||
surface.renderFrame();
|
||||
return false;
|
||||
},
|
||||
|
||||
onGestureEnd: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
surface = chart.getSurface('overlay-surface'),
|
||||
axes = chart.getAxes(),
|
||||
axis, axisPosition, axisSurface, axisLabel,
|
||||
i;
|
||||
|
||||
surface.remove(me.verticalLine);
|
||||
surface.remove(me.horizontalLine);
|
||||
|
||||
for (i = 0; i < axes.length; i++) {
|
||||
axis = axes[i];
|
||||
axisPosition = axis.getPosition();
|
||||
axisSurface = axis.getSurface();
|
||||
axisLabel = me.axesLabels[axisPosition];
|
||||
if (axisLabel) {
|
||||
delete me.axesLabels[axisPosition];
|
||||
axisSurface.remove(axisLabel);
|
||||
}
|
||||
axisSurface.renderFrame();
|
||||
}
|
||||
|
||||
surface.renderFrame();
|
||||
me.unlockEvents(me.getGesture());
|
||||
}
|
||||
|
||||
});
|
38
vendor/touch/src/chart/interactions/ItemHighlight.js
vendored
Normal file
38
vendor/touch/src/chart/interactions/ItemHighlight.js
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* @class Ext.chart.interactions.ItemHighlight
|
||||
* @extends Ext.chart.interactions.Abstract
|
||||
*
|
||||
* The ItemHighlight interaction allows the user to highlight series items in the chart.
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.ItemHighlight', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
|
||||
type: 'itemhighlight',
|
||||
alias: 'interaction.itemhighlight',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} gesture
|
||||
* Defines the gesture type that should trigger item highlighting.
|
||||
*/
|
||||
gesture: 'tap'
|
||||
},
|
||||
|
||||
getGestures: function () {
|
||||
var gestures = {};
|
||||
gestures['item' + this.getGesture()] = 'onGesture';
|
||||
gestures[this.getGesture()] = 'onFailedGesture';
|
||||
return gestures;
|
||||
},
|
||||
|
||||
onGesture: function (series, item, e) {
|
||||
e.highlightItem = item;
|
||||
return false;
|
||||
},
|
||||
|
||||
onFailedGesture: function (e) {
|
||||
this.getChart().setHighlightItem(e.highlightItem || null);
|
||||
this.sync();
|
||||
}
|
||||
});
|
107
vendor/touch/src/chart/interactions/ItemInfo.js
vendored
Normal file
107
vendor/touch/src/chart/interactions/ItemInfo.js
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* The ItemInfo interaction allows displaying detailed information about a series data
|
||||
* point in a popup panel.
|
||||
*
|
||||
* To attach this interaction to a chart, include an entry in the chart's
|
||||
* {@link Ext.chart.AbstractChart#interactions interactions} config with the `iteminfo` type:
|
||||
*
|
||||
* new Ext.chart.AbstractChart({
|
||||
* renderTo: Ext.getBody(),
|
||||
* width: 800,
|
||||
* height: 600,
|
||||
* store: store1,
|
||||
* axes: [ ...some axes options... ],
|
||||
* series: [ ...some series options... ],
|
||||
* interactions: [{
|
||||
* type: 'iteminfo',
|
||||
* listeners: {
|
||||
* show: function(me, item, panel) {
|
||||
* panel.setHtml('Stock Price: $' + item.record.get('price'));
|
||||
* }
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.ItemInfo', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
|
||||
type: 'iteminfo',
|
||||
alias: 'interaction.iteminfo',
|
||||
|
||||
/**
|
||||
* @event show
|
||||
* Fires when the info panel is shown.
|
||||
* @param {Ext.chart.interactions.ItemInfo} this The interaction instance
|
||||
* @param {Object} item The item whose info is being displayed
|
||||
* @param {Ext.Panel} panel The panel for displaying the info
|
||||
*/
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} gesture
|
||||
* Defines the gesture type that should trigger the item info panel to be displayed.
|
||||
*/
|
||||
gesture: 'itemtap',
|
||||
|
||||
/**
|
||||
* @cfg {Object} panel
|
||||
* An optional set of configuration overrides for the {@link Ext.Panel} that gets
|
||||
* displayed. This object will be merged with the default panel configuration.
|
||||
*/
|
||||
panel: {
|
||||
modal: true,
|
||||
centered: true,
|
||||
width: 250,
|
||||
height: 300,
|
||||
styleHtmlContent: true,
|
||||
scrollable: 'vertical',
|
||||
hideOnMaskTap: true,
|
||||
fullscreen: false,
|
||||
hidden: true,
|
||||
zIndex: 30,
|
||||
items: [
|
||||
{
|
||||
docked: 'top',
|
||||
xtype: 'toolbar',
|
||||
title: 'Item Detail'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
applyPanel: function (panel, oldPanel) {
|
||||
return Ext.factory(panel, 'Ext.Panel', oldPanel);
|
||||
},
|
||||
|
||||
updatePanel: function (panel, oldPanel) {
|
||||
if (panel) {
|
||||
panel.on('hide', "reset", this);
|
||||
}
|
||||
if (oldPanel) {
|
||||
oldPanel.un('hide', "reset", this);
|
||||
}
|
||||
},
|
||||
|
||||
onGesture: function (series, item) {
|
||||
var me = this,
|
||||
panel = me.getPanel();
|
||||
me.item = item;
|
||||
me.fireEvent('show', me, item, panel);
|
||||
Ext.Viewport.add(panel);
|
||||
panel.show('pop');
|
||||
series.setAttributesForItem(item, { highlighted: true });
|
||||
me.sync();
|
||||
return false;
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
var me = this,
|
||||
item = me.item;
|
||||
if (item) {
|
||||
item.series.setAttributesForItem(item, { highlighted: false });
|
||||
delete me.item;
|
||||
me.sync();
|
||||
}
|
||||
}
|
||||
});
|
502
vendor/touch/src/chart/interactions/PanZoom.js
vendored
Normal file
502
vendor/touch/src/chart/interactions/PanZoom.js
vendored
Normal file
|
@ -0,0 +1,502 @@
|
|||
/**
|
||||
* The PanZoom interaction allows the user to navigate the data for one or more chart
|
||||
* axes by panning and/or zooming. Navigation can be limited to particular axes. Zooming is
|
||||
* performed by pinching on the chart or axis area; panning is performed by single-touch dragging.
|
||||
*
|
||||
* For devices which do not support multiple-touch events, zooming can not be done via pinch gestures; in this case the
|
||||
* interaction will allow the user to perform both zooming and panning using the same single-touch drag gesture.
|
||||
* {@link #modeToggleButton} provides a button to indicate and toggle between two modes.
|
||||
*
|
||||
* @example preview
|
||||
* var lineChart = new Ext.chart.CartesianChart({
|
||||
* interactions: [{
|
||||
* type: 'panzoom',
|
||||
* zoomOnPanGesture: true
|
||||
* }],
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* style: {
|
||||
* stroke: 'rgb(143,203,203)'
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: 'data1',
|
||||
* marker: {
|
||||
* type: 'path',
|
||||
* path: ['M', -2, 0, 0, 2, 2, 0, 0, -2, 'Z'],
|
||||
* stroke: 'blue',
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }, {
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* fill: true,
|
||||
* xField: 'name',
|
||||
* yField: 'data3',
|
||||
* marker: {
|
||||
* type: 'circle',
|
||||
* radius: 4,
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(lineChart);
|
||||
*
|
||||
* The configuration object for the `panzoom` interaction type should specify which axes
|
||||
* will be made navigable via the `axes` config. See the {@link #axes} config documentation
|
||||
* for details on the allowed formats. If the `axes` config is not specified, it will default
|
||||
* to making all axes navigable with the default axis options.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.PanZoom', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
|
||||
type: 'panzoom',
|
||||
alias: 'interaction.panzoom',
|
||||
requires: [
|
||||
'Ext.util.Region',
|
||||
'Ext.draw.Animator'
|
||||
],
|
||||
|
||||
config: {
|
||||
|
||||
/**
|
||||
* @cfg {Object/Array} axes
|
||||
* Specifies which axes should be made navigable. The config value can take the following formats:
|
||||
*
|
||||
* - An Object with keys corresponding to the {@link Ext.chart.axis.Axis#position position} of each
|
||||
* axis that should be made navigable. Each key's value can either be an Object with further
|
||||
* configuration options for each axis or simply `true` for a default set of options.
|
||||
*
|
||||
* {
|
||||
* type: 'panzoom',
|
||||
* axes: {
|
||||
* left: {
|
||||
* maxZoom: 5,
|
||||
* allowPan: false
|
||||
* },
|
||||
* bottom: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* If using the full Object form, the following options can be specified for each axis:
|
||||
*
|
||||
* - minZoom (Number) A minimum zoom level for the axis. Defaults to `1` which is its natural size.
|
||||
* - maxZoom (Number) A maximum zoom level for the axis. Defaults to `10`.
|
||||
* - startZoom (Number) A starting zoom level for the axis. Defaults to `1`.
|
||||
* - allowZoom (Boolean) Whether zooming is allowed for the axis. Defaults to `true`.
|
||||
* - allowPan (Boolean) Whether panning is allowed for the axis. Defaults to `true`.
|
||||
* - startPan (Boolean) A starting panning offset for the axis. Defaults to `0`.
|
||||
*
|
||||
* - An Array of strings, each one corresponding to the {@link Ext.chart.axis.Axis#position position}
|
||||
* of an axis that should be made navigable. The default options will be used for each named axis.
|
||||
*
|
||||
* {
|
||||
* type: 'panzoom',
|
||||
* axes: ['left', 'bottom']
|
||||
* }
|
||||
*
|
||||
* If the `axes` config is not specified, it will default to making all axes navigable with the
|
||||
* default axis options.
|
||||
*/
|
||||
axes: {
|
||||
top: {},
|
||||
right: {},
|
||||
bottom: {},
|
||||
left: {}
|
||||
},
|
||||
|
||||
minZoom: null,
|
||||
|
||||
maxZoom: null,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} showOverflowArrows
|
||||
* If `true`, arrows will be conditionally shown at either end of each axis to indicate that the
|
||||
* axis is overflowing and can therefore be panned in that direction. Set this to `false` to
|
||||
* prevent the arrows from being displayed.
|
||||
*/
|
||||
showOverflowArrows: true,
|
||||
|
||||
/**
|
||||
* @cfg {Object} overflowArrowOptions
|
||||
* A set of optional overrides for the overflow arrow sprites' options. Only relevant when
|
||||
* {@link #showOverflowArrows} is `true`.
|
||||
*/
|
||||
|
||||
gesture: 'pinch',
|
||||
|
||||
panGesture: 'drag',
|
||||
|
||||
zoomOnPanGesture: false,
|
||||
|
||||
modeToggleButton: {
|
||||
cls: ['x-panzoom-toggle', 'x-zooming'],
|
||||
iconCls: 'expand'
|
||||
},
|
||||
|
||||
hideLabelInGesture: false //Ext.os.is.Android
|
||||
},
|
||||
|
||||
stopAnimationBeforeSync: true,
|
||||
|
||||
applyAxes: function (axesConfig, oldAxesConfig) {
|
||||
return Ext.merge(oldAxesConfig || {}, axesConfig);
|
||||
},
|
||||
|
||||
applyZoomOnPanGesture: function (zoomOnPanGesture) {
|
||||
this.getChart();
|
||||
if (this.isMultiTouch()) {
|
||||
return false;
|
||||
}
|
||||
return zoomOnPanGesture;
|
||||
},
|
||||
|
||||
updateZoomOnPanGesture: function (zoomOnPanGesture) {
|
||||
if (!this.isMultiTouch()) {
|
||||
var button = this.getModeToggleButton(),
|
||||
zoomModeCls = Ext.baseCSSPrefix + 'zooming';
|
||||
if (zoomOnPanGesture) {
|
||||
button.addCls(zoomModeCls);
|
||||
if (!button.config.hideText) {
|
||||
button.setText('Zoom');
|
||||
}
|
||||
} else {
|
||||
button.removeCls(zoomModeCls);
|
||||
if (!button.config.hideText) {
|
||||
button.setText('Pan');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggleMode: function () {
|
||||
var me = this;
|
||||
if (!me.isMultiTouch()) {
|
||||
me.setZoomOnPanGesture(!me.getZoomOnPanGesture());
|
||||
}
|
||||
},
|
||||
|
||||
applyModeToggleButton: function (button, oldButton) {
|
||||
var me = this,
|
||||
result = Ext.factory(button, "Ext.Button", oldButton);
|
||||
if (result && !oldButton) {
|
||||
result.setHandler(function () {
|
||||
me.toggleMode();
|
||||
});
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
getGestures: function () {
|
||||
var me = this,
|
||||
gestures = {};
|
||||
gestures[me.getGesture()] = 'onGesture';
|
||||
gestures[me.getGesture() + 'start'] = 'onGestureStart';
|
||||
gestures[me.getGesture() + 'end'] = 'onGestureEnd';
|
||||
gestures[me.getPanGesture()] = 'onPanGesture';
|
||||
gestures[me.getPanGesture() + 'start'] = 'onPanGestureStart';
|
||||
gestures[me.getPanGesture() + 'end'] = 'onPanGestureEnd';
|
||||
gestures.doubletap = 'onDoubleTap';
|
||||
return gestures;
|
||||
},
|
||||
|
||||
onDoubleTap: function (e) {
|
||||
|
||||
},
|
||||
|
||||
onPanGestureStart: function (e) {
|
||||
if (!e || !e.touches || e.touches.length < 2) { //Limit drags to single touch
|
||||
var me = this,
|
||||
region = me.getChart().getInnerRegion(),
|
||||
xy = me.getChart().element.getXY();
|
||||
me.startX = e.pageX - xy[0] - region[0];
|
||||
me.startY = e.pageY - xy[1] - region[1];
|
||||
me.oldVisibleRanges = null;
|
||||
me.hideLabels();
|
||||
me.getChart().suspendThicknessChanged();
|
||||
me.lockEvents(me.getPanGesture());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onPanGesture: function (e) {
|
||||
if (this.getLocks()[this.getPanGesture()] === this) { //Limit drags to single touch
|
||||
var me = this,
|
||||
region = me.getChart().getInnerRegion(),
|
||||
xy = me.getChart().element.getXY();
|
||||
if (me.getZoomOnPanGesture()) {
|
||||
me.transformAxesBy(me.getZoomableAxes(e), 0, 0, (e.pageX - xy[0] - region[0]) / me.startX, me.startY / (e.pageY - xy[1] - region[1]));
|
||||
} else {
|
||||
me.transformAxesBy(me.getPannableAxes(e), e.pageX - xy[0] - region[0] - me.startX, e.pageY - xy[1] - region[1] - me.startY, 1, 1);
|
||||
}
|
||||
me.sync();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onPanGestureEnd: function (e) {
|
||||
var me = this;
|
||||
if (this.getLocks()[this.getPanGesture()] === this) {
|
||||
me.getChart().resumeThicknessChanged();
|
||||
me.showLabels();
|
||||
me.sync();
|
||||
me.unlockEvents(me.getGestures());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGestureStart: function (e) {
|
||||
if (e.touches && e.touches.length === 2) {
|
||||
var me = this,
|
||||
xy = me.getChart().element.getXY(),
|
||||
region = me.getChart().getInnerRegion(),
|
||||
x = xy[0] + region[0],
|
||||
y = xy[1] + region[1],
|
||||
newPoints = [e.touches[0].point.x - x, e.touches[0].point.y - y, e.touches[1].point.x - x, e.touches[1].point.y - y],
|
||||
xDistance = Math.max(44, Math.abs(newPoints[2] - newPoints[0])),
|
||||
yDistance = Math.max(44, Math.abs(newPoints[3] - newPoints[1]));
|
||||
me.getChart().suspendThicknessChanged();
|
||||
me.lastZoomDistances = [xDistance, yDistance];
|
||||
me.lastPoints = newPoints;
|
||||
me.oldVisibleRanges = null;
|
||||
me.hideLabels();
|
||||
me.lockEvents(me.getGesture());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGesture: function (e) {
|
||||
if (this.getLocks()[this.getGesture()] === this) {
|
||||
var me = this,
|
||||
region = me.getChart().getInnerRegion(),
|
||||
xy = me.getChart().element.getXY(),
|
||||
x = xy[0] + region[0],
|
||||
y = xy[1] + region[1],
|
||||
abs = Math.abs,
|
||||
lastPoints = me.lastPoints,
|
||||
newPoints = [e.touches[0].point.x - x, e.touches[0].point.y - y, e.touches[1].point.x - x, e.touches[1].point.y - y],
|
||||
xDistance = Math.max(44, abs(newPoints[2] - newPoints[0])),
|
||||
yDistance = Math.max(44, abs(newPoints[3] - newPoints[1])),
|
||||
lastDistances = this.lastZoomDistances || [xDistance, yDistance],
|
||||
zoomX = xDistance / lastDistances[0],
|
||||
zoomY = yDistance / lastDistances[1];
|
||||
|
||||
me.transformAxesBy(me.getZoomableAxes(e),
|
||||
region[2] * (zoomX - 1) / 2 + newPoints[2] - lastPoints[2] * zoomX,
|
||||
region[3] * (zoomY - 1) / 2 + newPoints[3] - lastPoints[3] * zoomY,
|
||||
zoomX,
|
||||
zoomY);
|
||||
me.sync();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGestureEnd: function (e) {
|
||||
var me = this;
|
||||
if (me.getLocks()[me.getGesture()] === me) {
|
||||
me.getChart().resumeThicknessChanged();
|
||||
me.showLabels();
|
||||
me.sync();
|
||||
me.unlockEvents(me.getGestures());
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
hideLabels: function () {
|
||||
if (this.getHideLabelInGesture()) {
|
||||
this.eachInteractiveAxes(function (axis) {
|
||||
axis.hideLabels();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showLabels: function () {
|
||||
if (this.getHideLabelInGesture()) {
|
||||
this.eachInteractiveAxes(function (axis) {
|
||||
axis.showLabels();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
isEventOnAxis: function (e, axis) {
|
||||
// TODO: right now this uses the current event position but really we want to only
|
||||
// use the gesture's start event. Pinch does not give that to us though.
|
||||
var region = axis.getSurface().getRegion();
|
||||
return region[0] <= e.pageX && e.pageX <= region[0] + region[2] && region[1] <= e.pageY && e.pageY <= region[1] + region[3];
|
||||
},
|
||||
|
||||
getPannableAxes: function (e) {
|
||||
var me = this,
|
||||
axisConfigs = me.getAxes(),
|
||||
axes = me.getChart().getAxes(),
|
||||
i, ln = axes.length,
|
||||
result = [], isEventOnAxis = false,
|
||||
config;
|
||||
|
||||
if (e) {
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (this.isEventOnAxis(e, axes[i])) {
|
||||
isEventOnAxis = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
config = axisConfigs[axes[i].getPosition()];
|
||||
if (config && config.allowPan !== false && (!isEventOnAxis || this.isEventOnAxis(e, axes[i]))) {
|
||||
result.push(axes[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
getZoomableAxes: function (e) {
|
||||
var me = this,
|
||||
axisConfigs = me.getAxes(),
|
||||
axes = me.getChart().getAxes(),
|
||||
result = [],
|
||||
i, ln = axes.length, axis,
|
||||
isEventOnAxis = false, config;
|
||||
|
||||
if (e) {
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (this.isEventOnAxis(e, axes[i])) {
|
||||
isEventOnAxis = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
config = axisConfigs[axis.getPosition()];
|
||||
if (config && config.allowZoom !== false && (!isEventOnAxis || this.isEventOnAxis(e, axis))) {
|
||||
result.push(axis);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
eachInteractiveAxes: function (fn) {
|
||||
var me = this,
|
||||
axisConfigs = me.getAxes(),
|
||||
axes = me.getChart().getAxes();
|
||||
for (var i = 0; i < axes.length; i++) {
|
||||
if (axisConfigs[axes[i].getPosition()]) {
|
||||
if (false === fn.call(this, axes[i])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
transformAxesBy: function (axes, panX, panY, sx, sy) {
|
||||
var region = this.getChart().getInnerRegion(),
|
||||
axesCfg = this.getAxes(), axisCfg,
|
||||
oldVisibleRanges = this.oldVisibleRanges,
|
||||
result = false;
|
||||
|
||||
if (!oldVisibleRanges) {
|
||||
this.oldVisibleRanges = oldVisibleRanges = {};
|
||||
this.eachInteractiveAxes(function (axis) {
|
||||
oldVisibleRanges[axis.getId()] = axis.getVisibleRange();
|
||||
});
|
||||
}
|
||||
|
||||
if (!region) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < axes.length; i++) {
|
||||
axisCfg = axesCfg[axes[i].getPosition()];
|
||||
result = this.transformAxisBy(axes[i], oldVisibleRanges[axes[i].getId()], panX, panY, sx, sy, this.minZoom || axisCfg.minZoom, this.maxZoom || axisCfg.maxZoom) || result;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
transformAxisBy: function (axis, oldVisibleRange, panX, panY, sx, sy, minZoom, maxZoom) {
|
||||
var me = this,
|
||||
visibleLength = oldVisibleRange[1] - oldVisibleRange[0],
|
||||
visibleRange = axis.getVisibleRange(),
|
||||
actualMinZoom = minZoom || me.getMinZoom() || axis.config.minZoom,
|
||||
actualMaxZoom = maxZoom || me.getMaxZoom() || axis.config.maxZoom,
|
||||
region = me.getChart().getInnerRegion(),
|
||||
left, right;
|
||||
if (!region) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isSide = axis.isSide(),
|
||||
length = isSide ? region[3] : region[2],
|
||||
pan = isSide ? -panY : panX;
|
||||
visibleLength /= isSide ? sy : sx;
|
||||
if (visibleLength < 0) {
|
||||
visibleLength = -visibleLength;
|
||||
}
|
||||
|
||||
if (visibleLength * actualMinZoom > 1) {
|
||||
visibleLength = 1;
|
||||
}
|
||||
|
||||
if (visibleLength * actualMaxZoom < 1) {
|
||||
visibleLength = 1 / actualMaxZoom;
|
||||
}
|
||||
left = oldVisibleRange[0];
|
||||
right = oldVisibleRange[1];
|
||||
|
||||
visibleRange = visibleRange[1] - visibleRange[0];
|
||||
if (visibleLength === visibleRange && visibleRange === 1) {
|
||||
return;
|
||||
}
|
||||
axis.setVisibleRange([
|
||||
(oldVisibleRange[0] + oldVisibleRange[1] - visibleLength) * 0.5 - pan / length * visibleLength,
|
||||
(oldVisibleRange[0] + oldVisibleRange[1] + visibleLength) * 0.5 - pan / length * visibleLength
|
||||
]);
|
||||
return (Math.abs(left - axis.getVisibleRange()[0]) > 1e-10 || Math.abs(right - axis.getVisibleRange()[1]) > 1e-10);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.setModeToggleButton(null);
|
||||
this.callSuper();
|
||||
}
|
||||
});
|
197
vendor/touch/src/chart/interactions/Rotate.js
vendored
Normal file
197
vendor/touch/src/chart/interactions/Rotate.js
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
/**
|
||||
* @class Ext.chart.interactions.Rotate
|
||||
* @extends Ext.chart.interactions.Abstract
|
||||
*
|
||||
* The Rotate interaction allows the user to rotate a polar chart about its central point.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.PolarChart({
|
||||
* animate: true,
|
||||
* interactions: ['rotate'],
|
||||
* colors: ["#115fa6", "#94ae0a", "#a61120", "#ff8809", "#ffd13e"],
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* series: [{
|
||||
* type: 'pie',
|
||||
* label: {
|
||||
* field: 'name',
|
||||
* display: 'rotate'
|
||||
* },
|
||||
* xField: 'data3',
|
||||
* donut: 30
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.Rotate', {
|
||||
extend: 'Ext.chart.interactions.Abstract',
|
||||
|
||||
type: 'rotate',
|
||||
|
||||
alias: 'interaction.rotate',
|
||||
|
||||
/**
|
||||
* @event rotate
|
||||
* Fires on every tick of the rotation
|
||||
* @param {Ext.chart.interactions.Rotate} this This interaction.
|
||||
* @param {Number} angle The new current rotation angle.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @event rotationEnd
|
||||
* Fires after a user finishes the rotation
|
||||
* @param {Ext.chart.interactions.Rotate} this This interaction.
|
||||
* @param {Number} angle The new current rotation angle.
|
||||
*/
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} gesture
|
||||
* Defines the gesture type that will be used to rotate the chart. Currently only
|
||||
* supports `pinch` for two-finger rotation and `drag` for single-finger rotation.
|
||||
*/
|
||||
gesture: 'rotate',
|
||||
|
||||
/**
|
||||
* @cfg {Number} currentRotation
|
||||
* Saves the current rotation of the series. Accepts negative values and values > 360 ( / 180 * Math.PI)
|
||||
* @private
|
||||
*/
|
||||
currentRotation: 0
|
||||
},
|
||||
|
||||
oldRotations: null,
|
||||
|
||||
getGestures: function() {
|
||||
return {
|
||||
rotate: 'onRotate',
|
||||
rotateend: 'onRotate',
|
||||
dragstart: 'onGestureStart',
|
||||
drag: 'onGesture',
|
||||
dragend: 'onGestureEnd'
|
||||
};
|
||||
},
|
||||
|
||||
getAngle: function(e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
xy = chart.getEventXY(e),
|
||||
center = chart.getCenter();
|
||||
|
||||
return Math.atan2(
|
||||
xy[1] - center[1],
|
||||
xy[0] - center[0]
|
||||
);
|
||||
},
|
||||
|
||||
getEventRadius: function(e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
xy = chart.getEventXY(e),
|
||||
center = chart.getCenter(),
|
||||
dx = xy[0] - center[0],
|
||||
dy = xy[1] - center[1];
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
},
|
||||
|
||||
onGestureStart: function(e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
radius = chart.getRadius(),
|
||||
eventRadius = me.getEventRadius(e);
|
||||
|
||||
if (radius >= eventRadius) {
|
||||
me.lockEvents('drag');
|
||||
me.angle = me.getAngle(e);
|
||||
me.oldRotations = {};
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onGesture: function(e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
angle = me.getAngle(e) - me.angle,
|
||||
axes = chart.getAxes(),
|
||||
series = chart.getSeries(), seriesItem,
|
||||
oldRotations = me.oldRotations,
|
||||
axis, oldRotation, i, ln;
|
||||
|
||||
if (me.getLocks().drag === me) {
|
||||
chart.suspendAnimation();
|
||||
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axis = axes[i];
|
||||
oldRotation = oldRotations[axis.getId()] || (oldRotations[axis.getId()] = axis.getRotation());
|
||||
axis.setRotation(angle + oldRotation);
|
||||
}
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
seriesItem = series[i];
|
||||
oldRotation = oldRotations[seriesItem.getId()] || (oldRotations[seriesItem.getId()] = seriesItem.getRotation());
|
||||
|
||||
seriesItem.setRotation(angle + oldRotation);
|
||||
}
|
||||
|
||||
me.setCurrentRotation(angle + oldRotation);
|
||||
|
||||
me.fireEvent('rotate', me, me.getCurrentRotation());
|
||||
|
||||
me.sync();
|
||||
chart.resumeAnimation();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
rotateTo: function(angle) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
axes = chart.getAxes(),
|
||||
series = chart.getSeries(),
|
||||
i, ln;
|
||||
|
||||
chart.suspendAnimation();
|
||||
|
||||
for (i = 0, ln = axes.length; i < ln; i++) {
|
||||
axes[i].setRotation(angle);
|
||||
}
|
||||
|
||||
for (i = 0, ln = series.length; i < ln; i++) {
|
||||
series[i].setRotation(angle);
|
||||
}
|
||||
|
||||
me.setCurrentRotation(angle);
|
||||
|
||||
me.fireEvent('rotate', me, me.getCurrentRotation());
|
||||
|
||||
me.sync();
|
||||
chart.resumeAnimation();
|
||||
},
|
||||
|
||||
onGestureEnd: function(e) {
|
||||
var me = this;
|
||||
|
||||
if (me.getLocks().drag === me) {
|
||||
me.onGesture(e);
|
||||
me.unlockEvents('drag');
|
||||
|
||||
me.fireEvent('rotationEnd', me, me.getCurrentRotation());
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
onRotate: function(e) {
|
||||
|
||||
}
|
||||
});
|
22
vendor/touch/src/chart/interactions/RotatePie3D.js
vendored
Normal file
22
vendor/touch/src/chart/interactions/RotatePie3D.js
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* @class Ext.chart.interactions.RotatePie3D
|
||||
* @extends Ext.chart.interactions.Rotate
|
||||
*
|
||||
* A special version of the Rotate interaction used by Pie3D Chart.
|
||||
*/
|
||||
Ext.define('Ext.chart.interactions.RotatePie3D', {
|
||||
|
||||
extend: 'Ext.chart.interactions.Rotate',
|
||||
|
||||
type: 'rotatePie3d',
|
||||
|
||||
alias: 'interaction.rotatePie3d',
|
||||
|
||||
getAngle: function (e) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
xy = chart.element.getXY(),
|
||||
region = chart.getMainRegion();
|
||||
return Math.atan2(e.pageY - xy[1] - region[3] * 0.5, e.pageX - xy[0] - region[2] * 0.5);
|
||||
}
|
||||
});
|
107
vendor/touch/src/chart/label/Callout.js
vendored
Normal file
107
vendor/touch/src/chart/label/Callout.js
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* @class Ext.chart.label.Callout
|
||||
* @extends Ext.draw.modifier.Modifier
|
||||
*
|
||||
* This is a modifier to place labels and callouts by additional attributes.
|
||||
*/
|
||||
Ext.define("Ext.chart.label.Callout", {
|
||||
extend: 'Ext.draw.modifier.Modifier',
|
||||
|
||||
prepareAttributes: function (attr) {
|
||||
if (!attr.hasOwnProperty('calloutOriginal')) {
|
||||
attr.calloutOriginal = Ext.Object.chain(attr);
|
||||
}
|
||||
if (this._previous) {
|
||||
this._previous.prepareAttributes(attr.calloutOriginal);
|
||||
}
|
||||
},
|
||||
|
||||
setAttrs: function (attr, changes) {
|
||||
var callout = attr.callout,
|
||||
origin = attr.calloutOriginal,
|
||||
bbox = attr.bbox.plain,
|
||||
width = (bbox.width || 0) + attr.labelOverflowPadding,
|
||||
height = (bbox.height || 0) + attr.labelOverflowPadding,
|
||||
dx, dy;
|
||||
|
||||
if ('callout' in changes) {
|
||||
callout = changes.callout;
|
||||
}
|
||||
|
||||
if ('callout' in changes || 'calloutPlaceX' in changes || 'calloutPlaceY' in changes || 'x' in changes || 'y' in changes) {
|
||||
var rotationRads = 'rotationRads' in changes ? origin.rotationRads = changes.rotationRads : origin.rotationRads,
|
||||
x = 'x' in changes ? (origin.x = changes.x) : origin.x,
|
||||
y = 'y' in changes ? (origin.y = changes.y) : origin.y,
|
||||
calloutPlaceX = 'calloutPlaceX' in changes ? changes.calloutPlaceX : attr.calloutPlaceX,
|
||||
calloutPlaceY = 'calloutPlaceY' in changes ? changes.calloutPlaceY : attr.calloutPlaceY,
|
||||
calloutVertical = 'calloutVertical' in changes ? changes.calloutVertical : attr.calloutVertical,
|
||||
temp;
|
||||
|
||||
// Normalize Rotations
|
||||
rotationRads %= Math.PI * 2;
|
||||
if (Math.cos(rotationRads) < 0) {
|
||||
rotationRads = (rotationRads + Math.PI) % (Math.PI * 2);
|
||||
}
|
||||
|
||||
if (rotationRads > Math.PI) {
|
||||
rotationRads -= Math.PI * 2;
|
||||
}
|
||||
|
||||
if (calloutVertical) {
|
||||
rotationRads = rotationRads * (1 - callout) - Math.PI / 2 * callout;
|
||||
temp = width;
|
||||
width = height;
|
||||
height = temp;
|
||||
} else {
|
||||
rotationRads = rotationRads * (1 - callout);
|
||||
}
|
||||
changes.rotationRads = rotationRads;
|
||||
|
||||
|
||||
// Placing label.
|
||||
changes.x = x * (1 - callout) + calloutPlaceX * callout;
|
||||
changes.y = y * (1 - callout) + calloutPlaceY * callout;
|
||||
|
||||
|
||||
// Placing the end of the callout line.
|
||||
dx = calloutPlaceX - x;
|
||||
dy = calloutPlaceY - y;
|
||||
if (Math.abs(dy * width) > Math.abs(height * dx)) {
|
||||
// on top/bottom
|
||||
if (dy > 0) {
|
||||
changes.calloutEndX = changes.x - (height / (dy * 2) * dx) * callout;
|
||||
changes.calloutEndY = changes.y - height / 2 * callout;
|
||||
} else {
|
||||
changes.calloutEndX = changes.x + (height / (dy * 2) * dx) * callout;
|
||||
changes.calloutEndY = changes.y + height / 2 * callout;
|
||||
}
|
||||
} else {
|
||||
// on left/right
|
||||
if (dx > 0) {
|
||||
changes.calloutEndX = changes.x - width / 2;
|
||||
changes.calloutEndY = changes.y - (width / (dx * 2) * dy) * callout;
|
||||
} else {
|
||||
changes.calloutEndX = changes.x + width / 2;
|
||||
changes.calloutEndY = changes.y + (width / (dx * 2) * dy) * callout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changes;
|
||||
},
|
||||
|
||||
pushDown: function (attr, changes) {
|
||||
changes = Ext.draw.modifier.Modifier.prototype.pushDown.call(this, attr.calloutOriginal, changes);
|
||||
return this.setAttrs(attr, changes);
|
||||
},
|
||||
|
||||
popUp: function (attr, changes) {
|
||||
attr = Object.getPrototypeOf(attr);
|
||||
changes = this.setAttrs(attr, changes);
|
||||
if (this._next) {
|
||||
return this._next.popUp(attr, changes);
|
||||
} else {
|
||||
return Ext.apply(attr, changes);
|
||||
}
|
||||
}
|
||||
});
|
105
vendor/touch/src/chart/label/Label.js
vendored
Normal file
105
vendor/touch/src/chart/label/Label.js
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* @class Ext.chart.label.Label
|
||||
* @extends Ext.draw.sprite.Text
|
||||
*
|
||||
* Sprite used to represent labels in series.
|
||||
*/
|
||||
Ext.define('Ext.chart.label.Label', {
|
||||
extend: 'Ext.draw.sprite.Text',
|
||||
requires: ['Ext.chart.label.Callout'],
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
callout: 'limited01',
|
||||
calloutPlaceX: 'number',
|
||||
calloutPlaceY: 'number',
|
||||
calloutStartX: 'number',
|
||||
calloutStartY: 'number',
|
||||
calloutEndX: 'number',
|
||||
calloutEndY: 'number',
|
||||
calloutColor: 'color',
|
||||
calloutVertical: 'bool',
|
||||
labelOverflowPadding: 'number',
|
||||
display: 'enums(none,under,over,rotate,insideStart,insideEnd,outside)',
|
||||
orientation: 'enums(horizontal,vertical)',
|
||||
renderer: 'default'
|
||||
},
|
||||
defaults: {
|
||||
callout: 0,
|
||||
calloutPlaceX: 0,
|
||||
calloutPlaceY: 0,
|
||||
calloutStartX: 0,
|
||||
calloutStartY: 0,
|
||||
calloutEndX: 0,
|
||||
calloutEndY: 0,
|
||||
calloutVertical: false,
|
||||
calloutColor: 'black',
|
||||
labelOverflowPadding: 5,
|
||||
display: 'none',
|
||||
orientation: '',
|
||||
renderer: null
|
||||
},
|
||||
|
||||
dirtyTriggers: {
|
||||
callout: 'transform',
|
||||
calloutPlaceX: 'transform',
|
||||
calloutPlaceY: 'transform',
|
||||
labelOverflowPadding: 'transform',
|
||||
calloutRotation: 'transform',
|
||||
display: 'hidden'
|
||||
},
|
||||
|
||||
updaters: {
|
||||
hidden: function (attrs) {
|
||||
attrs.hidden = attrs.display === 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Object} fx Animation configuration.
|
||||
*/
|
||||
fx: {
|
||||
customDuration: {
|
||||
callout: 200
|
||||
}
|
||||
},
|
||||
field: null
|
||||
},
|
||||
|
||||
prepareModifiers: function () {
|
||||
this.callSuper(arguments);
|
||||
this.calloutModifier = new Ext.chart.label.Callout({sprite: this});
|
||||
this.fx.setNext(this.calloutModifier);
|
||||
this.calloutModifier.setNext(this.topModifier);
|
||||
},
|
||||
|
||||
render: function (surface, ctx, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr;
|
||||
ctx.save();
|
||||
ctx.globalAlpha *= Math.max(0, attr.callout - 0.5) * 2;
|
||||
if (ctx.globalAlpha > 0) {
|
||||
ctx.strokeStyle = attr.calloutColor;
|
||||
ctx.fillStyle = attr.calloutColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(me.attr.calloutStartX, me.attr.calloutStartY);
|
||||
ctx.lineTo(me.attr.calloutEndX, me.attr.calloutEndY);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(me.attr.calloutStartX, me.attr.calloutStartY, 1, 0, 2 * Math.PI, true);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(me.attr.calloutEndX, me.attr.calloutEndY, 1, 0, 2 * Math.PI, true);
|
||||
ctx.fill();
|
||||
}
|
||||
ctx.restore();
|
||||
|
||||
Ext.draw.sprite.Text.prototype.render.apply(me, arguments);
|
||||
}
|
||||
});
|
61
vendor/touch/src/chart/series/Area.js
vendored
Normal file
61
vendor/touch/src/chart/series/Area.js
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Area
|
||||
* @extends Ext.chart.series.StackedCartesian
|
||||
*
|
||||
* Creates an Area Chart.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'area',
|
||||
* subStyle: {
|
||||
* fill: ['blue', 'green', 'red']
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: ['data1', 'data2', 'data3']
|
||||
*
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Area', {
|
||||
|
||||
extend: 'Ext.chart.series.StackedCartesian',
|
||||
|
||||
alias: 'series.area',
|
||||
type: 'area',
|
||||
seriesType: 'areaSeries',
|
||||
|
||||
requires: ['Ext.chart.series.sprite.Area']
|
||||
});
|
134
vendor/touch/src/chart/series/Bar.js
vendored
Normal file
134
vendor/touch/src/chart/series/Bar.js
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Bar
|
||||
* @extends Ext.chart.series.StackedCartesian
|
||||
*
|
||||
* Creates a Bar Chart.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.Chart({
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* fields: 'data1'
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* fields: 'name'
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'bar',
|
||||
* xField: 'name',
|
||||
* yField: 'data1',
|
||||
* style: {
|
||||
* fill: 'blue'
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Bar', {
|
||||
|
||||
extend: 'Ext.chart.series.StackedCartesian',
|
||||
|
||||
alias: 'series.bar',
|
||||
type: 'bar',
|
||||
seriesType: 'barSeries',
|
||||
|
||||
requires: [
|
||||
'Ext.chart.series.sprite.Bar',
|
||||
'Ext.draw.sprite.Rect'
|
||||
],
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} itemInstancing Sprite template used for series.
|
||||
*/
|
||||
itemInstancing: {
|
||||
type: 'rect',
|
||||
fx: {
|
||||
customDuration: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getItemForPoint: function (x, y) {
|
||||
if (this.getSprites()) {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
padding = chart.getInnerPadding();
|
||||
|
||||
// Convert the coordinates because the "items" sprites that draw the bars ignore the chart's InnerPadding.
|
||||
// See also Ext.chart.series.sprite.Bar.getItemForPoint(x,y) regarding the series's vertical coordinate system.
|
||||
//
|
||||
// TODO: Cleanup the bar sprites.
|
||||
arguments[0] = x - padding.left;
|
||||
arguments[1] = y + padding.bottom;
|
||||
return me.callParent(arguments);
|
||||
}
|
||||
},
|
||||
|
||||
updateXAxis: function (axis) {
|
||||
axis.setLabelInSpan(true);
|
||||
this.callSuper(arguments);
|
||||
},
|
||||
|
||||
updateHidden: function (hidden) {
|
||||
this.callParent(arguments);
|
||||
this.updateStacked();
|
||||
},
|
||||
|
||||
updateStacked: function (stacked) {
|
||||
var sprites = this.getSprites(),
|
||||
ln = sprites.length,
|
||||
visible = [],
|
||||
attrs = {}, i;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (!sprites[i].attr.hidden) {
|
||||
visible.push(sprites[i]);
|
||||
}
|
||||
}
|
||||
ln = visible.length;
|
||||
|
||||
if (this.getStacked()) {
|
||||
attrs.groupCount = 1;
|
||||
attrs.groupOffset = 0;
|
||||
for (i = 0; i < ln; i++) {
|
||||
visible[i].setAttributes(attrs);
|
||||
}
|
||||
} else {
|
||||
attrs.groupCount = visible.length;
|
||||
for (i = 0; i < ln; i++) {
|
||||
attrs.groupOffset = i;
|
||||
visible[i].setAttributes(attrs);
|
||||
}
|
||||
}
|
||||
this.callSuper(arguments);
|
||||
}
|
||||
});
|
100
vendor/touch/src/chart/series/CandleStick.js
vendored
Normal file
100
vendor/touch/src/chart/series/CandleStick.js
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* @class Ext.chart.series.CandleStick
|
||||
* @extends Ext.chart.series.Cartesian
|
||||
*
|
||||
* Creates a candlestick or OHLC Chart.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['time', 'open', 'high', 'low', 'close'],
|
||||
* data: [
|
||||
* {'time':new Date('Jan 1 2010').getTime(), 'open':600, 'high':614, 'low':578, 'close':590},
|
||||
* {'time':new Date('Jan 2 2010').getTime(), 'open':590, 'high':609, 'low':580, 'close':580},
|
||||
* {'time':new Date('Jan 3 2010').getTime(), 'open':580, 'high':602, 'low':578, 'close':602},
|
||||
* {'time':new Date('Jan 4 2010').getTime(), 'open':602, 'high':614, 'low':586, 'close':586},
|
||||
* {'time':new Date('Jan 5 2010').getTime(), 'open':586, 'high':602, 'low':565, 'close':565}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['open', 'high', 'low', 'close'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 560,
|
||||
* maximum: 640
|
||||
* }, {
|
||||
* type: 'time',
|
||||
* position: 'bottom',
|
||||
* fields: ['time'],
|
||||
* fromDate: new Date('Dec 31 2009'),
|
||||
* toDate: new Date('Jan 6 2010'),
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* style: {
|
||||
* axisLine: false
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'candlestick',
|
||||
* xField: 'time',
|
||||
* openField: 'open',
|
||||
* highField: 'high',
|
||||
* lowField: 'low',
|
||||
* closeField: 'close',
|
||||
* style: {
|
||||
* dropStyle: {
|
||||
* fill: 'rgb(237, 123, 43)',
|
||||
* stroke: 'rgb(237, 123, 43)'
|
||||
* },
|
||||
* raiseStyle: {
|
||||
* fill: 'rgb(55, 153, 19)',
|
||||
* stroke: 'rgb(55, 153, 19)'
|
||||
* }
|
||||
* },
|
||||
* aggregator: {
|
||||
* strategy: 'time'
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.series.CandleStick', {
|
||||
extend: 'Ext.chart.series.Cartesian',
|
||||
requires: ['Ext.chart.series.sprite.CandleStick'],
|
||||
alias: 'series.candlestick',
|
||||
type: 'candlestick',
|
||||
seriesType: 'candlestickSeries',
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} openField
|
||||
* The store record field name that represents the opening value of the given period.
|
||||
*/
|
||||
openField: null,
|
||||
/**
|
||||
* @cfg {String} highField
|
||||
* The store record field name that represents the highest value of the time interval represented.
|
||||
*/
|
||||
highField: null,
|
||||
/**
|
||||
* @cfg {String} lowField
|
||||
* The store record field name that represents the lowest value of the time interval represented.
|
||||
*/
|
||||
lowField: null,
|
||||
/**
|
||||
* @cfg {String} closeField
|
||||
* The store record field name that represents the closing value of the given period.
|
||||
*/
|
||||
closeField: null
|
||||
},
|
||||
|
||||
fieldCategoryY: ['Open', 'High', 'Low', 'Close']
|
||||
});
|
145
vendor/touch/src/chart/series/Cartesian.js
vendored
Normal file
145
vendor/touch/src/chart/series/Cartesian.js
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* @abstract
|
||||
* @class Ext.chart.series.Cartesian
|
||||
* @extends Ext.chart.series.Series
|
||||
*
|
||||
* Common base class for series implementations which plot values using x/y coordinates.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Cartesian', {
|
||||
extend: 'Ext.chart.series.Series',
|
||||
config: {
|
||||
/**
|
||||
* The field used to access the x axis value from the items from the data source.
|
||||
*
|
||||
* @cfg {String} xField
|
||||
*/
|
||||
xField: null,
|
||||
|
||||
/**
|
||||
* The field(s) used to access the y-axis value(s) of the items from the data source.
|
||||
*
|
||||
* @cfg {String|String[]} yField
|
||||
*/
|
||||
yField: null,
|
||||
|
||||
/**
|
||||
* @cfg {Ext.chart.axis.Axis} xAxis The chart axis bound to the series on the x-axis.
|
||||
*/
|
||||
xAxis: null,
|
||||
|
||||
/**
|
||||
* @cfg {Ext.chart.axis.Axis} yAxis The chart axis bound to the series on the y-axis.
|
||||
*/
|
||||
yAxis: null
|
||||
},
|
||||
|
||||
directions: ['X', 'Y'],
|
||||
fieldCategoryX: ['X'],
|
||||
fieldCategoryY: ['Y'],
|
||||
|
||||
updateXAxis: function (axis) {
|
||||
axis.processData(this);
|
||||
},
|
||||
|
||||
updateYAxis: function (axis) {
|
||||
axis.processData(this);
|
||||
},
|
||||
|
||||
coordinateX: function () {
|
||||
return this.coordinate('X', 0, 2);
|
||||
},
|
||||
|
||||
coordinateY: function () {
|
||||
return this.coordinate('Y', 1, 2);
|
||||
},
|
||||
|
||||
getItemForPoint: function (x, y) {
|
||||
if (this.getSprites()) {
|
||||
var me = this,
|
||||
sprite = me.getSprites()[0],
|
||||
store = me.getStore(),
|
||||
item;
|
||||
|
||||
if(me.getHidden()) {
|
||||
return null;
|
||||
}
|
||||
if (sprite) {
|
||||
var index = sprite.getIndexNearPoint(x, y);
|
||||
if (index !== -1) {
|
||||
item = {
|
||||
series: this,
|
||||
category: this.getItemInstancing() ? 'items' : 'markers',
|
||||
index: index,
|
||||
record: store.getData().items[index],
|
||||
field: this.getYField(),
|
||||
sprite: sprite
|
||||
};
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
createSprite: function () {
|
||||
var sprite = this.callSuper(),
|
||||
xAxis = this.getXAxis();
|
||||
sprite.setAttributes({flipXY: this.getChart().getFlipXY()});
|
||||
if (sprite.setAggregator && xAxis && xAxis.getAggregator) {
|
||||
if (xAxis.getAggregator) {
|
||||
sprite.setAggregator({strategy: xAxis.getAggregator()});
|
||||
} else {
|
||||
sprite.setAggregator({});
|
||||
}
|
||||
}
|
||||
return sprite;
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
var me = this,
|
||||
chart = this.getChart(),
|
||||
animation = chart && chart.getAnimate(),
|
||||
itemInstancing = me.getItemInstancing(),
|
||||
sprites = me.sprites, sprite;
|
||||
|
||||
if (!chart) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!sprites.length) {
|
||||
sprite = me.createSprite();
|
||||
} else {
|
||||
sprite = sprites[0];
|
||||
}
|
||||
|
||||
if (animation) {
|
||||
me.getLabel().getTemplate().fx.setConfig(animation);
|
||||
if (itemInstancing) {
|
||||
sprite.itemsMarker.getTemplate().fx.setConfig(animation);
|
||||
}
|
||||
sprite.fx.setConfig(animation);
|
||||
}
|
||||
return sprites;
|
||||
},
|
||||
|
||||
provideLegendInfo: function (target) {
|
||||
var style = this.getStyle();
|
||||
target.push({
|
||||
name: this.getTitle() || this.getYField() || this.getId(),
|
||||
mark: style.fillStyle || style.strokeStyle || 'black',
|
||||
disabled: false,
|
||||
series: this.getId(),
|
||||
index: 0
|
||||
});
|
||||
},
|
||||
|
||||
getXRange: function () {
|
||||
return [this.dataRange[0], this.dataRange[2]];
|
||||
},
|
||||
|
||||
getYRange: function () {
|
||||
return [this.dataRange[1], this.dataRange[3]];
|
||||
}
|
||||
})
|
||||
;
|
467
vendor/touch/src/chart/series/Gauge.js
vendored
Normal file
467
vendor/touch/src/chart/series/Gauge.js
vendored
Normal file
|
@ -0,0 +1,467 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Gauge
|
||||
* @extends Ext.chart.series.Series
|
||||
*
|
||||
* Creates a Gauge Chart.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.SpaceFillingChart({
|
||||
* series: [{
|
||||
* type: 'gauge',
|
||||
* minimum: 100,
|
||||
* maximum: 800,
|
||||
* value: 400,
|
||||
* donut: 30,
|
||||
* colors: ["#115fa6", "lightgrey"]
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Gauge', {
|
||||
alias: 'series.gauge',
|
||||
extend: 'Ext.chart.series.Series',
|
||||
type: "gauge",
|
||||
seriesType: 'pieslice',
|
||||
|
||||
requires: [
|
||||
'Ext.draw.sprite.Sector'
|
||||
],
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} angleField
|
||||
* @deprecated Use `field` directly
|
||||
* The store record field name to be used for the gauge angles.
|
||||
* The values bound to this field name must be positive real numbers.
|
||||
*/
|
||||
angleField: null,
|
||||
|
||||
/**
|
||||
* @cfg {String} field
|
||||
* The store record field name to be used for the gauge value.
|
||||
* The values bound to this field name must be positive real numbers.
|
||||
*/
|
||||
field: null,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} needle
|
||||
* If true, display the gauge as a needle, otherwise as a sector.
|
||||
*/
|
||||
needle: false,
|
||||
|
||||
/**
|
||||
* @cfg {Number} needleLengthRatio
|
||||
* @deprecated Use `needleLength` directly
|
||||
* Ratio of the length of needle compared to the radius of the entire disk.
|
||||
*/
|
||||
needleLengthRatio: undefined,
|
||||
|
||||
/**
|
||||
* @cfg {Number} needleLength
|
||||
* Percentage of the length of needle compared to the radius of the entire disk.
|
||||
*/
|
||||
needleLength: 90,
|
||||
|
||||
/**
|
||||
* @cfg {Number} needleWidth
|
||||
* Width of the needle in pixels.
|
||||
*/
|
||||
needleWidth: 4,
|
||||
|
||||
/**
|
||||
* @cfg {Number} donut
|
||||
* Percentage of the radius of the donut hole compared to the entire disk.
|
||||
*/
|
||||
donut: 30,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} showInLegend
|
||||
* Whether to add the gauge chart elements as legend items.
|
||||
*/
|
||||
showInLegend: false,
|
||||
|
||||
/**
|
||||
* @cfg {Number} value
|
||||
* Directly sets the displayed value of the gauge.
|
||||
* It is ignored if {@link #field} is provided.
|
||||
*/
|
||||
value: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array} colors (required)
|
||||
* An array of color values which is used for the needle and the `sectors`.
|
||||
*/
|
||||
colors: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array} sectors
|
||||
* Allows to paint sectors of different colors in the background of the gauge,
|
||||
* with optional labels.
|
||||
*
|
||||
* It can be an array of numbers (each between `minimum` and `maximum`) that
|
||||
* define the highest value of each sector. For N sectors, only (N-1) values are
|
||||
* needed because it is assumed that the first sector starts at `minimum` and the
|
||||
* last sector ends at `maximum`. Example: a water temperature gauge that is blue
|
||||
* below 20C, red above 80C, gray in-between, and with an orange needle...
|
||||
*
|
||||
* minimum: 0,
|
||||
* maximum: 100,
|
||||
* sectors: [20, 80],
|
||||
* colors: ['orange', 'blue', 'lightgray', 'red']
|
||||
*
|
||||
* It can be also an array of objects, each with the following properties:
|
||||
*
|
||||
* @cfg {Number} sectors.start The starting value of the sector. If omitted, it
|
||||
* uses the previous sector's `end` value or the chart's `minimum`.
|
||||
* @cfg {Number} sectors.end The ending value of the sector. If omitted, it uses
|
||||
* the `maximum` defined for the chart.
|
||||
* @cfg {String} sectors.label The label for this sector. Labels are styled using
|
||||
* the series' {@link Ext.chart.series.Series#label label} config.
|
||||
* @cfg {String} sectors.color The color of the sector. If omitted, it uses one
|
||||
* of the `colors` defined for the series or for the chart.
|
||||
* @cfg {Object} sectors.style An additional style object for the sector (for
|
||||
* instance to set the opacity or to draw a line of a different color around the
|
||||
* sector).
|
||||
*
|
||||
* minimum: 0,
|
||||
* maximum: 100,
|
||||
* sectors: [{
|
||||
* end: 20,
|
||||
* label: 'Cold',
|
||||
* color: 'aqua'
|
||||
* },
|
||||
* {
|
||||
* end: 80,
|
||||
* label: 'Temp.',
|
||||
* color: 'lightgray',
|
||||
* style: { strokeStyle:'black', strokeOpacity:1, lineWidth:1 }
|
||||
* },
|
||||
* {
|
||||
* label: 'Hot',
|
||||
* color: 'tomato'
|
||||
* }]
|
||||
*/
|
||||
sectors: null,
|
||||
|
||||
/**
|
||||
* @cfg {Number} minimum
|
||||
* The minimum value of the gauge.
|
||||
*/
|
||||
minimum: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Number} maximum
|
||||
* The maximum value of the gauge.
|
||||
*/
|
||||
maximum: 100,
|
||||
|
||||
rotation: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Number} totalAngle
|
||||
* The size of the sector that the series will occupy.
|
||||
*/
|
||||
totalAngle: Math.PI / 2,
|
||||
|
||||
region: [0, 0, 1, 1],
|
||||
|
||||
center: [0.5, 0.75],
|
||||
|
||||
radius: 0.5,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} wholeDisk Indicates whether to show the whole disk or only the marked part.
|
||||
*/
|
||||
wholeDisk: false
|
||||
},
|
||||
|
||||
updateNeedle: function(needle) {
|
||||
var me = this,
|
||||
sprites = me.getSprites(),
|
||||
angle = me.valueToAngle(me.getValue());
|
||||
|
||||
if (sprites && sprites.length) {
|
||||
sprites[0].setAttributes({
|
||||
startAngle: (needle ? angle : 0),
|
||||
endAngle: angle,
|
||||
strokeOpacity: (needle ? 1 : 0),
|
||||
lineWidth: (needle ? me.getNeedleWidth() : 0)
|
||||
|
||||
});
|
||||
me.doUpdateStyles();
|
||||
}
|
||||
},
|
||||
|
||||
updateColors: function (colors, oldColors) {
|
||||
var me = this,
|
||||
sectors = me.getSectors(),
|
||||
sectorCount = sectors && sectors.length,
|
||||
sprites = me.getSprites(),
|
||||
spriteCount = sprites && sprites.length,
|
||||
newColors = Ext.Array.clone(colors),
|
||||
colorCount = colors && colors.length,
|
||||
needle = me.getNeedle(),
|
||||
i;
|
||||
|
||||
if (!colorCount || !colors[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the 'sectors' colors are not overridden.
|
||||
for (i = 0; i < sectorCount; i++) {
|
||||
newColors[i+1] = sectors[i].color || newColors[i+1] || colors[i%colorCount];
|
||||
}
|
||||
|
||||
sprites[0].setAttributes({stroke:newColors[0]});
|
||||
this.setSubStyle({color:newColors});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateAngleField: function (angleField) {
|
||||
this.setField(angleField);
|
||||
},
|
||||
|
||||
updateNeedleLengthRatio: function (needleLengthRatio) {
|
||||
this.setNeedleLength(needleLengthRatio * 100);
|
||||
},
|
||||
|
||||
updateRegion: function (region) {
|
||||
var wholeDisk = this.getWholeDisk(),
|
||||
halfTotalAngle = wholeDisk ? Math.PI : this.getTotalAngle() / 2,
|
||||
donut = this.getDonut() / 100,
|
||||
width, height, radius;
|
||||
if (halfTotalAngle <= Math.PI / 2) {
|
||||
width = 2 * Math.sin(halfTotalAngle);
|
||||
height = 1 - donut * Math.cos(halfTotalAngle);
|
||||
} else {
|
||||
width = 2;
|
||||
height = 1 - Math.cos(halfTotalAngle);
|
||||
}
|
||||
|
||||
radius = Math.min(region[2] / width, region[3] / height);
|
||||
this.setRadius(radius);
|
||||
this.setCenter([region[2] / 2, radius + (region[3] - height * radius) / 2]);
|
||||
},
|
||||
|
||||
updateCenter: function (center) {
|
||||
this.setStyle({
|
||||
centerX: center[0],
|
||||
centerY: center[1],
|
||||
rotationCenterX: center[0],
|
||||
rotationCenterY: center[1]
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
this.setStyle({
|
||||
rotationRads: rotation - (this.getTotalAngle() + Math.PI) / 2
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
doUpdateShape: function (radius, donut) {
|
||||
var endRhoArray,
|
||||
sectors = this.getSectors(),
|
||||
sectorCount = (sectors && sectors.length) || 0,
|
||||
needleLength = this.getNeedleLength() / 100;
|
||||
|
||||
// Initialize an array that contains the endRho for each sprite.
|
||||
// The first sprite is for the needle, the others for the gauge background sectors.
|
||||
// Note: SubStyle arrays are handled in series.getOverriddenStyleByIndex().
|
||||
endRhoArray = [radius * needleLength, radius];
|
||||
while (sectorCount --) {
|
||||
endRhoArray.push(radius);
|
||||
}
|
||||
|
||||
this.setSubStyle({
|
||||
endRho: endRhoArray,
|
||||
startRho: radius / 100 * donut
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRadius: function (radius) {
|
||||
var donut = this.getDonut();
|
||||
this.doUpdateShape(radius, donut);
|
||||
},
|
||||
|
||||
updateDonut: function (donut) {
|
||||
var radius = this.getRadius();
|
||||
this.doUpdateShape(radius, donut);
|
||||
},
|
||||
|
||||
valueToAngle: function(value) {
|
||||
value = this.applyValue(value);
|
||||
return this.getTotalAngle() * (value - this.getMinimum()) / (this.getMaximum() - this.getMinimum());
|
||||
},
|
||||
|
||||
applyValue: function (value) {
|
||||
return Math.min(this.getMaximum(), Math.max(value, this.getMinimum()));
|
||||
},
|
||||
|
||||
updateValue: function (value) {
|
||||
var me = this,
|
||||
needle = me.getNeedle(),
|
||||
angle = me.valueToAngle(value),
|
||||
sprites = me.getSprites();
|
||||
|
||||
sprites[0].rendererData.value = value;
|
||||
sprites[0].setAttributes({
|
||||
startAngle: (needle ? angle : 0),
|
||||
endAngle: angle
|
||||
});
|
||||
me.doUpdateStyles();
|
||||
},
|
||||
|
||||
processData: function () {
|
||||
var store = this.getStore();
|
||||
if (!store) {
|
||||
return;
|
||||
}
|
||||
var field = this.getField();
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
if (!store.getData().items.length) {
|
||||
return;
|
||||
}
|
||||
this.setValue(store.getData().items[0].get(field));
|
||||
},
|
||||
|
||||
getDefaultSpriteConfig: function () {
|
||||
return {
|
||||
type: this.seriesType,
|
||||
renderer: this.getRenderer(),
|
||||
fx: {
|
||||
customDuration: {
|
||||
translationX: 0,
|
||||
translationY: 0,
|
||||
rotationCenterX: 0,
|
||||
rotationCenterY: 0,
|
||||
centerX: 0,
|
||||
centerY: 0,
|
||||
startRho: 0,
|
||||
endRho: 0,
|
||||
baseRotation: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
normalizeSectors: function(sectors) {
|
||||
// Make sure all the sectors in the array have a legit start and end.
|
||||
// Note: the array is modified in-place.
|
||||
var me = this,
|
||||
sectorCount = (sectors && sectors.length) || 0,
|
||||
i, value, start, end;
|
||||
|
||||
if (sectorCount) {
|
||||
for (i = 0; i < sectorCount; i++) {
|
||||
value = sectors[i];
|
||||
if (typeof value == "number") {
|
||||
sectors[i] = {
|
||||
start: (i > 0 ? sectors[i-1].end : me.getMinimum()),
|
||||
end: Math.min(value, me.getMaximum())
|
||||
};
|
||||
if (i == (sectorCount - 1) && sectors[i].end < me.getMaximum()) {
|
||||
sectors[i+1] = {
|
||||
start: sectors[i].end,
|
||||
end: me.getMaximum()
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (typeof value.start == "number") {
|
||||
start = Math.max(value.start, me.getMinimum());
|
||||
} else {
|
||||
start = (i > 0 ? sectors[i-1].end : me.getMinimum());
|
||||
}
|
||||
if (typeof value.end == "number") {
|
||||
end = Math.min(value.end, me.getMaximum());
|
||||
} else {
|
||||
end = me.getMaximum();
|
||||
}
|
||||
sectors[i].start = start;
|
||||
sectors[i].end = end;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sectors = [{
|
||||
start: me.getMinimum(),
|
||||
end: me.getMaximum()
|
||||
}];
|
||||
}
|
||||
return sectors;
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
var me = this,
|
||||
store = me.getStore(),
|
||||
value = me.getValue(),
|
||||
i, ln;
|
||||
|
||||
// The store must be initialized, or the value must be set
|
||||
if (!store && !Ext.isNumber(value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Return cached sprites
|
||||
var chart = me.getChart(),
|
||||
animate = chart.getAnimate(),
|
||||
sprites = me.sprites,
|
||||
spriteIndex = 0,
|
||||
sprite, sectors, attr, rendererData;
|
||||
|
||||
if (sprites && sprites.length) {
|
||||
sprites[0].fx.setConfig(animate);
|
||||
return sprites;
|
||||
}
|
||||
|
||||
rendererData = {
|
||||
store: store,
|
||||
field: me.getField(),
|
||||
value: value,
|
||||
series: me
|
||||
};
|
||||
|
||||
// Create needle sprite
|
||||
sprite = me.createSprite();
|
||||
sprite.setAttributes({
|
||||
zIndex: 10
|
||||
}, true);
|
||||
sprite.rendererData = rendererData;
|
||||
sprite.rendererIndex = spriteIndex++;
|
||||
|
||||
// Create background sprite(s)
|
||||
me.getLabel().getTemplate().setField(true); // Enable labels
|
||||
sectors = me.normalizeSectors(me.getSectors());
|
||||
for (i = 0, ln = sectors.length; i < ln; i++) {
|
||||
attr = {
|
||||
startAngle: me.valueToAngle(sectors[i].start),
|
||||
endAngle: me.valueToAngle(sectors[i].end),
|
||||
label: sectors[i].label,
|
||||
fillStyle: sectors[i].color,
|
||||
strokeOpacity: 0,
|
||||
rotateLabels: false,
|
||||
doCallout: false, // Show labels inside sectors.
|
||||
labelOverflowPadding: -1 // Allow labels to overlap.
|
||||
};
|
||||
Ext.apply(attr, sectors[i].style);
|
||||
sprite = me.createSprite();
|
||||
sprite.rendererData = rendererData;
|
||||
sprite.rendererIndex = spriteIndex++;
|
||||
sprite.setAttributes(attr, true);
|
||||
}
|
||||
|
||||
// Make sure we have some default colors
|
||||
var colors = me.getColors() || (chart && chart.config.colors);
|
||||
if (!colors) {
|
||||
me.setColors(['blue','lightgray']);
|
||||
}
|
||||
|
||||
me.doUpdateStyles();
|
||||
return sprites;
|
||||
}
|
||||
});
|
||||
|
337
vendor/touch/src/chart/series/ItemPublisher.js
vendored
Normal file
337
vendor/touch/src/chart/series/ItemPublisher.js
vendored
Normal file
|
@ -0,0 +1,337 @@
|
|||
/**
|
||||
* @private
|
||||
*/
|
||||
Ext.define('Ext.chart.series.ItemPublisher', {
|
||||
extend: 'Ext.event.publisher.Publisher',
|
||||
|
||||
targetType: 'series',
|
||||
|
||||
handledEvents: [
|
||||
/**
|
||||
* @event itemmousemove
|
||||
* Fires when the mouse is moved on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemmousemove',
|
||||
/**
|
||||
* @event itemmouseup
|
||||
* Fires when a mouseup event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemmouseup',
|
||||
/**
|
||||
* @event itemmousedown
|
||||
* Fires when a mousedown event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemmousedown',
|
||||
/**
|
||||
* @event itemmouseover
|
||||
* Fires when the mouse enters a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemmouseover',
|
||||
/**
|
||||
* @event itemmouseout
|
||||
* Fires when the mouse exits a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemmouseout',
|
||||
/**
|
||||
* @event itemclick
|
||||
* Fires when a click event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemclick',
|
||||
/**
|
||||
* @event itemdoubleclick
|
||||
* Fires when a doubleclick event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemdoubleclick',
|
||||
/**
|
||||
* @event itemtap
|
||||
* Fires when a tap event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtap',
|
||||
/**
|
||||
* @event itemtapstart
|
||||
* Fires when a tapstart event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtapstart',
|
||||
/**
|
||||
* @event itemtapend
|
||||
* Fires when a tapend event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtapend',
|
||||
/**
|
||||
* @event itemtapcancel
|
||||
* Fires when a tapcancel event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtapcancel',
|
||||
/**
|
||||
* @event itemtaphold
|
||||
* Fires when a taphold event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtaphold',
|
||||
/**
|
||||
* @event itemdoubletap
|
||||
* Fires when a doubletap event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemdoubletap',
|
||||
/**
|
||||
* @event itemsingletap
|
||||
* Fires when a singletap event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemsingletap',
|
||||
/**
|
||||
* @event itemtouchstart
|
||||
* Fires when a touchstart event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtouchstart',
|
||||
/**
|
||||
* @event itemtouchmove
|
||||
* Fires when a touchmove event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtouchmove',
|
||||
/**
|
||||
* @event itemtouchend
|
||||
* Fires when a touchend event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemtouchend',
|
||||
/**
|
||||
* @event itemdragstart
|
||||
* Fires when a dragstart event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemdragstart',
|
||||
/**
|
||||
* @event itemdrag
|
||||
* Fires when a drag event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemdrag',
|
||||
/**
|
||||
* @event itemdragend
|
||||
* Fires when a dragend event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemdragend',
|
||||
/**
|
||||
* @event itempinchstart
|
||||
* Fires when a pinchstart event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itempinchstart',
|
||||
/**
|
||||
* @event itempinch
|
||||
* Fires when a pinch event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itempinch',
|
||||
/**
|
||||
* @event itempinchend
|
||||
* Fires when a pinchend event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itempinchend',
|
||||
/**
|
||||
* @event itemswipe
|
||||
* Fires when a swipe event occurs on a series item.
|
||||
* @param {Ext.chart.series.Series} series
|
||||
* @param {Object} item
|
||||
* @param {Event} event
|
||||
*/
|
||||
'itemswipe'
|
||||
],
|
||||
|
||||
delegationRegex: /^item([a-z]+)$/i,
|
||||
|
||||
getSubscribers: function (chartId) {
|
||||
var subscribers = this.subscribers;
|
||||
|
||||
if (!subscribers.hasOwnProperty(chartId)) {
|
||||
subscribers[chartId] = {};
|
||||
}
|
||||
|
||||
return subscribers[chartId];
|
||||
},
|
||||
|
||||
subscribe: function (target, eventName) {
|
||||
var match = target.match(this.idSelectorRegex),
|
||||
dispatcher = this.dispatcher,
|
||||
targetType = this.targetType,
|
||||
series, id;
|
||||
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
id = match[1];
|
||||
series = Ext.ComponentManager.get(id);
|
||||
if (!series) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!series.getChart()) {
|
||||
dispatcher.addListener(targetType, target, 'chartattached', 'attachChart', this, [series, eventName], 'before');
|
||||
} else {
|
||||
this.attachChart(series.getChart(), [series, eventName]);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
attachChart: function (chart, args) {
|
||||
var dispatcher = this.dispatcher,
|
||||
targetType = this.targetType,
|
||||
series = args[0],
|
||||
eventName = args[1],
|
||||
subscribers = this.getSubscribers(chart.getId()),
|
||||
match = eventName.match(this.delegationRegex);
|
||||
if (match) {
|
||||
var chartEventName = match[1];
|
||||
if (!subscribers.hasOwnProperty(eventName)) {
|
||||
subscribers[eventName] = [];
|
||||
dispatcher.addListener(targetType, '#' + series.getId(), 'chartdetached', 'detachChart', this, [series, eventName, subscribers], 'after');
|
||||
chart.element.on(chartEventName, "relayMethod", this, [chart, eventName]);
|
||||
}
|
||||
subscribers[eventName].push(series);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
unsubscribe: function (target, eventName) {
|
||||
var match = target.match(this.idSelectorRegex),
|
||||
dispatcher = this.dispatcher,
|
||||
targetType = this.targetType,
|
||||
series, id;
|
||||
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
|
||||
id = match[1];
|
||||
series = Ext.ComponentManager.get(id);
|
||||
if (!series) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dispatcher.removeListener(targetType, target, 'chartattached', 'attachChart', this, 'before');
|
||||
if (series.getChart()) {
|
||||
this.detachChart(series.getChart(), [series, eventName]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
detachChart: function (chart, args) {
|
||||
var dispatcher = this.dispatcher,
|
||||
targetType = this.targetType,
|
||||
series = args[0],
|
||||
eventName = args[1],
|
||||
subscribers = this.getSubscribers(chart.getId()),
|
||||
match = eventName.match(this.delegationRegex),
|
||||
index, seriesArray;
|
||||
if (match) {
|
||||
var chartEventName = match[1];
|
||||
if (subscribers.hasOwnProperty(eventName)) {
|
||||
seriesArray = subscribers[eventName];
|
||||
index = seriesArray.indexOf(series);
|
||||
if (index > -1) {
|
||||
seriesArray.splice(index, 1);
|
||||
}
|
||||
if (seriesArray.length === 0) {
|
||||
chart.element.un(chartEventName, "relayMethod", this, [chart, eventName]);
|
||||
dispatcher.removeListener(targetType, '#' + series.getId(), 'chartdetached', 'detachChart', this, 'after');
|
||||
delete subscribers[eventName];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
relayMethod: function (e, sender, args) {
|
||||
var chart = args[0],
|
||||
eventName = args[1],
|
||||
dispatcher = this.dispatcher,
|
||||
targetType = this.targetType,
|
||||
chartXY = chart.getEventXY(e),
|
||||
x = chartXY[0],
|
||||
y = chartXY[1],
|
||||
subscriber = this.getSubscribers(chart.getId())[eventName],
|
||||
i, ln;
|
||||
if (subscriber) {
|
||||
for (i = 0, ln = subscriber.length; i < ln; i++) {
|
||||
var series = subscriber[i],
|
||||
item = series.getItemForPoint(x, y);
|
||||
if (item) {
|
||||
// TODO: Don't stop at the first item.
|
||||
// Depending on the selectionTolerance, there might be an item in another
|
||||
// series that's closer to the event location. See test case 3943c.
|
||||
dispatcher.doDispatchEvent(targetType, '#' + series.getId(), eventName, [series, item, e]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, function () {
|
||||
|
||||
});
|
202
vendor/touch/src/chart/series/Line.js
vendored
Normal file
202
vendor/touch/src/chart/series/Line.js
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Line
|
||||
* @extends Ext.chart.series.Cartesian
|
||||
*
|
||||
* Creates a Line Chart. A Line Chart is a useful visualization technique to display quantitative information for different
|
||||
* categories or other real values (as opposed to the bar chart), that can show some progression (or regression) in the dataset.
|
||||
* As with all other series, the Line Series must be appended in the *series* Chart array configuration. See the Chart
|
||||
* documentation for more information. A typical configuration object for the line series could be:
|
||||
*
|
||||
* @example preview
|
||||
* var lineChart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* style: {
|
||||
* stroke: 'rgb(143,203,203)'
|
||||
* },
|
||||
* xField: 'name',
|
||||
* yField: 'data1',
|
||||
* marker: {
|
||||
* type: 'path',
|
||||
* path: ['M', -2, 0, 0, 2, 2, 0, 0, -2, 'Z'],
|
||||
* stroke: 'blue',
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }, {
|
||||
* type: 'line',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* fill: true,
|
||||
* xField: 'name',
|
||||
* yField: 'data3',
|
||||
* marker: {
|
||||
* type: 'circle',
|
||||
* radius: 4,
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(lineChart);
|
||||
*
|
||||
* In this configuration we're adding two series (or lines), one bound to the `data1`
|
||||
* property of the store and the other to `data3`. The type for both configurations is
|
||||
* `line`. The `xField` for both series is the same, the `name` property of the store.
|
||||
* Both line series share the same axis, the left axis. You can set particular marker
|
||||
* configuration by adding properties onto the markerConfig object. Both series have
|
||||
* an object as highlight so that markers animate smoothly to the properties in highlight
|
||||
* when hovered. The second series has `fill = true` which means that the line will also
|
||||
* have an area below it of the same color.
|
||||
*
|
||||
* **Note:** In the series definition remember to explicitly set the axis to bind the
|
||||
* values of the line series to. This can be done by using the `axis` configuration property.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Line', {
|
||||
extend: 'Ext.chart.series.Cartesian',
|
||||
alias: 'series.line',
|
||||
type: 'line',
|
||||
seriesType: 'lineSeries',
|
||||
|
||||
requires: [
|
||||
'Ext.chart.series.sprite.Line'
|
||||
],
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Number} selectionTolerance
|
||||
* The offset distance from the cursor position to the line series to trigger events (then used for highlighting series, etc).
|
||||
*/
|
||||
selectionTolerance: 20,
|
||||
|
||||
/**
|
||||
* @cfg {Object} style
|
||||
* An object containing styles for the visualization lines. These styles will override the theme styles.
|
||||
* Some options contained within the style object will are described next.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @cfg {Boolean/Number} smooth
|
||||
* If set to `true` or a non-zero number, the line will be smoothed/rounded around its points; otherwise
|
||||
* straight line segments will be drawn.
|
||||
*
|
||||
* A numeric value is interpreted as a divisor of the horizontal distance between consecutive points in
|
||||
* the line; larger numbers result in sharper curves while smaller numbers result in smoother curves.
|
||||
*
|
||||
* If set to `true` then a default numeric value of 3 will be used.
|
||||
*/
|
||||
smooth: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} step
|
||||
* If set to `true`, the line uses steps instead of straight lines to connect the dots.
|
||||
* It is ignored if `smooth` is true.
|
||||
*/
|
||||
step: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} fill
|
||||
* If set to `true`, the area underneath the line is filled with the color defined as follows, listed by priority:
|
||||
* - The color that is configured for this series ({@link Ext.chart.series.Series#colors}).
|
||||
* - The color that is configured for this chart ({@link Ext.chart.AbstractChart#colors}).
|
||||
* - The fill color that is set in the {@link #style} config.
|
||||
* - The stroke color that is set in the {@link #style} config, or the same color as the line.
|
||||
*
|
||||
* Note: Do not confuse `series.config.fill` (which is a boolean) with `series.style.fill' (which is an alias
|
||||
* for the `fillStyle` property and contains a color). For compatibility with previous versions of the API,
|
||||
* if `config.fill` is undefined but a `style.fill' color is provided, `config.fill` is considered true.
|
||||
* So the default value below must be undefined, not false.
|
||||
*/
|
||||
fill: undefined,
|
||||
|
||||
aggregator: { strategy: 'double' }
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Default numeric smoothing value to be used when `{@link #smooth} = true`.
|
||||
*/
|
||||
defaultSmoothness: 3,
|
||||
|
||||
/**
|
||||
* @private Size of the buffer area on either side of the viewport to provide seamless zoom/pan
|
||||
* transforms. Expressed as a multiple of the viewport length, e.g. 1 will make the buffer on
|
||||
* each side equal to the length of the visible axis viewport.
|
||||
*/
|
||||
overflowBuffer: 1,
|
||||
|
||||
/**
|
||||
* @private Override {@link Ext.chart.series.Series#getDefaultSpriteConfig}
|
||||
*/
|
||||
getDefaultSpriteConfig: function () {
|
||||
var me = this,
|
||||
parentConfig = me.callSuper(arguments),
|
||||
style = me.getStyle(),
|
||||
fillArea = false;
|
||||
|
||||
if (typeof me.config.fill != 'undefined') {
|
||||
// If config.fill is present but there is no fillStyle, then use the
|
||||
// strokeStyle to fill (and paint the area the same color as the line).
|
||||
if (me.config.fill) {
|
||||
fillArea = true;
|
||||
if (typeof style.fillStyle == 'undefined') {
|
||||
style.fillStyle = style.strokeStyle;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For compatibility with previous versions of the API, if config.fill
|
||||
// is undefined but style.fillStyle is provided, we fill the area.
|
||||
if (style.fillStyle) {
|
||||
fillArea = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't fill, then delete the fillStyle because that's what is used by
|
||||
// the Line sprite to fill below the line.
|
||||
if (!fillArea) {
|
||||
delete style.fillStyle;
|
||||
}
|
||||
|
||||
return Ext.apply(parentConfig || {}, {
|
||||
fillArea: fillArea,
|
||||
step: me.config.step,
|
||||
smooth: me.config.smooth,
|
||||
selectionTolerance: me.config.selectionTolerance
|
||||
});
|
||||
}
|
||||
|
||||
});
|
393
vendor/touch/src/chart/series/Pie.js
vendored
Normal file
393
vendor/touch/src/chart/series/Pie.js
vendored
Normal file
|
@ -0,0 +1,393 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Pie
|
||||
* @extends Ext.chart.series.Polar
|
||||
*
|
||||
* Creates a Pie Chart. A Pie Chart is a useful visualization technique to display quantitative information for different
|
||||
* categories that also have a meaning as a whole.
|
||||
* As with all other series, the Pie Series must be appended in the *series* Chart array configuration. See the Chart
|
||||
* documentation for more information. A typical configuration object for the pie series could be:
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.PolarChart({
|
||||
* animate: true,
|
||||
* interactions: ['rotate'],
|
||||
* colors: ['#115fa6', '#94ae0a', '#a61120', '#ff8809', '#ffd13e'],
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {name: 'metric one', data1: 10, data2: 12, data3: 14, data4: 8, data5: 13},
|
||||
* {name: 'metric two', data1: 7, data2: 8, data3: 16, data4: 10, data5: 3},
|
||||
* {name: 'metric three', data1: 5, data2: 2, data3: 14, data4: 12, data5: 7},
|
||||
* {name: 'metric four', data1: 2, data2: 14, data3: 6, data4: 1, data5: 23},
|
||||
* {name: 'metric five', data1: 27, data2: 38, data3: 36, data4: 13, data5: 33}
|
||||
* ]
|
||||
* },
|
||||
* series: [{
|
||||
* type: 'pie',
|
||||
* label: {
|
||||
* field: 'name',
|
||||
* display: 'rotate'
|
||||
* },
|
||||
* xField: 'data3',
|
||||
* donut: 30
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*
|
||||
* In this configuration we set `pie` as the type for the series, set an object with specific style properties for highlighting options
|
||||
* (triggered when hovering elements). We also set true to `showInLegend` so all the pie slices can be represented by a legend item.
|
||||
* We set `data1` as the value of the field to determine the angle span for each pie slice. We also set a label configuration object
|
||||
* where we set the field name of the store field to be rendered as text for the label. The labels will also be displayed rotated.
|
||||
* We set `contrast` to `true` to flip the color of the label if it is to similar to the background color. Finally, we set the font family
|
||||
* and size through the `font` parameter.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Pie', {
|
||||
extend: 'Ext.chart.series.Polar',
|
||||
requires: [
|
||||
'Ext.chart.series.sprite.PieSlice'
|
||||
],
|
||||
type: 'pie',
|
||||
alias: 'series.pie',
|
||||
seriesType: 'pieslice',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {String} labelField
|
||||
* @deprecated Use {@link Ext.chart.series.Pie#label} instead.
|
||||
* The store record field name to be used for the pie slice labels.
|
||||
*/
|
||||
labelField: false,
|
||||
|
||||
/**
|
||||
* @cfg {Number} donut Specifies the radius of the donut hole, as a percentage of the chart's radius.
|
||||
* Defaults to 0 (no donut hole).
|
||||
*/
|
||||
donut: 0,
|
||||
|
||||
/**
|
||||
* @cfg {String} field
|
||||
* @deprecated Use xField directly
|
||||
*/
|
||||
field: null,
|
||||
|
||||
/**
|
||||
* @cfg {Number} rotation The starting angle of the pie slices.
|
||||
*/
|
||||
rotation: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Number} [totalAngle=2*PI] The total angle of the pie series.
|
||||
*/
|
||||
totalAngle: Math.PI * 2,
|
||||
|
||||
/**
|
||||
* @cfg {Array} hidden Determines which pie slices are hidden.
|
||||
*/
|
||||
hidden: [],
|
||||
|
||||
/**
|
||||
* @cfg {Number} Allows adjustment of the radius by a spefic perfentage.
|
||||
*/
|
||||
radiusFactor: 100,
|
||||
|
||||
style: {
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
directions: ['X'],
|
||||
|
||||
setField: function (f) {
|
||||
return this.setXField(f);
|
||||
},
|
||||
|
||||
getField: function () {
|
||||
return this.getXField();
|
||||
},
|
||||
|
||||
applyRadius : function (radius) {
|
||||
return radius * this.getRadiusFactor() * 0.01;
|
||||
},
|
||||
|
||||
updateLabelData: function () {
|
||||
var me = this,
|
||||
store = me.getStore(),
|
||||
items = store.getData().items,
|
||||
sprites = me.getSprites(),
|
||||
labelField = me.getLabel().getTemplate().getField(),
|
||||
hidden = me.getHidden(),
|
||||
i, ln, labels, sprite;
|
||||
if (sprites.length > 0 && labelField) {
|
||||
labels = [];
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
labels.push(items[i].get(labelField));
|
||||
}
|
||||
for (i = 0, ln = sprites.length; i < ln; i++) {
|
||||
sprite = sprites[i];
|
||||
sprite.setAttributes({label: labels[i]});
|
||||
sprite.putMarker('labels', {hidden: hidden[i]}, sprite.attr.attributeId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
coordinateX: function () {
|
||||
var me = this,
|
||||
store = me.getStore(),
|
||||
items = store.getData().items,
|
||||
length = items.length,
|
||||
field = me.getXField(),
|
||||
value, sum = 0,
|
||||
hidden = me.getHidden(),
|
||||
summation = [], i,
|
||||
lastAngle = 0,
|
||||
totalAngle = me.getTotalAngle(),
|
||||
sprites = me.getSprites();
|
||||
|
||||
if (!sprites) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
value = Math.abs(Number(items[i].get(field))) || 0;
|
||||
if (!hidden[i]) {
|
||||
sum += value;
|
||||
}
|
||||
summation[i] = sum;
|
||||
if (i >= hidden.length) {
|
||||
hidden[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sum !== 0) {
|
||||
sum = totalAngle / sum;
|
||||
}
|
||||
for (i = 0; i < length; i++) {
|
||||
sprites[i].setAttributes({
|
||||
startAngle: lastAngle,
|
||||
endAngle: lastAngle = (sum ? summation[i] * sum : 0),
|
||||
globalAlpha: 1
|
||||
});
|
||||
}
|
||||
for (; i < me.sprites.length; i++) {
|
||||
sprites[i].setAttributes({
|
||||
startAngle: totalAngle,
|
||||
endAngle: totalAngle,
|
||||
globalAlpha: 0
|
||||
});
|
||||
}
|
||||
me.getChart().refreshLegendStore();
|
||||
},
|
||||
|
||||
updateCenter: function (center) {
|
||||
this.setStyle({
|
||||
translationX: center[0] + this.getOffsetX(),
|
||||
translationY: center[1] + this.getOffsetY()
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRadius: function (radius) {
|
||||
this.setStyle({
|
||||
startRho: radius * this.getDonut() * 0.01, // Percentage
|
||||
endRho: radius
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateDonut: function (donut) {
|
||||
var radius = this.getRadius();
|
||||
this.setStyle({
|
||||
startRho: radius * donut * 0.01, // Percentage
|
||||
endRho: radius
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
this.setStyle({
|
||||
rotationRads: rotation
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateTotalAngle: function (totalAngle) {
|
||||
this.processData();
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
store = me.getStore();
|
||||
if (!chart || !store) {
|
||||
return [];
|
||||
}
|
||||
me.getColors();
|
||||
me.getSubStyle();
|
||||
var items = store.getData().items,
|
||||
length = items.length,
|
||||
animation = chart && chart.getAnimate(),
|
||||
sprites = me.sprites, sprite,
|
||||
spriteIndex = 0, rendererData,
|
||||
i, spriteCreated = false,
|
||||
label = me.getLabel(),
|
||||
labelTpl = label.getTemplate();
|
||||
|
||||
rendererData = {
|
||||
store: store,
|
||||
field: me.getField(),
|
||||
series: me
|
||||
};
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
sprite = sprites[i];
|
||||
if (!sprite) {
|
||||
sprite = me.createSprite();
|
||||
if (me.getHighlightCfg()) {
|
||||
sprite.config.highlightCfg = me.getHighlightCfg();
|
||||
sprite.addModifier('highlight', true);
|
||||
}
|
||||
if (labelTpl.getField()) {
|
||||
labelTpl.setAttributes({
|
||||
labelOverflowPadding: me.getLabelOverflowPadding()
|
||||
});
|
||||
labelTpl.fx.setCustomDuration({'callout': 200});
|
||||
sprite.bindMarker('labels', label);
|
||||
}
|
||||
sprite.setAttributes(me.getStyleByIndex(i));
|
||||
sprite.rendererData = rendererData;
|
||||
sprite.rendererIndex = spriteIndex++;
|
||||
spriteCreated = true;
|
||||
}
|
||||
sprite.fx.setConfig(animation);
|
||||
}
|
||||
if (spriteCreated) {
|
||||
me.doUpdateStyles();
|
||||
}
|
||||
return me.sprites;
|
||||
},
|
||||
|
||||
normalizeAngle: function (angle) {
|
||||
var pi2 = Math.PI * 2;
|
||||
if (angle >= 0) {
|
||||
return angle % pi2;
|
||||
}
|
||||
return (angle % pi2 + pi2) % pi2;
|
||||
},
|
||||
|
||||
betweenAngle: function (x, a, b) {
|
||||
var normalize = this.normalizeAngle;
|
||||
a = normalize(a);
|
||||
b = normalize(b);
|
||||
x = normalize(x);
|
||||
if (b === 0) {
|
||||
b = Math.PI * 2;
|
||||
}
|
||||
return x >= a && x < b;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the pie slice for a given angle
|
||||
* @param {Number} angle The angle to search for the slice
|
||||
* @return {Object} An object containing the reocord, sprite, scope etc.
|
||||
*/
|
||||
getItemForAngle: function (angle) {
|
||||
var me = this,
|
||||
sprites = me.getSprites(),
|
||||
attr;
|
||||
|
||||
angle %= Math.PI * 2;
|
||||
|
||||
while (angle < 0) {
|
||||
angle += Math.PI * 2;
|
||||
}
|
||||
|
||||
if (sprites) {
|
||||
var store = me.getStore(),
|
||||
items = store.getData().items,
|
||||
hidden = me.getHidden(),
|
||||
i = 0,
|
||||
ln = store.getCount();
|
||||
|
||||
for (; i < ln; i++) {
|
||||
if(!hidden[i]) {
|
||||
// Fortunately, the id of items equals the index of it in instances list.
|
||||
attr = sprites[i].attr;
|
||||
|
||||
if (attr.startAngle <= angle && attr.endAngle >= angle) {
|
||||
return {
|
||||
series: me,
|
||||
sprite: sprites[i],
|
||||
index: i,
|
||||
record: items[i],
|
||||
field: me.getXField()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getItemForPoint: function (x, y) {
|
||||
var me = this,
|
||||
sprites = me.getSprites();
|
||||
if (sprites) {
|
||||
var center = me.getCenter(),
|
||||
offsetX = me.getOffsetX(),
|
||||
offsetY = me.getOffsetY(),
|
||||
originalX = x - center[0] + offsetX,
|
||||
originalY = y - center[1] + offsetY,
|
||||
store = me.getStore(),
|
||||
donut = me.getDonut(),
|
||||
items = store.getData().items,
|
||||
direction = Math.atan2(originalY, originalX) - me.getRotation(),
|
||||
donutLimit = Math.sqrt(originalX * originalX + originalY * originalY),
|
||||
endRadius = me.getRadius(),
|
||||
startRadius = donut / 100 * endRadius,
|
||||
hidden = me.getHidden(),
|
||||
i, ln, attr;
|
||||
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
if(!hidden[i]) {
|
||||
// Fortunately, the id of items equals the index of it in instances list.
|
||||
attr = sprites[i].attr;
|
||||
if (startRadius + attr.margin <= donutLimit && donutLimit + attr.margin <= endRadius) {
|
||||
if (this.betweenAngle(direction, attr.startAngle, attr.endAngle)) {
|
||||
return {
|
||||
series: this,
|
||||
sprite: sprites[i],
|
||||
index: i,
|
||||
record: items[i],
|
||||
field: this.getXField()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
provideLegendInfo: function (target) {
|
||||
var store = this.getStore();
|
||||
if (store) {
|
||||
var items = store.getData().items,
|
||||
labelField = this.getLabel().getTemplate().getField(),
|
||||
field = this.getField(),
|
||||
hidden = this.getHidden();
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
target.push({
|
||||
name: labelField ? String(items[i].get(labelField)) : field + ' ' + i,
|
||||
mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
|
||||
disabled: hidden[i],
|
||||
series: this.getId(),
|
||||
index: i
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
247
vendor/touch/src/chart/series/Pie3D.js
vendored
Normal file
247
vendor/touch/src/chart/series/Pie3D.js
vendored
Normal file
|
@ -0,0 +1,247 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Pie3D
|
||||
* @extends Ext.chart.series.Polar
|
||||
*
|
||||
* Creates a 3D Pie Chart.
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.PolarChart({
|
||||
* animate: true,
|
||||
* interactions: ['rotate'],
|
||||
* colors: ["#115fa6", "#94ae0a", "#a61120", "#ff8809", "#ffd13e"],
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* series: [{
|
||||
* type: 'pie3d',
|
||||
* field: 'data3',
|
||||
* donut: 30
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Pie3D', {
|
||||
requires: ['Ext.chart.series.sprite.Pie3DPart'],
|
||||
extend: 'Ext.chart.series.Polar',
|
||||
type: 'pie3d',
|
||||
seriesType: 'pie3d',
|
||||
alias: 'series.pie3d',
|
||||
config: {
|
||||
region: [0, 0, 0, 0],
|
||||
thickness: 35,
|
||||
distortion: 0.5,
|
||||
|
||||
/**
|
||||
* @cfg {String} field (required)
|
||||
* The store record field name to be used for the pie angles.
|
||||
* The values bound to this field name must be positive real numbers.
|
||||
*/
|
||||
field: false,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {String} lengthField
|
||||
* Not supported.
|
||||
*/
|
||||
lengthField: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean/Number} donut
|
||||
* Whether to set the pie chart as donut chart.
|
||||
* Can be set to a particular percentage to set the radius
|
||||
* of the donut chart.
|
||||
*/
|
||||
donut: false,
|
||||
|
||||
rotation: 0
|
||||
},
|
||||
|
||||
applyRotation: function (rotation) {
|
||||
var twoPie = Math.PI * 2;
|
||||
return (rotation % twoPie + twoPie) % twoPie;
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
var sprites = this.getSprites(),
|
||||
i, ln;
|
||||
for (i = 0, ln = sprites.length; i < ln; i++) {
|
||||
sprites[i].setAttributes({
|
||||
baseRotation: rotation
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
updateColors: function (colorSet) {
|
||||
this.setSubStyle({baseColor: colorSet});
|
||||
},
|
||||
|
||||
doUpdateStyles: function () {
|
||||
var sprites = this.getSprites(),
|
||||
i = 0, j = 0, ln = sprites && sprites.length;
|
||||
for (; i < ln; i += 5, j++) {
|
||||
sprites[i].setAttributes(this.getStyleByIndex(j));
|
||||
sprites[i + 1].setAttributes(this.getStyleByIndex(j));
|
||||
sprites[i + 2].setAttributes(this.getStyleByIndex(j));
|
||||
sprites[i + 3].setAttributes(this.getStyleByIndex(j));
|
||||
sprites[i + 4].setAttributes(this.getStyleByIndex(j));
|
||||
}
|
||||
},
|
||||
|
||||
processData: function () {
|
||||
var me = this,
|
||||
chart = me.getChart(),
|
||||
animation = chart && chart.getAnimate(),
|
||||
store = me.getStore(),
|
||||
items = store.getData().items,
|
||||
length = items.length,
|
||||
field = me.getField(),
|
||||
value, sum = 0, ratio,
|
||||
summation = [],
|
||||
i,
|
||||
sprites = this.getSprites(),
|
||||
lastAngle;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
value = items[i].get(field);
|
||||
sum += value;
|
||||
summation[i] = sum;
|
||||
}
|
||||
if (sum === 0) {
|
||||
return;
|
||||
}
|
||||
ratio = 2 * Math.PI / sum;
|
||||
for (i = 0; i < length; i++) {
|
||||
summation[i] *= ratio;
|
||||
}
|
||||
|
||||
for (i = 0; i < sprites.length; i++) {
|
||||
sprites[i].fx.setConfig(animation);
|
||||
}
|
||||
|
||||
for (i = 0, lastAngle = 0; i < length; i++) {
|
||||
var commonAttributes = {opacity: 1, startAngle: lastAngle, endAngle: summation[i]};
|
||||
sprites[i * 5].setAttributes(commonAttributes);
|
||||
sprites[i * 5 + 1].setAttributes(commonAttributes);
|
||||
sprites[i * 5 + 2].setAttributes(commonAttributes);
|
||||
sprites[i * 5 + 3].setAttributes(commonAttributes);
|
||||
sprites[i * 5 + 4].setAttributes(commonAttributes);
|
||||
lastAngle = summation[i];
|
||||
}
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
var me = this,
|
||||
chart = this.getChart(),
|
||||
surface = me.getSurface(),
|
||||
store = me.getStore();
|
||||
if (!store) {
|
||||
return [];
|
||||
}
|
||||
var items = store.getData().items,
|
||||
length = items.length,
|
||||
animation = chart && chart.getAnimate(),
|
||||
region = chart.getMainRegion() || [0, 0, 1, 1],
|
||||
rotation = me.getRotation(),
|
||||
center = me.getCenter(),
|
||||
offsetX = me.getOffsetX(),
|
||||
offsetY = me.getOffsetY(),
|
||||
radius = Math.min((region[3] - me.getThickness() * 2) / me.getDistortion(), region[2]) / 2,
|
||||
commonAttributes = {
|
||||
centerX: center[0] + offsetX,
|
||||
centerY: center[1] + offsetY - me.getThickness() / 2,
|
||||
endRho: radius,
|
||||
startRho: radius * me.getDonut() / 100,
|
||||
thickness: me.getThickness(),
|
||||
distortion: me.getDistortion()
|
||||
}, sliceAttributes, twoPie = Math.PI * 2,
|
||||
topSprite, startSprite, endSprite, innerSideSprite, outerSideSprite,
|
||||
i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
sliceAttributes = Ext.apply({}, this.getStyleByIndex(i), commonAttributes);
|
||||
topSprite = me.sprites[i * 5];
|
||||
if (!topSprite) {
|
||||
topSprite = surface.add({
|
||||
type: 'pie3dPart',
|
||||
part: 'top',
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie
|
||||
});
|
||||
startSprite = surface.add({
|
||||
type: 'pie3dPart',
|
||||
part: 'start',
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie
|
||||
});
|
||||
endSprite = surface.add({
|
||||
type: 'pie3dPart',
|
||||
part: 'end',
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie
|
||||
});
|
||||
innerSideSprite = surface.add({
|
||||
type: 'pie3dPart',
|
||||
part: 'inner',
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie,
|
||||
thickness: 0
|
||||
});
|
||||
outerSideSprite = surface.add({
|
||||
type: 'pie3dPart',
|
||||
part: 'outer',
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie,
|
||||
thickness: 0
|
||||
});
|
||||
topSprite.fx.setDurationOn('baseRotation', 0);
|
||||
startSprite.fx.setDurationOn('baseRotation', 0);
|
||||
endSprite.fx.setDurationOn('baseRotation', 0);
|
||||
innerSideSprite.fx.setDurationOn('baseRotation', 0);
|
||||
outerSideSprite.fx.setDurationOn('baseRotation', 0);
|
||||
topSprite.setAttributes(sliceAttributes);
|
||||
startSprite.setAttributes(sliceAttributes);
|
||||
endSprite.setAttributes(sliceAttributes);
|
||||
innerSideSprite.setAttributes(sliceAttributes);
|
||||
outerSideSprite.setAttributes(sliceAttributes);
|
||||
me.sprites.push(topSprite, startSprite, endSprite, innerSideSprite, outerSideSprite);
|
||||
} else {
|
||||
startSprite = me.sprites[i * 5 + 1];
|
||||
endSprite = me.sprites[i * 5 + 2];
|
||||
innerSideSprite = me.sprites[i * 5 + 3];
|
||||
outerSideSprite = me.sprites[i * 5 + 4];
|
||||
if (animation) {
|
||||
topSprite.fx.setConfig(animation);
|
||||
startSprite.fx.setConfig(animation);
|
||||
endSprite.fx.setConfig(animation);
|
||||
innerSideSprite.fx.setConfig(animation);
|
||||
outerSideSprite.fx.setConfig(animation);
|
||||
}
|
||||
topSprite.setAttributes(sliceAttributes);
|
||||
startSprite.setAttributes(sliceAttributes);
|
||||
endSprite.setAttributes(sliceAttributes);
|
||||
innerSideSprite.setAttributes(sliceAttributes);
|
||||
outerSideSprite.setAttributes(sliceAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
for (i *= 5; i < me.sprites.length; i++) {
|
||||
me.sprites[i].fx.setConfig(animation);
|
||||
me.sprites[i].setAttributes({
|
||||
opacity: 0,
|
||||
startAngle: twoPie,
|
||||
endAngle: twoPie,
|
||||
baseRotation: rotation
|
||||
});
|
||||
}
|
||||
|
||||
return me.sprites;
|
||||
}
|
||||
});
|
90
vendor/touch/src/chart/series/Polar.js
vendored
Normal file
90
vendor/touch/src/chart/series/Polar.js
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* Polar series.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Polar', {
|
||||
|
||||
extend: 'Ext.chart.series.Series',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Number} rotation
|
||||
* The angle in degrees at which the first polar series item should start.
|
||||
*/
|
||||
rotation: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Number} radius
|
||||
* The radius of the polar series. Set to `null` will fit the polar series to the boundary.
|
||||
*/
|
||||
radius: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array} center for the polar series.
|
||||
*/
|
||||
center: [0, 0],
|
||||
|
||||
/**
|
||||
* @cfg {Number} offsetX
|
||||
* The x-offset of center of the polar series related to the center of the boundary.
|
||||
*/
|
||||
offsetX: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Number} offsetY
|
||||
* The y-offset of center of the polar series related to the center of the boundary.
|
||||
*/
|
||||
offsetY: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} showInLegend
|
||||
* Whether to add the series elements as legend items.
|
||||
*/
|
||||
showInLegend: true,
|
||||
|
||||
/**
|
||||
* @cfg {String} xField
|
||||
* The store record field name for the labels used in the radar series.
|
||||
*/
|
||||
xField: null,
|
||||
|
||||
/**
|
||||
* @cfg {String} yField
|
||||
* The store record field name for the deflection of the graph in the radar series.
|
||||
*/
|
||||
yField: null,
|
||||
|
||||
xAxis: null,
|
||||
|
||||
yAxis: null
|
||||
},
|
||||
|
||||
directions: ['X', 'Y'],
|
||||
|
||||
fieldCategoryX: ['X'],
|
||||
fieldCategoryY: ['Y'],
|
||||
|
||||
getDefaultSpriteConfig: function () {
|
||||
return {
|
||||
type: this.seriesType,
|
||||
renderer: this.getRenderer(),
|
||||
centerX: 0,
|
||||
centerY: 0,
|
||||
rotationCenterX: 0,
|
||||
rotationCenterY: 0
|
||||
};
|
||||
},
|
||||
|
||||
applyRotation: function (rotation) {
|
||||
var twoPie = Math.PI * 2;
|
||||
return (rotation % twoPie + Math.PI) % twoPie - Math.PI;
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
var sprites = this.getSprites();
|
||||
if (sprites && sprites[0]) {
|
||||
sprites[0].setAttributes({
|
||||
baseRotation: rotation
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
171
vendor/touch/src/chart/series/Radar.js
vendored
Normal file
171
vendor/touch/src/chart/series/Radar.js
vendored
Normal file
|
@ -0,0 +1,171 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Radar
|
||||
* @extends Ext.chart.series.Polar
|
||||
*
|
||||
* Creates a Radar Chart. A Radar Chart is a useful visualization technique for comparing different quantitative values for
|
||||
* a constrained number of categories.
|
||||
* As with all other series, the Radar series must be appended in the *series* Chart array configuration. See the Chart
|
||||
* documentation for more information. A typical configuration object for the radar series could be:
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.PolarChart({
|
||||
* animate: true,
|
||||
* interactions: ['rotate'],
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* series: [{
|
||||
* type: 'radar',
|
||||
* xField: 'name',
|
||||
* yField: 'data4',
|
||||
* style: {
|
||||
* fillStyle: 'rgba(0, 0, 255, 0.1)',
|
||||
* strokeStyle: 'rgba(0, 0, 0, 0.8)',
|
||||
* lineWidth: 1
|
||||
* }
|
||||
* }],
|
||||
* axes: [
|
||||
* {
|
||||
* type: 'numeric',
|
||||
* position: 'radial',
|
||||
* fields: 'data4',
|
||||
* style: {
|
||||
* estStepSize: 10
|
||||
* },
|
||||
* grid: true
|
||||
* },
|
||||
* {
|
||||
* type: 'category',
|
||||
* position: 'angular',
|
||||
* fields: 'name',
|
||||
* style: {
|
||||
* estStepSize: 1
|
||||
* },
|
||||
* grid: true
|
||||
* }
|
||||
* ]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Radar', {
|
||||
extend: 'Ext.chart.series.Polar',
|
||||
type: "radar",
|
||||
seriesType: 'radar',
|
||||
alias: 'series.radar',
|
||||
requires: ['Ext.chart.series.Cartesian', 'Ext.chart.series.sprite.Radar'],
|
||||
/**
|
||||
* @cfg {Object} style
|
||||
* An object containing styles for overriding series styles from theming.
|
||||
*/
|
||||
|
||||
config: {
|
||||
|
||||
},
|
||||
|
||||
updateAngularAxis: function (axis) {
|
||||
axis.processData(this);
|
||||
},
|
||||
|
||||
updateRadialAxis: function (axis) {
|
||||
axis.processData(this);
|
||||
},
|
||||
|
||||
coordinateX: function () {
|
||||
return this.coordinate('X', 0, 2);
|
||||
},
|
||||
|
||||
coordinateY: function () {
|
||||
return this.coordinate('Y', 1, 2);
|
||||
},
|
||||
|
||||
updateCenter: function (center) {
|
||||
this.setStyle({
|
||||
translationX: center[0] + this.getOffsetX(),
|
||||
translationY: center[1] + this.getOffsetY()
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRadius: function (radius) {
|
||||
this.setStyle({
|
||||
endRho: radius
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateRotation: function (rotation) {
|
||||
this.setStyle({
|
||||
rotationRads: rotation
|
||||
});
|
||||
this.doUpdateStyles();
|
||||
},
|
||||
|
||||
updateTotalAngle: function (totalAngle) {
|
||||
this.processData();
|
||||
},
|
||||
|
||||
getItemForPoint: function (x, y) {
|
||||
var me = this,
|
||||
sprite = me.sprites && me.sprites[0],
|
||||
attr = sprite.attr,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
minX = attr.dataMinX,
|
||||
maxX = attr.dataMaxX,
|
||||
maxY = attr.dataMaxY,
|
||||
endRho = attr.endRho,
|
||||
startRho = attr.startRho,
|
||||
baseRotation = attr.baseRotation,
|
||||
i, length = dataX.length,
|
||||
store = me.getStore(),
|
||||
marker = me.getMarker(),
|
||||
item, th, r;
|
||||
|
||||
if(me.getHidden()) {
|
||||
return null;
|
||||
}
|
||||
if (sprite && marker) {
|
||||
for (i = 0; i < length; i++) {
|
||||
th = (dataX[i] - minX) / (maxX - minX + 1) * 2 * Math.PI + baseRotation;
|
||||
r = dataY[i] / maxY * (endRho - startRho) + startRho;
|
||||
if (Math.abs(centerX + Math.cos(th) * r - x) < 22 && Math.abs(centerY + Math.sin(th) * r - y) < 22) {
|
||||
item = {
|
||||
series: this,
|
||||
sprite: sprite,
|
||||
index: i,
|
||||
record: store.getData().items[i],
|
||||
field: store.getFields().items[i]
|
||||
};
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.callSuper(arguments);
|
||||
},
|
||||
|
||||
getXRange: function () {
|
||||
return [this.dataRange[0], this.dataRange[2]];
|
||||
},
|
||||
|
||||
getYRange: function () {
|
||||
return [this.dataRange[1], this.dataRange[3]];
|
||||
}
|
||||
}, function () {
|
||||
var klass = this;
|
||||
// TODO: [HACK] Steal from cartesian series.
|
||||
klass.prototype.onAxesChanged = Ext.chart.series.Cartesian.prototype.onAxesChanged;
|
||||
klass.prototype.getSprites = Ext.chart.series.Cartesian.prototype.getSprites;
|
||||
});
|
||||
|
108
vendor/touch/src/chart/series/Scatter.js
vendored
Normal file
108
vendor/touch/src/chart/series/Scatter.js
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* @class Ext.chart.series.Scatter
|
||||
* @extends Ext.chart.series.Cartesian
|
||||
*
|
||||
* Creates a Scatter Chart. The scatter plot is useful when trying to display more than two variables in the same visualization.
|
||||
* These variables can be mapped into x, y coordinates and also to an element's radius/size, color, etc.
|
||||
* As with all other series, the Scatter Series must be appended in the *series* Chart array configuration. See the Chart
|
||||
* documentation for more information on creating charts. A typical configuration object for the scatter could be:
|
||||
*
|
||||
* @example preview
|
||||
* var chart = new Ext.chart.CartesianChart({
|
||||
* animate: true,
|
||||
* store: {
|
||||
* fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
|
||||
* data: [
|
||||
* {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
|
||||
* {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
|
||||
* {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
|
||||
* {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
|
||||
* {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
|
||||
* ]
|
||||
* },
|
||||
* axes: [{
|
||||
* type: 'numeric',
|
||||
* position: 'left',
|
||||
* fields: ['data1'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* },
|
||||
* grid: true,
|
||||
* minimum: 0
|
||||
* }, {
|
||||
* type: 'category',
|
||||
* position: 'bottom',
|
||||
* fields: ['name'],
|
||||
* title: {
|
||||
* text: 'Sample Values',
|
||||
* fontSize: 15
|
||||
* }
|
||||
* }],
|
||||
* series: [{
|
||||
* type: 'scatter',
|
||||
* highlight: {
|
||||
* size: 7,
|
||||
* radius: 7
|
||||
* },
|
||||
* fill: true,
|
||||
* xField: 'name',
|
||||
* yField: 'data3',
|
||||
* marker: {
|
||||
* type: 'circle',
|
||||
* fillStyle: 'blue',
|
||||
* radius: 10,
|
||||
* lineWidth: 0
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(chart);
|
||||
*
|
||||
* In this configuration we add three different categories of scatter series. Each of them is bound to a different field of the same data store,
|
||||
* `data1`, `data2` and `data3` respectively. All x-fields for the series must be the same field, in this case `name`.
|
||||
* Each scatter series has a different styling configuration for markers, specified by the `marker` object. Finally we set the left axis as
|
||||
* axis to show the current values of the elements.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.series.Scatter', {
|
||||
|
||||
extend: 'Ext.chart.series.Cartesian',
|
||||
|
||||
alias: 'series.scatter',
|
||||
|
||||
type: 'scatter',
|
||||
seriesType: 'scatterSeries',
|
||||
|
||||
requires: [
|
||||
'Ext.chart.series.sprite.Scatter'
|
||||
],
|
||||
|
||||
config: {
|
||||
itemInstancing: {
|
||||
fx: {
|
||||
customDuration: {
|
||||
translationX: 0,
|
||||
translationY: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyMarker: function (marker) {
|
||||
this.getItemInstancing();
|
||||
this.setItemInstancing(marker);
|
||||
},
|
||||
|
||||
provideLegendInfo: function (target) {
|
||||
var style = this.config.marker;
|
||||
target.push({
|
||||
name: this.getTitle() || this.getYField() || this.getId(),
|
||||
mark: style.fill || style.stroke || 'black',
|
||||
disabled: false,
|
||||
series: this.getId(),
|
||||
index: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
|
1028
vendor/touch/src/chart/series/Series.js
vendored
Normal file
1028
vendor/touch/src/chart/series/Series.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
160
vendor/touch/src/chart/series/StackedCartesian.js
vendored
Normal file
160
vendor/touch/src/chart/series/StackedCartesian.js
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
/**
|
||||
* @abstract
|
||||
* @extends Ext.chart.series.Cartesian
|
||||
* Abstract class for all the stacked cartesian series including area series
|
||||
* and bar series.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.StackedCartesian', {
|
||||
|
||||
extend: 'Ext.chart.series.Cartesian',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Boolean}
|
||||
* 'true' to display the series in its stacked configuration.
|
||||
*/
|
||||
stacked: true,
|
||||
|
||||
/**
|
||||
* @cfg {Array} hidden
|
||||
*/
|
||||
hidden: []
|
||||
},
|
||||
|
||||
animatingSprites: 0,
|
||||
|
||||
updateStacked: function () {
|
||||
this.processData();
|
||||
},
|
||||
|
||||
coordinateY: function () {
|
||||
return this.coordinateStacked('Y', 1, 2);
|
||||
},
|
||||
|
||||
getFields: function (fieldCategory) {
|
||||
var me = this,
|
||||
fields = [], fieldsItem,
|
||||
i, ln;
|
||||
for (i = 0, ln = fieldCategory.length; i < ln; i++) {
|
||||
fieldsItem = me['get' + fieldCategory[i] + 'Field']();
|
||||
if (Ext.isArray(fieldsItem)) {
|
||||
fields.push.apply(fields, fieldsItem);
|
||||
} else {
|
||||
fields.push(fieldsItem);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
},
|
||||
|
||||
updateLabelOverflowPadding: function (labelOverflowPadding) {
|
||||
this.getLabel().setAttributes({labelOverflowPadding: labelOverflowPadding});
|
||||
},
|
||||
|
||||
getSprites: function () {
|
||||
var me = this,
|
||||
chart = this.getChart(),
|
||||
animation = chart && chart.getAnimate(),
|
||||
fields = me.getFields(me.fieldCategoryY),
|
||||
itemInstancing = me.getItemInstancing(),
|
||||
sprites = me.sprites, sprite,
|
||||
hidden = me.getHidden(),
|
||||
spritesCreated = false,
|
||||
i, length = fields.length;
|
||||
|
||||
if (!chart) {
|
||||
return [];
|
||||
}
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
sprite = sprites[i];
|
||||
if (!sprite) {
|
||||
sprite = me.createSprite();
|
||||
if (chart.getFlipXY()) {
|
||||
sprite.setAttributes({zIndex: i});
|
||||
} else {
|
||||
sprite.setAttributes({zIndex: -i});
|
||||
}
|
||||
sprite.setField(fields[i]);
|
||||
spritesCreated = true;
|
||||
hidden.push(false);
|
||||
if (itemInstancing) {
|
||||
sprite.itemsMarker.getTemplate().setAttributes(me.getOverriddenStyleByIndex(i));
|
||||
} else {
|
||||
sprite.setAttributes(me.getStyleByIndex(i));
|
||||
}
|
||||
}
|
||||
if (animation) {
|
||||
if (itemInstancing) {
|
||||
sprite.itemsMarker.getTemplate().fx.setConfig(animation);
|
||||
}
|
||||
sprite.fx.setConfig(animation);
|
||||
}
|
||||
}
|
||||
|
||||
if (spritesCreated) {
|
||||
me.updateHidden(hidden);
|
||||
}
|
||||
return sprites;
|
||||
},
|
||||
|
||||
getItemForPoint: function (x, y) {
|
||||
if (this.getSprites()) {
|
||||
var me = this,
|
||||
i, ln, sprite,
|
||||
itemInstancing = me.getItemInstancing(),
|
||||
sprites = me.getSprites(),
|
||||
store = me.getStore(),
|
||||
hidden = me.getHidden(),
|
||||
item;
|
||||
|
||||
for (i = 0, ln = sprites.length; i < ln; i++) {
|
||||
if(!hidden[i]) {
|
||||
sprite = sprites[i];
|
||||
var index = sprite.getIndexNearPoint(x, y);
|
||||
if (index !== -1) {
|
||||
item = {
|
||||
series: me,
|
||||
index: index,
|
||||
category: itemInstancing ? 'items' : 'markers',
|
||||
record: store.getData().items[index],
|
||||
field: this.getYField()[i],
|
||||
sprite: sprite
|
||||
};
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
provideLegendInfo: function (target) {
|
||||
var sprites = this.getSprites(),
|
||||
title = this.getTitle(),
|
||||
field = this.getYField(),
|
||||
hidden = this.getHidden();
|
||||
for (var i = 0; i < sprites.length; i++) {
|
||||
target.push({
|
||||
name: Ext.isArray(this.getTitle()) ? this.getTitle()[i] : (field && field[i]) || this.getId(),
|
||||
mark: this.getStyleByIndex(i).fillStyle || this.getStyleByIndex(i).strokeStyle || 'black',
|
||||
disabled: hidden[i],
|
||||
series: this.getId(),
|
||||
index: i
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onSpriteAnimationStart: function (sprite) {
|
||||
this.animatingSprites++;
|
||||
if (this.animatingSprites === 1) {
|
||||
this.fireEvent('animationstart');
|
||||
}
|
||||
},
|
||||
|
||||
onSpriteAnimationEnd: function (sprite) {
|
||||
this.animatingSprites--;
|
||||
if (this.animatingSprites === 0) {
|
||||
this.fireEvent('animationend');
|
||||
}
|
||||
}
|
||||
});
|
86
vendor/touch/src/chart/series/sprite/Aggregative.js
vendored
Normal file
86
vendor/touch/src/chart/series/sprite/Aggregative.js
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Aggregative', {
|
||||
extend: 'Ext.chart.series.sprite.Cartesian',
|
||||
requires: ['Ext.draw.LimitedCache', 'Ext.draw.SegmentTree'],
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Object} [dataHigh=null] Data items representing the high values of the aggregated data.
|
||||
*/
|
||||
dataHigh: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataLow=null] Data items representing the low values of the aggregated data.
|
||||
*/
|
||||
dataLow: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataClose=null] Data items representing the closing values of the aggregated data.
|
||||
*/
|
||||
dataClose: 'data'
|
||||
},
|
||||
aliases: {
|
||||
/**
|
||||
* @cfg {Object} [dataOpen=null] Data items representing the opening values of the aggregated data.
|
||||
*/
|
||||
dataOpen: 'dataY'
|
||||
},
|
||||
defaults: {
|
||||
dataHigh: null,
|
||||
dataLow: null,
|
||||
dataClose: null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
aggregator: {}
|
||||
},
|
||||
|
||||
applyAggregator: function (aggregator, oldAggr) {
|
||||
return Ext.factory(aggregator, Ext.draw.SegmentTree, oldAggr);
|
||||
},
|
||||
|
||||
constructor: function () {
|
||||
this.callSuper(arguments);
|
||||
},
|
||||
|
||||
processDataY: function () {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
high = attr.dataHigh,
|
||||
low = attr.dataLow,
|
||||
close = attr.dataClose,
|
||||
open = attr.dataY;
|
||||
me.callSuper(arguments);
|
||||
if (attr.dataX && open && open.length > 0) {
|
||||
if (high) {
|
||||
me.getAggregator().setData(attr.dataX, attr.dataY, high, low, close);
|
||||
} else {
|
||||
me.getAggregator().setData(attr.dataX, attr.dataY);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getGapWidth: function () {
|
||||
return 1;
|
||||
},
|
||||
|
||||
renderClipped: function (surface, ctx, clip, region) {
|
||||
var me = this,
|
||||
aggregates = me.getAggregator() && me.getAggregator().getAggregation(
|
||||
clip[0],
|
||||
clip[2],
|
||||
(clip[2] - clip[0]) / region[2] * me.getGapWidth()
|
||||
);
|
||||
if (aggregates) {
|
||||
me.dataStart = aggregates.data.startIdx[aggregates.start];
|
||||
me.dataEnd = aggregates.data.endIdx[aggregates.end - 1];
|
||||
|
||||
me.renderAggregates(aggregates.data, aggregates.start, aggregates.end, surface, ctx, clip, region);
|
||||
}
|
||||
}
|
||||
});
|
116
vendor/touch/src/chart/series/sprite/Area.js
vendored
Normal file
116
vendor/touch/src/chart/series/sprite/Area.js
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Area
|
||||
* @extends Ext.chart.series.sprite.StackedCartesian
|
||||
*
|
||||
* Area series sprite.
|
||||
*/
|
||||
Ext.define("Ext.chart.series.sprite.Area", {
|
||||
alias: 'sprite.areaSeries',
|
||||
extend: "Ext.chart.series.sprite.StackedCartesian",
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Boolean} [step=false] 'true' if the area is represented with steps instead of lines.
|
||||
*/
|
||||
step: 'bool'
|
||||
},
|
||||
defaults: {
|
||||
step: false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderClipped: function (surface, ctx, clip, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
dataStartY = attr.dataStartY,
|
||||
matrix = attr.matrix,
|
||||
x, y, i, lastX, lastY,
|
||||
xx = matrix.elements[0],
|
||||
dx = matrix.elements[4],
|
||||
yy = matrix.elements[3],
|
||||
dy = matrix.elements[5],
|
||||
surfaceMatrix = me.surfaceMatrix,
|
||||
markerCfg = {},
|
||||
start = Math.max(0, this.binarySearch(clip[0])),
|
||||
end = Math.min(dataX.length - 1, this.binarySearch(clip[2]) + 1);
|
||||
ctx.beginPath();
|
||||
|
||||
if (attr.step) {
|
||||
lastY = dataY[start] * yy + dy;
|
||||
for (i = start; i <= end; i++) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataY[i] * yy + dy;
|
||||
ctx.lineTo(x, lastY);
|
||||
ctx.lineTo(x, lastY = y);
|
||||
}
|
||||
} else {
|
||||
for (i = start; i <= end; i++) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataY[i] * yy + dy;
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (dataStartY) {
|
||||
if (attr.step) {
|
||||
lastX = dataX[end] * xx + dx;
|
||||
for (i = end; i >= start; i--) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataStartY[i] * yy + dy;
|
||||
ctx.lineTo(lastX, y);
|
||||
ctx.lineTo(lastX = x, y);
|
||||
}
|
||||
} else {
|
||||
for (i = end; i >= start; i--) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataStartY[i] * yy + dy;
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// dataStartY[i] == 0;
|
||||
ctx.lineTo(dataX[end] * xx + dx, y);
|
||||
ctx.lineTo(dataX[end] * xx + dx, dy);
|
||||
ctx.lineTo(dataX[start] * xx + dx, dy);
|
||||
ctx.lineTo(dataX[start] * xx + dx, dataY[i] * yy + dy);
|
||||
}
|
||||
if (attr.transformFillStroke) {
|
||||
attr.matrix.toContext(ctx);
|
||||
}
|
||||
ctx.fill();
|
||||
if (attr.transformFillStroke) {
|
||||
attr.inverseMatrix.toContext(ctx);
|
||||
}
|
||||
ctx.beginPath();
|
||||
if (attr.step) {
|
||||
for (i = start; i <= end; i++) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataY[i] * yy + dy;
|
||||
ctx.lineTo(x, lastY);
|
||||
ctx.lineTo(x, lastY = y);
|
||||
markerCfg.translationX = surfaceMatrix.x(x, y);
|
||||
markerCfg.translationY = surfaceMatrix.y(x, y);
|
||||
me.putMarker("markers", markerCfg, i, !attr.renderer);
|
||||
}
|
||||
} else {
|
||||
for (i = start; i <= end; i++) {
|
||||
x = dataX[i] * xx + dx;
|
||||
y = dataY[i] * yy + dy;
|
||||
ctx.lineTo(x, y);
|
||||
markerCfg.translationX = surfaceMatrix.x(x, y);
|
||||
markerCfg.translationY = surfaceMatrix.y(x, y);
|
||||
me.putMarker("markers", markerCfg, i, !attr.renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (attr.transformFillStroke) {
|
||||
attr.matrix.toContext(ctx);
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
});
|
223
vendor/touch/src/chart/series/sprite/Bar.js
vendored
Normal file
223
vendor/touch/src/chart/series/sprite/Bar.js
vendored
Normal file
|
@ -0,0 +1,223 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Bar
|
||||
* @extends Ext.chart.series.sprite.StackedCartesian
|
||||
*
|
||||
* Draws a sprite used in the bar series.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Bar', {
|
||||
alias: 'sprite.barSeries',
|
||||
extend: 'Ext.chart.series.sprite.StackedCartesian',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Number} [minBarWidth=2] The minimum bar width.
|
||||
*/
|
||||
minBarWidth: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [maxBarWidth=100] The maximum bar width.
|
||||
*/
|
||||
maxBarWidth: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [minGapWidth=5] The minimum gap between bars.
|
||||
*/
|
||||
minGapWidth: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [radius=0] The degree of rounding for rounded bars.
|
||||
*/
|
||||
radius: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [inGroupGapWidth=3] The gap between grouped bars.
|
||||
*/
|
||||
inGroupGapWidth: 'number'
|
||||
},
|
||||
defaults: {
|
||||
minBarWidth: 2,
|
||||
maxBarWidth: 100,
|
||||
minGapWidth: 5,
|
||||
inGroupGapWidth: 3,
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: design this more carefully
|
||||
drawLabel: function (text, dataX, dataStartY, dataY, labelId) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
label = me.getBoundMarker('labels')[0],
|
||||
labelTpl = label.getTemplate(),
|
||||
labelCfg = me.labelCfg || (me.labelCfg = {}),
|
||||
surfaceMatrix = me.surfaceMatrix,
|
||||
labelOverflowPadding = attr.labelOverflowPadding,
|
||||
labelDisplay = labelTpl.attr.display,
|
||||
labelOrientation = labelTpl.attr.orientation,
|
||||
labelY, halfWidth, labelBox,
|
||||
changes;
|
||||
|
||||
labelBox = me.getMarkerBBox('labels', labelId, true);
|
||||
labelCfg.text = text;
|
||||
if (!labelBox) {
|
||||
me.putMarker('labels', labelCfg, labelId);
|
||||
labelBox = me.getMarkerBBox('labels', labelId, true);
|
||||
}
|
||||
if (!attr.flipXY) {
|
||||
labelCfg.rotationRads = -Math.PI * 0.5;
|
||||
} else {
|
||||
labelCfg.rotationRads = 0;
|
||||
}
|
||||
labelCfg.calloutVertical = !attr.flipXY;
|
||||
|
||||
switch (labelOrientation) {
|
||||
case 'horizontal': labelCfg.rotationRads = 0; break;
|
||||
case 'vertical': labelCfg.rotationRads = -Math.PI * 0.5; break;
|
||||
}
|
||||
|
||||
halfWidth = (labelBox.width / 2 + labelOverflowPadding);
|
||||
if (dataStartY > dataY) {
|
||||
halfWidth = -halfWidth;
|
||||
}
|
||||
|
||||
if ((labelOrientation === 'horizontal' && attr.flipXY) || (labelOrientation === 'vertical' && !attr.flipXY) || !labelOrientation) {
|
||||
labelY = (labelDisplay === 'insideStart') ? dataStartY + halfWidth : dataY - halfWidth;
|
||||
} else {
|
||||
labelY = (labelDisplay === 'insideStart') ? dataStartY + labelOverflowPadding * 2 : dataY - labelOverflowPadding * 2;
|
||||
}
|
||||
labelCfg.x = surfaceMatrix.x(dataX, labelY);
|
||||
labelCfg.y = surfaceMatrix.y(dataX, labelY);
|
||||
|
||||
labelY = (labelDisplay === 'insideStart') ? dataStartY - halfWidth : dataY + halfWidth;
|
||||
labelCfg.calloutPlaceX = surfaceMatrix.x(dataX, labelY);
|
||||
labelCfg.calloutPlaceY = surfaceMatrix.y(dataX, labelY);
|
||||
|
||||
labelY = (labelDisplay === 'insideStart') ? dataStartY : dataY;
|
||||
labelCfg.calloutStartX = surfaceMatrix.x(dataX, labelY);
|
||||
labelCfg.calloutStartY = surfaceMatrix.y(dataX, labelY);
|
||||
if (dataStartY > dataY) {
|
||||
halfWidth = -halfWidth;
|
||||
}
|
||||
if (Math.abs(dataY - dataStartY) <= halfWidth * 2 || labelDisplay === 'outside') {
|
||||
labelCfg.callout = 1;
|
||||
} else {
|
||||
labelCfg.callout = 0;
|
||||
}
|
||||
|
||||
if (labelTpl.attr.renderer) {
|
||||
changes = labelTpl.attr.renderer.call(this, text, label, labelCfg, {store: this.getStore()}, labelId);
|
||||
if (typeof changes === 'string') {
|
||||
labelCfg.text = changes;
|
||||
} else {
|
||||
Ext.apply(labelCfg, changes);
|
||||
}
|
||||
}
|
||||
|
||||
me.putMarker('labels', labelCfg, labelId);
|
||||
},
|
||||
|
||||
drawBar: function (ctx, surface, clip, left, top, right, bottom, index) {
|
||||
var itemCfg = this.itemCfg || (this.itemCfg = {}),
|
||||
changes;
|
||||
|
||||
itemCfg.x = left;
|
||||
itemCfg.y = top;
|
||||
itemCfg.width = right - left;
|
||||
itemCfg.height = bottom - top;
|
||||
itemCfg.radius = this.attr.radius;
|
||||
|
||||
if (this.attr.renderer) {
|
||||
changes = this.attr.renderer.call(this, this, itemCfg, {store:this.getStore()}, index);
|
||||
Ext.apply(itemCfg, changes);
|
||||
}
|
||||
this.putMarker('items', itemCfg, index, !this.attr.renderer);
|
||||
},
|
||||
|
||||
//@inheritdoc
|
||||
renderClipped: function (surface, ctx, clip) {
|
||||
if (this.cleanRedraw) {
|
||||
return;
|
||||
}
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
dataText = attr.labels,
|
||||
dataStartY = attr.dataStartY,
|
||||
groupCount = attr.groupCount,
|
||||
groupOffset = attr.groupOffset - (groupCount - 1) * 0.5,
|
||||
inGroupGapWidth = attr.inGroupGapWidth,
|
||||
yLow, yHi,
|
||||
lineWidth = ctx.lineWidth,
|
||||
matrix = attr.matrix,
|
||||
xx = matrix.elements[0],
|
||||
yy = matrix.elements[3],
|
||||
dx = matrix.elements[4],
|
||||
dy = surface.roundPixel(matrix.elements[5]) - 1,
|
||||
maxBarWidth = xx - attr.minGapWidth,
|
||||
barWidth = surface.roundPixel(Math.max(attr.minBarWidth, (Math.min(maxBarWidth, attr.maxBarWidth) - inGroupGapWidth * (groupCount - 1)) / groupCount)),
|
||||
surfaceMatrix = this.surfaceMatrix,
|
||||
left, right, bottom, top, i, center,
|
||||
halfLineWidth = 0.5 * attr.lineWidth,
|
||||
start = Math.max(0, Math.floor(clip[0])),
|
||||
end = Math.min(dataX.length - 1, Math.ceil(clip[2])),
|
||||
drawMarkers = dataText && !!this.getBoundMarker('labels');
|
||||
|
||||
for (i = start; i <= end; i++) {
|
||||
yLow = dataStartY ? dataStartY[i] : 0;
|
||||
yHi = dataY[i];
|
||||
|
||||
center = dataX[i] * xx + dx + groupOffset * (barWidth + inGroupGapWidth);
|
||||
left = surface.roundPixel(center - barWidth / 2) + halfLineWidth;
|
||||
top = surface.roundPixel(yHi * yy + lineWidth + dy);
|
||||
right = surface.roundPixel(center + barWidth / 2) - halfLineWidth;
|
||||
bottom = surface.roundPixel(yLow * yy + lineWidth + dy);
|
||||
|
||||
me.drawBar(ctx, surface, clip, left, top - halfLineWidth, right, bottom - halfLineWidth, i);
|
||||
|
||||
if (drawMarkers && dataText[i]) {
|
||||
me.drawLabel(dataText[i], center, bottom, top, i);
|
||||
}
|
||||
me.putMarker('markers', {
|
||||
translationX: surfaceMatrix.x(center, top),
|
||||
translationY: surfaceMatrix.y(center, top)
|
||||
}, i, true);
|
||||
}
|
||||
},
|
||||
|
||||
//@inheritdoc
|
||||
getIndexNearPoint: function (x, y) {
|
||||
var sprite = this,
|
||||
attr = sprite.attr,
|
||||
dataX = attr.dataX,
|
||||
surface = sprite.getParent(),
|
||||
surfaceRegion = surface.getRegion(),
|
||||
surfaceHeight = surfaceRegion[3],
|
||||
hitX, hitY, index = -1;
|
||||
|
||||
// The "items" sprites that draw the bars work in a reverse vertical coordinate system
|
||||
// starting with 0 at the bottom and increasing the Y coordinate toward the top.
|
||||
// See also Ext.chart.series.Bar.getItemForPoint(x,y) regarding the chart's InnerPadding.
|
||||
//
|
||||
// TODO: Cleanup the bar sprites.
|
||||
if (attr.flipXY) {
|
||||
hitX = surfaceHeight - y;
|
||||
hitY = x;
|
||||
} else {
|
||||
hitX = x;
|
||||
hitY = surfaceHeight - y;
|
||||
}
|
||||
|
||||
for (var i = 0; i < dataX.length; i++) {
|
||||
var bbox = sprite.getMarkerBBox('items', i);
|
||||
if (bbox && hitX >= bbox.x && hitX <= (bbox.x + bbox.width) && hitY >= bbox.y && hitY <= (bbox.y + bbox.height)) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
});
|
165
vendor/touch/src/chart/series/sprite/CandleStick.js
vendored
Normal file
165
vendor/touch/src/chart/series/sprite/CandleStick.js
vendored
Normal file
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.CandleStick
|
||||
* @extends Ext.chart.series.sprite.Aggregative
|
||||
*
|
||||
* CandleStick series sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.CandleStick', {
|
||||
alias: 'sprite.candlestickSeries',
|
||||
extend: 'Ext.chart.series.sprite.Aggregative',
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
raiseStyle: function (n, o) {
|
||||
return Ext.merge({}, o || {}, n);
|
||||
},
|
||||
dropStyle: function (n, o) {
|
||||
return Ext.merge({}, o || {}, n);
|
||||
},
|
||||
|
||||
/**
|
||||
* @cfg {Number} [barWidth=15] The bar width of the candles.
|
||||
*/
|
||||
barWidth: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [padding=3] The amount of padding between candles.
|
||||
*/
|
||||
padding: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {String} [ohlcType='candlestick'] Determines whether candlestick or ohlc is used.
|
||||
*/
|
||||
ohlcType: 'enums(candlestick,ohlc)'
|
||||
},
|
||||
defaults: {
|
||||
raiseStyle: {
|
||||
strokeStyle: 'green',
|
||||
fillStyle: 'green'
|
||||
},
|
||||
dropStyle: {
|
||||
strokeStyle: 'red',
|
||||
fillStyle: 'red'
|
||||
},
|
||||
planar: false,
|
||||
barWidth: 15,
|
||||
padding: 3,
|
||||
lineJoin: 'miter',
|
||||
miterLimit: 5,
|
||||
ohlcType: 'candlestick'
|
||||
},
|
||||
|
||||
dirtyTriggers: {
|
||||
raiseStyle: 'raiseStyle',
|
||||
dropStyle: 'dropStyle'
|
||||
},
|
||||
|
||||
updaters: {
|
||||
raiseStyle: function () {
|
||||
this.raiseTemplate && this.raiseTemplate.setAttributes(this.attr.raiseStyle);
|
||||
},
|
||||
dropStyle: function () {
|
||||
this.dropTemplate && this.dropTemplate.setAttributes(this.attr.dropStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
candlestick: function (ctx, open, high, low, close, mid, halfWidth) {
|
||||
var minOC = Math.min(open, close),
|
||||
maxOC = Math.max(open, close);
|
||||
ctx.moveTo(mid, low);
|
||||
ctx.lineTo(mid, maxOC);
|
||||
|
||||
ctx.moveTo(mid + halfWidth, maxOC);
|
||||
ctx.lineTo(mid + halfWidth, minOC);
|
||||
ctx.lineTo(mid - halfWidth, minOC);
|
||||
ctx.lineTo(mid - halfWidth, maxOC);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.moveTo(mid, high);
|
||||
ctx.lineTo(mid, minOC);
|
||||
},
|
||||
|
||||
ohlc: function (ctx, open, high, low, close, mid, halfWidth) {
|
||||
ctx.moveTo(mid, high);
|
||||
ctx.lineTo(mid, low);
|
||||
ctx.moveTo(mid, open);
|
||||
ctx.lineTo(mid - halfWidth, open);
|
||||
ctx.moveTo(mid, close);
|
||||
ctx.lineTo(mid + halfWidth, close);
|
||||
},
|
||||
|
||||
constructor: function () {
|
||||
this.callSuper(arguments);
|
||||
this.raiseTemplate = new Ext.draw.sprite.Rect({parent: this});
|
||||
this.dropTemplate = new Ext.draw.sprite.Rect({parent: this});
|
||||
},
|
||||
|
||||
getGapWidth: function () {
|
||||
var attr = this.attr,
|
||||
barWidth = attr.barWidth,
|
||||
padding = attr.padding;
|
||||
return barWidth + padding;
|
||||
},
|
||||
|
||||
renderAggregates: function (aggregates, start, end, surface, ctx, clip, region) {
|
||||
var me = this,
|
||||
attr = this.attr,
|
||||
dataX = attr.dataX,
|
||||
matrix = attr.matrix,
|
||||
xx = matrix.getXX(),
|
||||
yy = matrix.getYY(),
|
||||
dx = matrix.getDX(),
|
||||
dy = matrix.getDY(),
|
||||
barWidth = attr.barWidth / xx,
|
||||
template,
|
||||
ohlcType = attr.ohlcType,
|
||||
halfWidth = Math.round(barWidth * 0.5 * xx),
|
||||
opens = aggregates.open,
|
||||
highs = aggregates.high,
|
||||
lows = aggregates.low,
|
||||
closes = aggregates.close,
|
||||
maxYs = aggregates.maxY,
|
||||
minYs = aggregates.minY,
|
||||
startIdxs = aggregates.startIdx,
|
||||
open, high, low, close, mid,
|
||||
i,
|
||||
pixelAdjust = attr.lineWidth * surface.devicePixelRatio / 2;
|
||||
|
||||
pixelAdjust -= Math.floor(pixelAdjust);
|
||||
ctx.save();
|
||||
template = this.raiseTemplate;
|
||||
template.useAttributes(ctx);
|
||||
ctx.beginPath();
|
||||
for (i = start; i < end; i++) {
|
||||
if (opens[i] <= closes[i]) {
|
||||
open = Math.round(opens[i] * yy + dy) + pixelAdjust;
|
||||
high = Math.round(maxYs[i] * yy + dy) + pixelAdjust;
|
||||
low = Math.round(minYs[i] * yy + dy) + pixelAdjust;
|
||||
close = Math.round(closes[i] * yy + dy) + pixelAdjust;
|
||||
mid = Math.round(dataX[startIdxs[i]] * xx + dx) + pixelAdjust;
|
||||
me[ohlcType](ctx, open, high, low, close, mid, halfWidth);
|
||||
}
|
||||
}
|
||||
ctx.fillStroke(template.attr);
|
||||
ctx.restore();
|
||||
|
||||
ctx.save();
|
||||
template = this.dropTemplate;
|
||||
template.useAttributes(ctx);
|
||||
ctx.beginPath();
|
||||
for (i = start; i < end; i++) {
|
||||
if (opens[i] > closes[i]) {
|
||||
open = Math.round(opens[i] * yy + dy) + pixelAdjust;
|
||||
high = Math.round(maxYs[i] * yy + dy) + pixelAdjust;
|
||||
low = Math.round(minYs[i] * yy + dy) + pixelAdjust;
|
||||
close = Math.round(closes[i] * yy + dy) + pixelAdjust;
|
||||
mid = Math.round(dataX[startIdxs[i]] * xx + dx) + pixelAdjust;
|
||||
me[ohlcType](ctx, open, high, low, close, mid, halfWidth);
|
||||
}
|
||||
}
|
||||
ctx.fillStroke(template.attr);
|
||||
ctx.restore();
|
||||
}
|
||||
});
|
284
vendor/touch/src/chart/series/sprite/Cartesian.js
vendored
Normal file
284
vendor/touch/src/chart/series/sprite/Cartesian.js
vendored
Normal file
|
@ -0,0 +1,284 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Cartesian
|
||||
* @extends Ext.draw.sprite.Sprite
|
||||
*
|
||||
* Cartesian sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Cartesian', {
|
||||
extend: 'Ext.draw.sprite.Sprite',
|
||||
mixins: {
|
||||
markerHolder: 'Ext.chart.MarkerHolder'
|
||||
},
|
||||
homogeneous: true,
|
||||
ascending: true,
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Number} [dataMinX=0] Data minimum on the x-axis.
|
||||
*/
|
||||
dataMinX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMaxX=1] Data maximum on the x-axis.
|
||||
*/
|
||||
dataMaxX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMinY=0] Data minimum on the y-axis.
|
||||
*/
|
||||
dataMinY: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMaxY=2] Data maximum on the y-axis.
|
||||
*/
|
||||
dataMaxY: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Array} Data range derived from all the series bound to the x-axis.
|
||||
*/
|
||||
rangeX: 'data',
|
||||
/**
|
||||
* @cfg {Array} Data range derived from all the series bound to the y-axis.
|
||||
*/
|
||||
rangeY: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataY=null] Data items on the y-axis.
|
||||
*/
|
||||
dataY: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataX=null] Data items on the x-axis.
|
||||
*/
|
||||
dataX: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [labels=null] Labels used in the series.
|
||||
*/
|
||||
labels: 'default',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [labelOverflowPadding=10] Padding around labels to determine overlap.
|
||||
*/
|
||||
labelOverflowPadding: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [selectionTolerance=20]
|
||||
* The distance from the event position to the sprite's data points to trigger interactions (used for 'iteminfo', etc).
|
||||
*/
|
||||
selectionTolerance: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} If flipXY is 'true', the series is flipped.
|
||||
*/
|
||||
flipXY: 'bool',
|
||||
|
||||
renderer: 'default',
|
||||
|
||||
// PanZoom information
|
||||
visibleMinX: 'number',
|
||||
visibleMinY: 'number',
|
||||
visibleMaxX: 'number',
|
||||
visibleMaxY: 'number',
|
||||
innerWidth: 'number',
|
||||
innerHeight: 'number'
|
||||
},
|
||||
defaults: {
|
||||
dataY: null,
|
||||
dataX: null,
|
||||
dataMinX: 0,
|
||||
dataMaxX: 1,
|
||||
dataMinY: 0,
|
||||
dataMaxY: 1,
|
||||
labels: null,
|
||||
labelOverflowPadding: 10,
|
||||
selectionTolerance: 20,
|
||||
flipXY: false,
|
||||
renderer: null,
|
||||
transformFillStroke: false,
|
||||
|
||||
visibleMinX: 0,
|
||||
visibleMinY: 0,
|
||||
visibleMaxX: 1,
|
||||
visibleMaxY: 1,
|
||||
innerWidth: 1,
|
||||
innerHeight: 1
|
||||
},
|
||||
dirtyTriggers: {
|
||||
dataX: 'dataX,bbox',
|
||||
dataY: 'dataY,bbox',
|
||||
dataMinX: 'bbox',
|
||||
dataMaxX: 'bbox',
|
||||
dataMinY: 'bbox',
|
||||
dataMaxY: 'bbox',
|
||||
visibleMinX: 'panzoom',
|
||||
visibleMinY: 'panzoom',
|
||||
visibleMaxX: 'panzoom',
|
||||
visibleMaxY: 'panzoom',
|
||||
innerWidth: 'panzoom',
|
||||
innerHeight: 'panzoom'
|
||||
},
|
||||
updaters: {
|
||||
dataX: function (attrs) {
|
||||
this.processDataX();
|
||||
if (!attrs.dirtyFlags.dataY) {
|
||||
attrs.dirtyFlags.dataY = [];
|
||||
}
|
||||
attrs.dirtyFlags.dataY.push('dataY');
|
||||
},
|
||||
|
||||
dataY: function () {
|
||||
this.processDataY();
|
||||
},
|
||||
|
||||
panzoom: function (attrs) {
|
||||
var dx = attrs.visibleMaxX - attrs.visibleMinX,
|
||||
dy = attrs.visibleMaxY - attrs.visibleMinY,
|
||||
innerWidth = attrs.flipXY ? attrs.innerHeight : attrs.innerWidth,
|
||||
innerHeight = !attrs.flipXY ? attrs.innerHeight : attrs.innerWidth;
|
||||
attrs.translationX = -attrs.visibleMinX * innerWidth / dx;
|
||||
attrs.translationY = -attrs.visibleMinY * innerHeight / dy;
|
||||
attrs.scalingX = innerWidth / dx;
|
||||
attrs.scalingY = innerHeight / dy;
|
||||
attrs.scalingCenterX = 0;
|
||||
attrs.scalingCenterY = 0;
|
||||
this.applyTransformations(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} store The store that is passed to the renderer.
|
||||
*/
|
||||
store: null,
|
||||
|
||||
/**
|
||||
* @cfg {String} field The store field used by the series.
|
||||
*/
|
||||
field: null
|
||||
},
|
||||
|
||||
processDataY: Ext.emptyFn,
|
||||
|
||||
processDataX: Ext.emptyFn,
|
||||
|
||||
updatePlainBBox: function (plain) {
|
||||
var attr = this.attr;
|
||||
plain.x = attr.dataMinX;
|
||||
plain.y = attr.dataMinY;
|
||||
plain.width = attr.dataMaxX - attr.dataMinX;
|
||||
plain.height = attr.dataMaxY - attr.dataMinY;
|
||||
},
|
||||
|
||||
/**
|
||||
* Does a binary search of the data on the x-axis using the given key.
|
||||
* @param {String} key
|
||||
* @return {*}
|
||||
*/
|
||||
binarySearch: function (key) {
|
||||
var dx = this.attr.dataX,
|
||||
start = 0,
|
||||
end = dx.length;
|
||||
if (key <= dx[0]) {
|
||||
return start;
|
||||
}
|
||||
if (key >= dx[end - 1]) {
|
||||
return end - 1;
|
||||
}
|
||||
while (start + 1 < end) {
|
||||
var mid = (start + end) >> 1,
|
||||
val = dx[mid];
|
||||
if (val === key) {
|
||||
return mid;
|
||||
} else if (val < key) {
|
||||
start = mid;
|
||||
} else {
|
||||
end = mid;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
},
|
||||
|
||||
render: function (surface, ctx, region) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
flipXY = attr.flipXY,
|
||||
inverseMatrix = attr.inverseMatrix.clone();
|
||||
|
||||
inverseMatrix.appendMatrix(surface.inverseMatrix);
|
||||
|
||||
if (attr.dataX === null) {
|
||||
return;
|
||||
}
|
||||
if (attr.dataY === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inverseMatrix.getXX() * inverseMatrix.getYX() || inverseMatrix.getXY() * inverseMatrix.getYY()) {
|
||||
console.log('Cartesian Series sprite does not support rotation/sheering');
|
||||
return;
|
||||
}
|
||||
|
||||
var clip = inverseMatrix.transformList([
|
||||
[region[0] - 1, region[3] + 1],
|
||||
[region[0] + region[2] + 1, -1]
|
||||
]);
|
||||
|
||||
clip = clip[0].concat(clip[1]);
|
||||
|
||||
if (clip[2] < clip[0]) {
|
||||
console.log('Cartesian Series sprite does not supports flipped X.');
|
||||
// TODO: support it
|
||||
return;
|
||||
}
|
||||
me.renderClipped(surface, ctx, clip, region);
|
||||
},
|
||||
|
||||
/**
|
||||
* Render the given visible clip range.
|
||||
* @param {Ext.draw.Surface} surface
|
||||
* @param {Ext.draw.engine.Canvas/Ext.draw.engine.SvgContext} ctx
|
||||
* @param {Array} clip
|
||||
* @param {Arrary} region
|
||||
*/
|
||||
renderClipped: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Get the nearest item index from point (x, y). -1 as not found.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @return {Number} The index
|
||||
*/
|
||||
getIndexNearPoint: function (x, y) {
|
||||
var sprite = this,
|
||||
mat = sprite.attr.matrix,
|
||||
dataX = sprite.attr.dataX,
|
||||
dataY = sprite.attr.dataY,
|
||||
selectionTolerance = sprite.attr.selectionTolerance,
|
||||
minX, minY, index = -1,
|
||||
imat = mat.clone().prependMatrix(this.surfaceMatrix).inverse(),
|
||||
center = imat.transformPoint([x, y]),
|
||||
positionLB = imat.transformPoint([x - selectionTolerance, y - selectionTolerance]),
|
||||
positionTR = imat.transformPoint([x + selectionTolerance, y + selectionTolerance]),
|
||||
left = Math.min(positionLB[0], positionTR[0]),
|
||||
right = Math.max(positionLB[0], positionTR[0]),
|
||||
top = Math.min(positionLB[1], positionTR[1]),
|
||||
bottom = Math.max(positionLB[1], positionTR[1]);
|
||||
|
||||
for (var i = 0; i < dataX.length; i++) {
|
||||
if (left < dataX[i] && dataX[i] < right && top < dataY[i] && dataY[i] < bottom) {
|
||||
if (index === -1 || (Math.abs(dataX[i] - center[0]) < minX) && (Math.abs(dataY[i] - center[1]) < minY)) {
|
||||
minX = Math.abs(dataX[i] - center[0]);
|
||||
minY = Math.abs(dataY[i] - center[1]);
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
});
|
387
vendor/touch/src/chart/series/sprite/Line.js
vendored
Normal file
387
vendor/touch/src/chart/series/sprite/Line.js
vendored
Normal file
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Line
|
||||
* @extends Ext.chart.series.sprite.Aggregative
|
||||
*
|
||||
* Line series sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Line', {
|
||||
alias: 'sprite.lineSeries',
|
||||
extend: 'Ext.chart.series.sprite.Aggregative',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
smooth: 'bool',
|
||||
fillArea: 'bool',
|
||||
step: 'bool',
|
||||
preciseStroke: 'bool'
|
||||
},
|
||||
|
||||
defaults: {
|
||||
/**
|
||||
* @cfg {Boolean} smooth 'true' if the sprite uses line smoothing.
|
||||
*/
|
||||
smooth: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} fillArea 'true' if the sprite paints the area underneath the line.
|
||||
*/
|
||||
fillArea: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} step 'true' if the line uses steps instead of straight lines to connect the dots.
|
||||
* It is ignored if `smooth` is true.
|
||||
*/
|
||||
step: false,
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} preciseStroke 'true' if the line uses precise stroke.
|
||||
*/
|
||||
preciseStroke: true
|
||||
},
|
||||
|
||||
dirtyTriggers: {
|
||||
dataX: 'dataX,bbox,smooth',
|
||||
dataY: 'dataY,bbox,smooth',
|
||||
smooth: 'smooth'
|
||||
},
|
||||
|
||||
updaters: {
|
||||
smooth: function (attr) {
|
||||
if (attr.smooth && attr.dataX && attr.dataY && attr.dataX.length > 2 && attr.dataY.length > 2) {
|
||||
this.smoothX = Ext.draw.Draw.spline(attr.dataX);
|
||||
this.smoothY = Ext.draw.Draw.spline(attr.dataY);
|
||||
} else {
|
||||
delete this.smoothX;
|
||||
delete this.smoothY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
list: null,
|
||||
|
||||
updatePlainBBox: function (plain) {
|
||||
var attr = this.attr,
|
||||
ymin = Math.min(0, attr.dataMinY),
|
||||
ymax = Math.max(0, attr.dataMaxY);
|
||||
plain.x = attr.dataMinX;
|
||||
plain.y = ymin;
|
||||
plain.width = attr.dataMaxX - attr.dataMinX;
|
||||
plain.height = ymax - ymin;
|
||||
},
|
||||
|
||||
drawStroke: function (surface, ctx, start, end, list, xAxis) {
|
||||
var attr = this.attr,
|
||||
matrix = attr.matrix,
|
||||
xx = matrix.getXX(),
|
||||
yy = matrix.getYY(),
|
||||
dx = matrix.getDX(),
|
||||
dy = matrix.getDY(),
|
||||
smooth = attr.smooth,
|
||||
step = attr.step,
|
||||
scale = Math.pow(2, power(attr.dataX.length, end)),
|
||||
smoothX = this.smoothX,
|
||||
smoothY = this.smoothY,
|
||||
i, j, lineConfig, changes,
|
||||
cx1, cy1, cx2, cy2, x, y, x0, y0, saveOpacity;
|
||||
|
||||
function power(count, end) {
|
||||
var power = 0,
|
||||
n = count;
|
||||
while (n > 0 && n < end) {
|
||||
power++;
|
||||
n += count >> power;
|
||||
}
|
||||
return power > 0 ? power - 1 : power;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
if (smooth && smoothX && smoothY) {
|
||||
ctx.moveTo(smoothX[start * 3] * xx + dx, smoothY[start * 3] * yy + dy);
|
||||
for (i = 0, j = start * 3 + 1; i < list.length - 3; i += 3, j += 3 * scale) {
|
||||
cx1 = smoothX[j] * xx + dx;
|
||||
cy1 = smoothY[j] * yy + dy;
|
||||
cx2 = smoothX[j + 1] * xx + dx;
|
||||
cy2 = smoothY[j + 1] * yy + dy;
|
||||
x = list[i + 3];
|
||||
y = list[i + 4];
|
||||
x0 = list[i];
|
||||
y0 = list[i + 1];
|
||||
if (attr.renderer) {
|
||||
lineConfig = {
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
step: step,
|
||||
cx1: cx1,
|
||||
cy1: cy1,
|
||||
cx2: cx2,
|
||||
cy2: cy2,
|
||||
x: x,
|
||||
y: y,
|
||||
x0: x0,
|
||||
y0: y0
|
||||
};
|
||||
changes = attr.renderer.call(this, this, lineConfig, {store:this.getStore()}, (i/3 + 1));
|
||||
ctx.save();
|
||||
Ext.apply(ctx, changes);
|
||||
// Fill the area if we need to, using the fill color and transparent strokes.
|
||||
if (attr.fillArea) {
|
||||
saveOpacity = ctx.strokeOpacity;
|
||||
ctx.save();
|
||||
ctx.strokeOpacity = 0;
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x, y);
|
||||
ctx.lineTo(x, xAxis);
|
||||
ctx.lineTo(x0, xAxis);
|
||||
ctx.lineTo(x0, y0);
|
||||
ctx.closePath();
|
||||
ctx.fillStroke(attr, true);
|
||||
ctx.restore();
|
||||
ctx.strokeOpacity = saveOpacity;
|
||||
ctx.beginPath();
|
||||
}
|
||||
// Draw the line on top of the filled area.
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x, y);
|
||||
ctx.moveTo(x0, y0);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x, y);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.moveTo(list[0], list[1]);
|
||||
for (i = 3; i < list.length; i += 3) {
|
||||
x = list[i];
|
||||
y = list[i + 1];
|
||||
x0 = list[i - 3];
|
||||
y0 = list[i - 2];
|
||||
if (attr.renderer) {
|
||||
lineConfig = {
|
||||
type: 'line',
|
||||
smooth: false,
|
||||
step: step,
|
||||
x: x,
|
||||
y: y,
|
||||
x0: x0,
|
||||
y0: y0
|
||||
};
|
||||
changes = attr.renderer.call(this, this, lineConfig, {store:this.getStore()}, i/3);
|
||||
ctx.save();
|
||||
Ext.apply(ctx, changes);
|
||||
// Fill the area if we need to, using the fill color and transparent strokes.
|
||||
if (attr.fillArea) {
|
||||
saveOpacity = ctx.strokeOpacity;
|
||||
ctx.save();
|
||||
ctx.strokeOpacity = 0;
|
||||
if (step) {
|
||||
ctx.lineTo(x, y0);
|
||||
} else {
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
ctx.lineTo(x, xAxis);
|
||||
ctx.lineTo(x0, xAxis);
|
||||
ctx.lineTo(x0, y0);
|
||||
ctx.closePath();
|
||||
ctx.fillStroke(attr, true);
|
||||
ctx.restore();
|
||||
ctx.strokeOpacity = saveOpacity;
|
||||
ctx.beginPath();
|
||||
}
|
||||
// Draw the line (or the 2 lines if 'step') on top of the filled area.
|
||||
ctx.moveTo(x0, y0);
|
||||
if (step) {
|
||||
ctx.lineTo(x, y0);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y0);
|
||||
}
|
||||
ctx.lineTo(x, y);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y);
|
||||
} else {
|
||||
if (step) {
|
||||
ctx.lineTo(x, y0);
|
||||
}
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
drawLabel: function (text, dataX, dataY, labelId, region) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
label = me.getBoundMarker('labels')[0],
|
||||
labelTpl = label.getTemplate(),
|
||||
labelCfg = me.labelCfg || (me.labelCfg = {}),
|
||||
surfaceMatrix = me.surfaceMatrix,
|
||||
labelX, labelY,
|
||||
labelOverflowPadding = attr.labelOverflowPadding,
|
||||
halfWidth, halfHeight,
|
||||
labelBox,
|
||||
changes;
|
||||
|
||||
labelCfg.text = text;
|
||||
|
||||
labelBox = this.getMarkerBBox('labels', labelId, true);
|
||||
if (!labelBox) {
|
||||
me.putMarker('labels', labelCfg, labelId);
|
||||
labelBox = this.getMarkerBBox('labels', labelId, true);
|
||||
}
|
||||
|
||||
if (attr.flipXY) {
|
||||
labelCfg.rotationRads = Math.PI * 0.5;
|
||||
} else {
|
||||
labelCfg.rotationRads = 0;
|
||||
}
|
||||
|
||||
halfWidth = labelBox.width / 2;
|
||||
halfHeight = labelBox.height / 2;
|
||||
|
||||
labelX = dataX;
|
||||
if (labelTpl.attr.display === 'over') {
|
||||
labelY = dataY + halfHeight + labelOverflowPadding;
|
||||
} else {
|
||||
labelY = dataY - halfHeight - labelOverflowPadding;
|
||||
}
|
||||
|
||||
if (labelX <= region[0] + halfWidth) {
|
||||
labelX = region[0] + halfWidth;
|
||||
} else if (labelX >= region[2] - halfWidth) {
|
||||
labelX = region[2] - halfWidth;
|
||||
}
|
||||
|
||||
if (labelY <= region[1] + halfHeight) {
|
||||
labelY = region[1] + halfHeight;
|
||||
} else if (labelY >= region[3] - halfHeight) {
|
||||
labelY = region[3] - halfHeight;
|
||||
}
|
||||
|
||||
labelCfg.x = surfaceMatrix.x(labelX, labelY);
|
||||
labelCfg.y = surfaceMatrix.y(labelX, labelY);
|
||||
|
||||
if (labelTpl.attr.renderer) {
|
||||
changes = labelTpl.attr.renderer.call(this, text, label, labelCfg, {store: this.getStore()}, labelId);
|
||||
if (typeof changes === 'string') {
|
||||
labelCfg.text = changes;
|
||||
} else {
|
||||
Ext.apply(labelCfg, changes);
|
||||
}
|
||||
}
|
||||
|
||||
me.putMarker('labels', labelCfg, labelId);
|
||||
},
|
||||
|
||||
renderAggregates: function (aggregates, start, end, surface, ctx, clip, region) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
labels = attr.labels,
|
||||
drawLabels = labels && !!me.getBoundMarker('labels'),
|
||||
matrix = attr.matrix,
|
||||
surfaceMatrix = surface.matrix,
|
||||
pixel = surface.devicePixelRatio,
|
||||
xx = matrix.getXX(),
|
||||
yy = matrix.getYY(),
|
||||
dx = matrix.getDX(),
|
||||
dy = matrix.getDY(),
|
||||
markerCfg = {},
|
||||
list = this.list || (this.list = []),
|
||||
x, y, i, index,
|
||||
minXs = aggregates.minX,
|
||||
maxXs = aggregates.maxX,
|
||||
minYs = aggregates.minY,
|
||||
maxYs = aggregates.maxY,
|
||||
idx = aggregates.startIdx;
|
||||
|
||||
list.length = 0;
|
||||
for (i = start; i < end; i++) {
|
||||
var minX = minXs[i],
|
||||
maxX = maxXs[i],
|
||||
minY = minYs[i],
|
||||
maxY = maxYs[i];
|
||||
|
||||
if (minX < maxX) {
|
||||
list.push(minX * xx + dx, minY * yy + dy, idx[i]);
|
||||
list.push(maxX * xx + dx, maxY * yy + dy, idx[i]);
|
||||
} else if (minX > maxX) {
|
||||
list.push(maxX * xx + dx, maxY * yy + dy, idx[i]);
|
||||
list.push(minX * xx + dx, minY * yy + dy, idx[i]);
|
||||
} else {
|
||||
list.push(maxX * xx + dx, maxY * yy + dy, idx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (list.length) {
|
||||
for (i = 0; i < list.length; i += 3) {
|
||||
x = list[i];
|
||||
y = list[i + 1];
|
||||
index = list[i + 2];
|
||||
if (attr.renderer) {
|
||||
markerCfg = {
|
||||
type: 'marker',
|
||||
x: x,
|
||||
y: y
|
||||
};
|
||||
markerCfg = attr.renderer.call(this, this, markerCfg, {store:this.getStore()}, i/3) || {};
|
||||
}
|
||||
markerCfg.translationX = surfaceMatrix.x(x, y);
|
||||
markerCfg.translationY = surfaceMatrix.y(x, y);
|
||||
me.putMarker('markers', markerCfg, index, !attr.renderer);
|
||||
|
||||
if (drawLabels && labels[index]) {
|
||||
me.drawLabel(labels[index], x, y, index, region);
|
||||
}
|
||||
}
|
||||
me.drawStroke(surface, ctx, start, end, list, region[1] - pixel);
|
||||
if (!attr.renderer) {
|
||||
var lastPointX = dataX[dataX.length - 1] * xx + dx + pixel,
|
||||
lastPointY = dataY[dataY.length - 1] * yy + dy,
|
||||
bottomY = region[1] - pixel,
|
||||
firstPointX = dataX[0] * xx + dx - pixel,
|
||||
firstPointY = dataY[0] * yy + dy;
|
||||
ctx.lineTo(lastPointX, lastPointY);
|
||||
ctx.lineTo(lastPointX, bottomY);
|
||||
ctx.lineTo(firstPointX, bottomY);
|
||||
ctx.lineTo(firstPointX, firstPointY);
|
||||
}
|
||||
ctx.closePath();
|
||||
|
||||
if (attr.transformFillStroke) {
|
||||
attr.matrix.toContext(ctx);
|
||||
}
|
||||
if (attr.preciseStroke) {
|
||||
if (attr.fillArea) {
|
||||
ctx.fill();
|
||||
}
|
||||
if (attr.transformFillStroke) {
|
||||
attr.inverseMatrix.toContext(ctx);
|
||||
}
|
||||
me.drawStroke(surface, ctx, start, end, list, region[1] - pixel);
|
||||
if (attr.transformFillStroke) {
|
||||
attr.matrix.toContext(ctx);
|
||||
}
|
||||
ctx.stroke();
|
||||
} else {
|
||||
// Prevent the reverse transform to fix floating point err.
|
||||
if (attr.fillArea) {
|
||||
ctx.fillStroke(attr, true);
|
||||
} else {
|
||||
ctx.stroke(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
377
vendor/touch/src/chart/series/sprite/Pie3DPart.js
vendored
Normal file
377
vendor/touch/src/chart/series/sprite/Pie3DPart.js
vendored
Normal file
|
@ -0,0 +1,377 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Pie3DPart
|
||||
* @extends Ext.draw.sprite.Path
|
||||
*
|
||||
* Pie3D series sprite.
|
||||
*/
|
||||
Ext.define("Ext.chart.series.sprite.Pie3DPart", {
|
||||
extend: 'Ext.draw.sprite.Path',
|
||||
mixins: {
|
||||
markerHolder: "Ext.chart.MarkerHolder"
|
||||
},
|
||||
alias: 'sprite.pie3dPart',
|
||||
type: 'pie3dPart',
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Number} [centerX=0] The central point of the series on the x-axis.
|
||||
*/
|
||||
centerX: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [centerY=0] The central point of the series on the x-axis.
|
||||
*/
|
||||
centerY: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [startAngle=0] The starting angle of the polar series.
|
||||
*/
|
||||
startAngle: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [endAngle=Math.PI] The ending angle of the polar series.
|
||||
*/
|
||||
endAngle: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [startRho=0] The starting radius of the polar series.
|
||||
*/
|
||||
startRho: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [endRho=150] The ending radius of the polar series.
|
||||
*/
|
||||
endRho: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [margin=0] Margin from the center of the pie. Used for donut.
|
||||
*/
|
||||
margin: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [thickness=0] The thickness of the 3D pie part.
|
||||
*/
|
||||
thickness: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [distortion=0] The distortion of the 3D pie part.
|
||||
*/
|
||||
distortion: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Object} [baseColor='white'] The color of the 3D pie part before adding the 3D effect.
|
||||
*/
|
||||
baseColor: "color",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [baseRotation=0] The starting rotation of the polar series.
|
||||
*/
|
||||
baseRotation: "number",
|
||||
|
||||
/**
|
||||
* @cfg {String} [part=0] The part of the 3D Pie represented by the sprite.
|
||||
*/
|
||||
part: "enums(top,start,end,inner,outer)"
|
||||
},
|
||||
aliases: {
|
||||
rho: 'endRho'
|
||||
},
|
||||
dirtyTriggers: {
|
||||
centerX: "path,bbox",
|
||||
centerY: "path,bbox",
|
||||
startAngle: "path,partZIndex",
|
||||
endAngle: "path,partZIndex",
|
||||
startRho: "path",
|
||||
endRho: "path,bbox",
|
||||
margin: "path,bbox",
|
||||
thickness: "path",
|
||||
baseRotation: "path,partZIndex,partColor",
|
||||
baseColor: 'partZIndex,partColor',
|
||||
part: "path,partZIndex"
|
||||
},
|
||||
defaults: {
|
||||
centerX: 0,
|
||||
centerY: 0,
|
||||
startAngle: 0,
|
||||
endAngle: 0,
|
||||
startRho: 0,
|
||||
endRho: 150,
|
||||
margin: 0,
|
||||
distortion: 1,
|
||||
baseRotation: 0,
|
||||
baseColor: 'white',
|
||||
part: "top"
|
||||
},
|
||||
updaters: {
|
||||
"partColor": function (attrs) {
|
||||
var color = Ext.draw.Color.fly(attrs.baseColor),
|
||||
fillStyle;
|
||||
switch (attrs.part) {
|
||||
case 'top':
|
||||
fillStyle = color.toString();
|
||||
break;
|
||||
case 'outer':
|
||||
fillStyle = Ext.create("Ext.draw.gradient.Linear", {
|
||||
type: 'linear',
|
||||
stops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: color.createDarker(0.3).toString()
|
||||
},
|
||||
{
|
||||
offset: 0.3,
|
||||
color: color.toString()
|
||||
},
|
||||
{
|
||||
offset: 0.8,
|
||||
color: color.createLighter(0.2).toString()
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: color.createDarker(0.4).toString()
|
||||
}
|
||||
]
|
||||
});
|
||||
break;
|
||||
case 'start':
|
||||
fillStyle = color.createDarker(0.3).toString();
|
||||
break;
|
||||
case 'end':
|
||||
fillStyle = color.createDarker(0.3).toString();
|
||||
break;
|
||||
case 'inner':
|
||||
fillStyle = Ext.create("Ext.draw.gradient.Linear", {
|
||||
type: 'linear',
|
||||
stops: [
|
||||
{
|
||||
offset: 0,
|
||||
color: color.createDarker(0.4).toString()
|
||||
},
|
||||
{
|
||||
offset: 0.2,
|
||||
color: color.createLighter(0.2).toString()
|
||||
},
|
||||
{
|
||||
offset: 0.7,
|
||||
color: color.toString()
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: color.createDarker(0.3).toString()
|
||||
}
|
||||
]
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
attrs.fillStyle = fillStyle;
|
||||
attrs.canvasAttributes.fillStyle = fillStyle;
|
||||
},
|
||||
"partZIndex": function (attrs) {
|
||||
var rotation = attrs.baseRotation;
|
||||
switch (attrs.part) {
|
||||
case 'top':
|
||||
attrs.zIndex = 5;
|
||||
break;
|
||||
case 'outer':
|
||||
attrs.zIndex = 4;
|
||||
break;
|
||||
case 'start':
|
||||
attrs.zIndex = 1 + Math.sin(attrs.startAngle + rotation);
|
||||
break;
|
||||
case 'end':
|
||||
attrs.zIndex = 1 + Math.sin(attrs.endAngle + rotation);
|
||||
break;
|
||||
case 'inner':
|
||||
attrs.zIndex = 1;
|
||||
break;
|
||||
}
|
||||
attrs.dirtyZIndex = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updatePlainBBox: function (plain) {
|
||||
var attr = this.attr,
|
||||
rho = attr.part === 'inner' ? attr.startRho : attr.endRho;
|
||||
plain.width = rho * 2;
|
||||
plain.height = rho * attr.distortion * 2 + attr.thickness;
|
||||
plain.x = attr.centerX - rho;
|
||||
plain.y = attr.centerY - rho * attr.distortion;
|
||||
},
|
||||
|
||||
updateTransformedBBox: function (transform) {
|
||||
return this.updatePlainBBox(transform);
|
||||
},
|
||||
|
||||
updatePath: function (path) {
|
||||
if (this.attr.endAngle < this.attr.startAngle) {
|
||||
return;
|
||||
}
|
||||
this[this.attr.part + 'Renderer'](path);
|
||||
},
|
||||
|
||||
topRenderer: function (path) {
|
||||
var attr = this.attr,
|
||||
margin = attr.margin,
|
||||
distortion = attr.distortion,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
baseRotation = attr.baseRotation,
|
||||
startAngle = attr.startAngle + baseRotation ,
|
||||
endAngle = attr.endAngle + baseRotation ,
|
||||
startRho = attr.startRho,
|
||||
endRho = attr.endRho,
|
||||
midAngle,
|
||||
sinEnd = Math.sin(endAngle),
|
||||
cosEnd = Math.cos(endAngle);
|
||||
midAngle = (startAngle + endAngle) * 0.5;
|
||||
centerX += Math.cos(midAngle) * margin;
|
||||
centerY += Math.sin(midAngle) * margin * distortion;
|
||||
path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, startAngle, endAngle, false);
|
||||
path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion);
|
||||
path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, endAngle, startAngle, true);
|
||||
path.closePath();
|
||||
},
|
||||
|
||||
startRenderer: function (path) {
|
||||
var attr = this.attr,
|
||||
margin = attr.margin,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
distortion = attr.distortion,
|
||||
baseRotation = attr.baseRotation,
|
||||
startAngle = attr.startAngle + baseRotation ,
|
||||
endAngle = attr.endAngle + baseRotation,
|
||||
thickness = attr.thickness,
|
||||
startRho = attr.startRho,
|
||||
endRho = attr.endRho,
|
||||
sinStart = Math.sin(startAngle),
|
||||
cosStart = Math.cos(startAngle),
|
||||
midAngle;
|
||||
if (cosStart < 0) {
|
||||
midAngle = (startAngle + endAngle) * 0.5;
|
||||
centerX += Math.cos(midAngle) * margin;
|
||||
centerY += Math.sin(midAngle) * margin * distortion;
|
||||
path.moveTo(centerX + cosStart * startRho, centerY + sinStart * startRho * distortion);
|
||||
path.lineTo(centerX + cosStart * endRho, centerY + sinStart * endRho * distortion);
|
||||
path.lineTo(centerX + cosStart * endRho, centerY + sinStart * endRho * distortion + thickness);
|
||||
path.lineTo(centerX + cosStart * startRho, centerY + sinStart * startRho * distortion + thickness);
|
||||
path.closePath();
|
||||
}
|
||||
},
|
||||
|
||||
endRenderer: function (path) {
|
||||
var attr = this.attr,
|
||||
margin = attr.margin,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
distortion = attr.distortion,
|
||||
baseRotation = attr.baseRotation,
|
||||
startAngle = attr.startAngle + baseRotation ,
|
||||
endAngle = attr.endAngle + baseRotation,
|
||||
thickness = attr.thickness,
|
||||
startRho = attr.startRho,
|
||||
endRho = attr.endRho,
|
||||
sin = Math.sin(endAngle),
|
||||
cos = Math.cos(endAngle), midAngle;
|
||||
if (cos > 0) {
|
||||
midAngle = (startAngle + endAngle) * 0.5;
|
||||
centerX += Math.cos(midAngle) * margin;
|
||||
centerY += Math.sin(midAngle) * margin * distortion;
|
||||
path.moveTo(centerX + cos * startRho, centerY + sin * startRho * distortion);
|
||||
path.lineTo(centerX + cos * endRho, centerY + sin * endRho * distortion);
|
||||
path.lineTo(centerX + cos * endRho, centerY + sin * endRho * distortion + thickness);
|
||||
path.lineTo(centerX + cos * startRho, centerY + sin * startRho * distortion + thickness);
|
||||
path.closePath();
|
||||
}
|
||||
},
|
||||
|
||||
innerRenderer: function (path) {
|
||||
var attr = this.attr,
|
||||
margin = attr.margin,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
distortion = attr.distortion,
|
||||
baseRotation = attr.baseRotation,
|
||||
startAngle = attr.startAngle + baseRotation ,
|
||||
endAngle = attr.endAngle + baseRotation,
|
||||
thickness = attr.thickness,
|
||||
startRho = attr.startRho,
|
||||
sinEnd, cosEnd,
|
||||
tempStart, tempEnd, midAngle;
|
||||
midAngle = (startAngle + endAngle) * 0.5;
|
||||
centerX += Math.cos(midAngle) * margin;
|
||||
centerY += Math.sin(midAngle) * margin * distortion;
|
||||
if (startAngle >= Math.PI * 2) {
|
||||
startAngle -= Math.PI * 2;
|
||||
endAngle -= Math.PI * 2;
|
||||
}
|
||||
if (endAngle > Math.PI && endAngle < Math.PI * 3) {
|
||||
tempStart = startAngle;
|
||||
tempEnd = Math.min(endAngle, Math.PI * 2);
|
||||
sinEnd = Math.sin(tempEnd);
|
||||
cosEnd = Math.cos(tempEnd);
|
||||
path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, tempStart, tempEnd, false);
|
||||
path.lineTo(centerX + cosEnd * startRho, centerY + sinEnd * startRho * distortion + thickness);
|
||||
path.ellipse(centerX, centerY + thickness, startRho, startRho * distortion, 0, tempEnd, tempStart, true);
|
||||
path.closePath();
|
||||
}
|
||||
if (endAngle > Math.PI * 3) {
|
||||
tempStart = Math.PI;
|
||||
tempEnd = endAngle;
|
||||
sinEnd = Math.sin(tempEnd);
|
||||
cosEnd = Math.cos(tempEnd);
|
||||
path.ellipse(centerX, centerY, startRho, startRho * distortion, 0, tempStart, tempEnd, false);
|
||||
path.lineTo(centerX + cosEnd * startRho, centerY + sinEnd * startRho * distortion + thickness);
|
||||
path.ellipse(centerX, centerY + thickness, startRho, startRho * distortion, 0, tempEnd, tempStart, true);
|
||||
path.closePath();
|
||||
}
|
||||
},
|
||||
|
||||
outerRenderer: function (path) {
|
||||
var attr = this.attr,
|
||||
margin = attr.margin,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
distortion = attr.distortion,
|
||||
baseRotation = attr.baseRotation,
|
||||
startAngle = attr.startAngle + baseRotation ,
|
||||
endAngle = attr.endAngle + baseRotation,
|
||||
thickness = attr.thickness,
|
||||
endRho = attr.endRho,
|
||||
sinEnd, cosEnd,
|
||||
tempStart, tempEnd, midAngle;
|
||||
midAngle = (startAngle + endAngle) * 0.5;
|
||||
centerX += Math.cos(midAngle) * margin;
|
||||
centerY += Math.sin(midAngle) * margin * distortion;
|
||||
|
||||
if (startAngle >= Math.PI * 2) {
|
||||
startAngle -= Math.PI * 2;
|
||||
endAngle -= Math.PI * 2;
|
||||
}
|
||||
|
||||
if (startAngle < Math.PI) {
|
||||
tempStart = startAngle;
|
||||
tempEnd = Math.min(endAngle, Math.PI);
|
||||
sinEnd = Math.sin(tempEnd);
|
||||
cosEnd = Math.cos(tempEnd);
|
||||
path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, tempStart, tempEnd, false);
|
||||
path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion + thickness);
|
||||
path.ellipse(centerX, centerY + thickness, endRho, endRho * distortion, 0, tempEnd, tempStart, true);
|
||||
path.closePath();
|
||||
}
|
||||
if (endAngle > Math.PI * 2) {
|
||||
tempStart = Math.max(startAngle, Math.PI * 2);
|
||||
tempEnd = endAngle;
|
||||
sinEnd = Math.sin(tempEnd);
|
||||
cosEnd = Math.cos(tempEnd);
|
||||
path.ellipse(centerX, centerY, endRho, endRho * distortion, 0, tempStart, tempEnd, false);
|
||||
path.lineTo(centerX + cosEnd * endRho, centerY + sinEnd * endRho * distortion + thickness);
|
||||
path.ellipse(centerX, centerY + thickness, endRho, endRho * distortion, 0, tempEnd, tempStart, true);
|
||||
path.closePath();
|
||||
}
|
||||
}
|
||||
});
|
180
vendor/touch/src/chart/series/sprite/PieSlice.js
vendored
Normal file
180
vendor/touch/src/chart/series/sprite/PieSlice.js
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.PieSlice
|
||||
*
|
||||
* Pie slice sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.PieSlice', {
|
||||
alias: 'sprite.pieslice',
|
||||
mixins: {
|
||||
markerHolder: 'Ext.chart.MarkerHolder'
|
||||
},
|
||||
extend: 'Ext.draw.sprite.Sector',
|
||||
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Boolean} [doCallout=true] 'true' if the pie series uses label callouts.
|
||||
*/
|
||||
doCallout: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {Boolean} [rotateLabels=true] 'true' if the labels are rotated for easier reading.
|
||||
*/
|
||||
rotateLabels: 'bool',
|
||||
|
||||
/**
|
||||
* @cfg {String} [label=''] Label associated with the Pie sprite.
|
||||
*/
|
||||
label: 'string',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [labelOverflowPadding=10] Padding around labels to determine overlap.
|
||||
* Any negative number allows the labels to overlap.
|
||||
*/
|
||||
labelOverflowPadding: 'number',
|
||||
|
||||
renderer: 'default'
|
||||
},
|
||||
defaults: {
|
||||
doCallout: true,
|
||||
rotateLabels: true,
|
||||
label: '',
|
||||
labelOverflowPadding: 10,
|
||||
renderer: null
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} rendererData The object that is passed to the renderer.
|
||||
*
|
||||
* For instance when the PieSlice sprite is used in a Gauge chart, the object
|
||||
* contains the 'store' and 'field' properties, and the 'value' as well
|
||||
* for that one PieSlice that is used to draw the needle of the Gauge.
|
||||
*/
|
||||
rendererData: null,
|
||||
rendererIndex: 0
|
||||
},
|
||||
|
||||
render: function (ctx, surface, clipRegion) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
itemCfg = {},
|
||||
changes;
|
||||
|
||||
if (attr.renderer) {
|
||||
itemCfg = {
|
||||
type: 'sector',
|
||||
text: attr.text,
|
||||
centerX: attr.centerX,
|
||||
centerY: attr.centerY,
|
||||
margin: attr.margin,
|
||||
startAngle: Math.min(attr.startAngle, attr.endAngle),
|
||||
endAngle: Math.max(attr.startAngle, attr.endAngle),
|
||||
startRho: Math.min(attr.startRho, attr.endRho),
|
||||
endRho: Math.max(attr.startRho, attr.endRho)
|
||||
};
|
||||
changes = attr.renderer.call(me, me, itemCfg, me.rendererData, me.rendererIndex);
|
||||
Ext.apply(me.attr, changes);
|
||||
}
|
||||
|
||||
// Draw the sector
|
||||
me.callSuper(arguments);
|
||||
|
||||
// Draw the labels
|
||||
if (attr.label && me.getBoundMarker('labels')) {
|
||||
me.placeLabel();
|
||||
}
|
||||
},
|
||||
|
||||
placeLabel: function () {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
startAngle = Math.min(attr.startAngle, attr.endAngle),
|
||||
endAngle = Math.max(attr.startAngle, attr.endAngle),
|
||||
midAngle = (startAngle + endAngle) * 0.5,
|
||||
margin = attr.margin,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
startRho = Math.min(attr.startRho, attr.endRho) + margin,
|
||||
endRho = Math.max(attr.startRho, attr.endRho) + margin,
|
||||
midRho = (startRho + endRho) * 0.5,
|
||||
surfaceMatrix = me.surfaceMatrix,
|
||||
labelCfg = me.labelCfg || (me.labelCfg = {}),
|
||||
labelTpl = me.getBoundMarker('labels')[0].getTemplate(),
|
||||
labelBox, x, y, changes;
|
||||
|
||||
surfaceMatrix.appendMatrix(attr.matrix);
|
||||
|
||||
labelCfg.text = attr.label;
|
||||
|
||||
x = centerX + Math.cos(midAngle) * midRho;
|
||||
y = centerY + Math.sin(midAngle) * midRho;
|
||||
labelCfg.x = surfaceMatrix.x(x, y);
|
||||
labelCfg.y = surfaceMatrix.y(x, y);
|
||||
|
||||
x = centerX + Math.cos(midAngle) * endRho;
|
||||
y = centerY + Math.sin(midAngle) * endRho;
|
||||
labelCfg.calloutStartX = surfaceMatrix.x(x, y);
|
||||
labelCfg.calloutStartY = surfaceMatrix.y(x, y);
|
||||
|
||||
x = centerX + Math.cos(midAngle) * (endRho + 40);
|
||||
y = centerY + Math.sin(midAngle) * (endRho + 40);
|
||||
labelCfg.calloutPlaceX = surfaceMatrix.x(x, y);
|
||||
labelCfg.calloutPlaceY = surfaceMatrix.y(x, y);
|
||||
|
||||
labelCfg.rotationRads = (attr.rotateLabels ? midAngle + Math.atan2(surfaceMatrix.y(1, 0) - surfaceMatrix.y(0, 0), surfaceMatrix.x(1, 0) - surfaceMatrix.x(0, 0)) : 0);
|
||||
labelCfg.calloutColor = me.attr.fillStyle;
|
||||
labelCfg.globalAlpha = attr.globalAlpha * attr.fillOpacity;
|
||||
|
||||
// If a slice is empty, don't display the label.
|
||||
// This behavior can be overridden by a renderer.
|
||||
labelCfg.hidden = (attr.startAngle == attr.endAngle);
|
||||
|
||||
if (attr.renderer) {
|
||||
labelCfg.type = 'label';
|
||||
changes = attr.renderer.call(me, me, labelCfg, me.rendererData, me.rendererIndex);
|
||||
Ext.apply(labelCfg, changes);
|
||||
}
|
||||
me.putMarker('labels', labelCfg, me.attr.attributeId);
|
||||
|
||||
labelBox = me.getMarkerBBox('labels', me.attr.attributeId, true);
|
||||
if (labelBox) {
|
||||
if (attr.doCallout) {
|
||||
if (labelTpl.attr.display === 'outside') {
|
||||
me.putMarker('labels', {callout: 1}, me.attr.attributeId);
|
||||
} else {
|
||||
me.putMarker('labels', {callout: 1 - +me.sliceContainsLabel(attr, labelBox)}, me.attr.attributeId);
|
||||
}
|
||||
} else {
|
||||
me.putMarker('labels', {globalAlpha: +me.sliceContainsLabel(attr, labelBox)}, me.attr.attributeId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
sliceContainsLabel: function (attr, bbox) {
|
||||
var padding = attr.labelOverflowPadding,
|
||||
middle = (attr.endRho + attr.startRho) / 2,
|
||||
outer = middle + (bbox.width + padding) / 2,
|
||||
inner = middle - (bbox.width + padding) / 2,
|
||||
sliceAngle, l1, l2, l3;
|
||||
|
||||
if (padding < 0) {
|
||||
return 1;
|
||||
}
|
||||
if (bbox.width + padding * 2 > (attr.endRho - attr.startRho)) {
|
||||
return 0;
|
||||
}
|
||||
l1 = Math.sqrt(attr.endRho * attr.endRho - outer * outer);
|
||||
l2 = Math.sqrt(attr.endRho * attr.endRho - inner * inner);
|
||||
sliceAngle = Math.abs(attr.endAngle - attr.startAngle);
|
||||
l3 = (sliceAngle > Math.PI/2 ? inner : Math.abs(Math.tan(sliceAngle / 2)) * inner);
|
||||
if (bbox.height + padding * 2 > Math.min(l1, l2, l3) * 2) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
150
vendor/touch/src/chart/series/sprite/Polar.js
vendored
Normal file
150
vendor/touch/src/chart/series/sprite/Polar.js
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Polar
|
||||
* @extends Ext.draw.sprite.Sprite
|
||||
*
|
||||
* Polar sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Polar', {
|
||||
mixins: {
|
||||
markerHolder: 'Ext.chart.MarkerHolder'
|
||||
},
|
||||
extend: 'Ext.draw.sprite.Sprite',
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @cfg {Number} [dataMinX=0] Data minimum on the x-axis.
|
||||
*/
|
||||
dataMinX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMaxX=1] Data maximum on the x-axis.
|
||||
*/
|
||||
dataMaxX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMinY=0] Data minimum on the y-axis.
|
||||
*/
|
||||
dataMinY: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [dataMaxY=2] Data maximum on the y-axis.
|
||||
*/
|
||||
dataMaxY: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Array} Data range derived from all the series bound to the x-axis.
|
||||
*/
|
||||
rangeX: 'data',
|
||||
/**
|
||||
* @cfg {Array} Data range derived from all the series bound to the y-axis.
|
||||
*/
|
||||
rangeY: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataY=null] Data items on the y-axis.
|
||||
*/
|
||||
dataY: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Object} [dataX=null] Data items on the x-axis.
|
||||
*/
|
||||
dataX: 'data',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [centerX=0] The central point of the series on the x-axis.
|
||||
*/
|
||||
centerX: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [centerY=0] The central point of the series on the y-axis.
|
||||
*/
|
||||
centerY: 'number',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [startAngle=0] The starting angle of the polar series.
|
||||
*/
|
||||
startAngle: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [endAngle=Math.PI] The ending angle of the polar series.
|
||||
*/
|
||||
endAngle: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [startRho=0] The starting radius of the polar series.
|
||||
*/
|
||||
startRho: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [endRho=150] The ending radius of the polar series.
|
||||
*/
|
||||
endRho: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Number} [baseRotation=0] The starting rotation of the polar series.
|
||||
*/
|
||||
baseRotation: "number",
|
||||
|
||||
/**
|
||||
* @cfg {Object} [labels=null] Labels used in the series.
|
||||
*/
|
||||
labels: 'default',
|
||||
|
||||
/**
|
||||
* @cfg {Number} [labelOverflowPadding=10] Padding around labels to determine overlap.
|
||||
*/
|
||||
labelOverflowPadding: 'number'
|
||||
},
|
||||
defaults: {
|
||||
dataY: null,
|
||||
dataX: null,
|
||||
dataMinX: 0,
|
||||
dataMaxX: 1,
|
||||
dataMinY: 0,
|
||||
dataMaxY: 1,
|
||||
centerX: 0,
|
||||
centerY: 0,
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI,
|
||||
startRho: 0,
|
||||
endRho: 150,
|
||||
baseRotation: 0,
|
||||
labels: null,
|
||||
labelOverflowPadding: 10
|
||||
},
|
||||
dirtyTriggers: {
|
||||
dataX: 'bbox',
|
||||
dataY: 'bbox',
|
||||
dataMinX: 'bbox',
|
||||
dataMaxX: 'bbox',
|
||||
dataMinY: 'bbox',
|
||||
dataMaxY: 'bbox',
|
||||
centerX: "bbox",
|
||||
centerY: "bbox",
|
||||
startAngle: "bbox",
|
||||
endAngle: "bbox",
|
||||
startRho: "bbox",
|
||||
endRho: "bbox",
|
||||
baseRotation: "bbox"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} store The store that is passed to the renderer.
|
||||
*/
|
||||
store: null,
|
||||
field: null
|
||||
},
|
||||
|
||||
updatePlainBBox: function (plain) {
|
||||
var attr = this.attr;
|
||||
plain.x = attr.centerX - attr.endRho;
|
||||
plain.y = attr.centerY + attr.endRho;
|
||||
plain.width = attr.endRho * 2;
|
||||
plain.height = attr.endRho * 2;
|
||||
}
|
||||
});
|
44
vendor/touch/src/chart/series/sprite/Radar.js
vendored
Normal file
44
vendor/touch/src/chart/series/sprite/Radar.js
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Radar
|
||||
* @extends Ext.chart.series.sprite.Polar
|
||||
*
|
||||
* Radar series sprite.
|
||||
*/
|
||||
Ext.define('Ext.chart.series.sprite.Radar', {
|
||||
alias: 'sprite.radar',
|
||||
extend: 'Ext.chart.series.sprite.Polar',
|
||||
|
||||
render: function (surface, ctx) {
|
||||
var me = this,
|
||||
attr = me.attr,
|
||||
centerX = attr.centerX,
|
||||
centerY = attr.centerY,
|
||||
matrix = attr.matrix,
|
||||
minX = attr.dataMinX,
|
||||
maxX = attr.dataMaxX,
|
||||
maxY = attr.dataMaxY,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
rangeY = attr.rangeY,
|
||||
endRho = attr.endRho,
|
||||
startRho = attr.startRho,
|
||||
baseRotation = attr.baseRotation,
|
||||
i, length = dataX.length,
|
||||
markerCfg = {},
|
||||
surfaceMatrix = me.surfaceMatrix,
|
||||
x, y, r, th;
|
||||
ctx.beginPath();
|
||||
for (i = 0; i < length; i++) {
|
||||
th = (dataX[i] - minX) / (maxX - minX + 1) * 2 * Math.PI + baseRotation;
|
||||
r = dataY[i] / (rangeY ? rangeY[1] : maxY) * (endRho - startRho) + startRho;
|
||||
x = matrix.x(centerX + Math.cos(th) * r, centerY + Math.sin(th) * r);
|
||||
y = matrix.y(centerX + Math.cos(th) * r, centerY + Math.sin(th) * r);
|
||||
ctx.lineTo(x, y);
|
||||
markerCfg.translationX = surfaceMatrix.x(x, y);
|
||||
markerCfg.translationY = surfaceMatrix.y(x, y);
|
||||
me.putMarker('markers', markerCfg, i, true);
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.fillStroke(attr);
|
||||
}
|
||||
});
|
43
vendor/touch/src/chart/series/sprite/Scatter.js
vendored
Normal file
43
vendor/touch/src/chart/series/sprite/Scatter.js
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.Scatter
|
||||
* @extends Ext.chart.series.sprite.Cartesian
|
||||
*
|
||||
* Scatter series sprite.
|
||||
*/
|
||||
Ext.define("Ext.chart.series.sprite.Scatter", {
|
||||
alias: 'sprite.scatterSeries',
|
||||
extend: 'Ext.chart.series.sprite.Cartesian',
|
||||
renderClipped: function (surface, ctx, clip, clipRegion) {
|
||||
if (this.cleanRedraw) {
|
||||
return;
|
||||
}
|
||||
var attr = this.attr,
|
||||
dataX = attr.dataX,
|
||||
dataY = attr.dataY,
|
||||
matrix = this.attr.matrix,
|
||||
xx = matrix.getXX(),
|
||||
yy = matrix.getYY(),
|
||||
dx = matrix.getDX(),
|
||||
dy = matrix.getDY(),
|
||||
markerCfg = {},
|
||||
left = clipRegion[0] - xx,
|
||||
right = clipRegion[0] + clipRegion[2] + xx,
|
||||
top = clipRegion[1] - yy,
|
||||
bottom = clipRegion[1] + clipRegion[3] + yy,
|
||||
x, y;
|
||||
for (var i = 0; i < dataX.length; i++) {
|
||||
x = dataX[i];
|
||||
y = dataY[i];
|
||||
x = x * xx + dx;
|
||||
y = y * yy + dy;
|
||||
if (left <= x && x <= right && top <= y && y <= bottom) {
|
||||
if (attr.renderer) {
|
||||
attr.renderer.call(this, this, markerCfg, {store:this.getStore()}, i);
|
||||
}
|
||||
markerCfg.translationX = x;
|
||||
markerCfg.translationY = y;
|
||||
this.putMarker("items", markerCfg, i, !attr.renderer);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
73
vendor/touch/src/chart/series/sprite/StackedCartesian.js
vendored
Normal file
73
vendor/touch/src/chart/series/sprite/StackedCartesian.js
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @class Ext.chart.series.sprite.StackedCartesian
|
||||
* @extends Ext.chart.series.sprite.Cartesian
|
||||
*
|
||||
* Stacked cartesian sprite.
|
||||
*/
|
||||
Ext.define("Ext.chart.series.sprite.StackedCartesian", {
|
||||
extend: 'Ext.chart.series.sprite.Cartesian',
|
||||
inheritableStatics: {
|
||||
def: {
|
||||
processors: {
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} [groupCount=1] The number of groups in the series.
|
||||
*/
|
||||
groupCount: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Number} [groupOffset=0] The group index of the series sprite.
|
||||
*/
|
||||
groupOffset: 'number',
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @cfg {Object} [dataStartY=null] The starting point of the data used in the series.
|
||||
*/
|
||||
dataStartY: 'data'
|
||||
},
|
||||
defaults: {
|
||||
selectionTolerance: 20,
|
||||
groupCount: 1,
|
||||
groupOffset: 0,
|
||||
dataStartY: null
|
||||
},
|
||||
dirtyTriggers: {
|
||||
dataStartY: 'dataY,bbox'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//@inheritdoc
|
||||
getIndexNearPoint: function (x, y) {
|
||||
var sprite = this,
|
||||
mat = sprite.attr.matrix,
|
||||
dataX = sprite.attr.dataX,
|
||||
dataY = sprite.attr.dataY,
|
||||
dataStartY = sprite.attr.dataStartY,
|
||||
selectionTolerance = sprite.attr.selectionTolerance,
|
||||
minX = 0.5, minY = Infinity, index = -1,
|
||||
imat = mat.clone().prependMatrix(this.surfaceMatrix).inverse(),
|
||||
center = imat.transformPoint([x, y]),
|
||||
positionLB = imat.transformPoint([x - selectionTolerance, y - selectionTolerance]),
|
||||
positionTR = imat.transformPoint([x + selectionTolerance, y + selectionTolerance]),
|
||||
dx, dy,
|
||||
top = Math.min(positionLB[1], positionTR[1]),
|
||||
bottom = Math.max(positionLB[1], positionTR[1]);
|
||||
|
||||
for (var i = 0; i < dataX.length; i++) {
|
||||
if (Math.min(dataStartY[i], dataY[i]) <= bottom && top <= Math.max(dataStartY[i], dataY[i])) {
|
||||
dx = Math.abs(dataX[i] - center[0]);
|
||||
dy = Math.max(-Math.min(dataY[i] - center[1], center[1] - dataStartY[i]), 0);
|
||||
if (dx < minX && dy <= minY) {
|
||||
minX = dx;
|
||||
minY = dy;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
});
|
5
vendor/touch/src/core/Ext-more.js
vendored
5
vendor/touch/src/core/Ext-more.js
vendored
|
@ -293,6 +293,11 @@ Ext.apply(Ext, {
|
|||
elementSize: {
|
||||
xclass: 'Ext.event.publisher.ElementSize'
|
||||
}
|
||||
//<feature charts>
|
||||
,seriesItemEvents: {
|
||||
xclass: 'Ext.chart.series.ItemPublisher'
|
||||
}
|
||||
//</feature>
|
||||
},
|
||||
|
||||
//<feature logger>
|
||||
|
|
201
vendor/touch/src/draw/Animator.js
vendored
Normal file
201
vendor/touch/src/draw/Animator.js
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
/**
|
||||
* @class Ext.draw.Animator
|
||||
*
|
||||
* Singleton class that manages the animation pool.
|
||||
*/
|
||||
Ext.define('Ext.draw.Animator', {
|
||||
uses: ['Ext.draw.Draw'],
|
||||
singleton: true,
|
||||
|
||||
frameCallbacks: {},
|
||||
frameCallbackId: 0,
|
||||
scheduled: 0,
|
||||
frameStartTimeOffset:Date.now(),
|
||||
animations: [],
|
||||
running: false,
|
||||
|
||||
/**
|
||||
* Cross platform `animationTime` implementation.
|
||||
* @return {Number}
|
||||
*/
|
||||
animationTime: function () {
|
||||
return Ext.AnimationQueue.frameStartTime - this.frameStartTimeOffset;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an animated object to the animation pool.
|
||||
*
|
||||
* @param {Object} animation The animation descriptor to add to the pool.
|
||||
*/
|
||||
add: function (animation) {
|
||||
if (!this.contains(animation)) {
|
||||
this.animations.push(animation);
|
||||
Ext.draw.Animator.ignite();
|
||||
if ('fireEvent' in animation) {
|
||||
animation.fireEvent('animationstart', animation);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes an animation from the pool.
|
||||
* TODO: This is broken when called within `step` method.
|
||||
* @param {Object} animation The animation to remove from the pool.
|
||||
*/
|
||||
remove: function (animation) {
|
||||
var me = this,
|
||||
animations = me.animations,
|
||||
i = 0,
|
||||
l = animations.length;
|
||||
|
||||
for (; i < l; ++i) {
|
||||
if (animations[i] === animation) {
|
||||
animations.splice(i, 1);
|
||||
if ('fireEvent' in animation) {
|
||||
animation.fireEvent('animationend', animation);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns `true` or `false` whether it contains the given animation or not.
|
||||
*
|
||||
* @param {Object} animation The animation to check for.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
contains: function (animation) {
|
||||
return this.animations.indexOf(animation) > -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns `true` or `false` whether the pool is empty or not.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
empty: function () {
|
||||
return this.animations.length === 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a frame time it will filter out finished animations from the pool.
|
||||
*
|
||||
* @param {Number} frameTime The frame's start time, in milliseconds.
|
||||
*/
|
||||
step: function (frameTime) {
|
||||
var me = this,
|
||||
animations = me.animations,
|
||||
animation,
|
||||
i = 0,
|
||||
ln = animations.length;
|
||||
|
||||
for (; i < ln; i++) {
|
||||
animation = animations[i];
|
||||
animation.step(frameTime);
|
||||
if (!animation.animating) {
|
||||
animations.splice(i, 1);
|
||||
i--;
|
||||
ln--;
|
||||
if (animation.fireEvent) {
|
||||
animation.fireEvent('animationend');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register an one-time callback that will be called at the next frame.
|
||||
* @param {Function} callback
|
||||
* @param {Object} scope
|
||||
* @return {String}
|
||||
*/
|
||||
schedule: function (callback, scope) {
|
||||
scope = scope || this;
|
||||
var id = 'frameCallback' + (this.frameCallbackId++);
|
||||
|
||||
if (Ext.isString(callback)) {
|
||||
callback = scope[callback];
|
||||
}
|
||||
Ext.draw.Animator.frameCallbacks[id] = {fn: callback, scope: scope, once: true};
|
||||
this.scheduled++;
|
||||
Ext.draw.Animator.ignite();
|
||||
return id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel a registered one-time callback
|
||||
* @param {String} id
|
||||
*/
|
||||
cancel: function (id) {
|
||||
if (Ext.draw.Animator.frameCallbacks[id] && Ext.draw.Animator.frameCallbacks[id].once) {
|
||||
this.scheduled--;
|
||||
delete Ext.draw.Animator.frameCallbacks[id];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register a recursive callback that will be called at every frame.
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @param {Object} scope
|
||||
* @return {String}
|
||||
*/
|
||||
addFrameCallback: function (callback, scope) {
|
||||
scope = scope || this;
|
||||
if (Ext.isString(callback)) {
|
||||
callback = scope[callback];
|
||||
}
|
||||
var id = 'frameCallback' + (this.frameCallbackId++);
|
||||
|
||||
Ext.draw.Animator.frameCallbacks[id] = {fn: callback, scope: scope};
|
||||
return id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister a recursive callback.
|
||||
* @param {String} id
|
||||
*/
|
||||
removeFrameCallback: function (id) {
|
||||
delete Ext.draw.Animator.frameCallbacks[id];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
fireFrameCallbacks: function () {
|
||||
var callbacks = this.frameCallbacks,
|
||||
id, fn, cb;
|
||||
|
||||
for (id in callbacks) {
|
||||
cb = callbacks[id];
|
||||
fn = cb.fn;
|
||||
if (Ext.isString(fn)) {
|
||||
fn = cb.scope[fn];
|
||||
}
|
||||
|
||||
fn.call(cb.scope);
|
||||
|
||||
if (callbacks[id] && cb.once) {
|
||||
this.scheduled--;
|
||||
delete callbacks[id];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleFrame: function() {
|
||||
this.step(this.animationTime());
|
||||
this.fireFrameCallbacks();
|
||||
if (!this.scheduled && this.empty()) {
|
||||
Ext.AnimationQueue.stop(this.handleFrame, this);
|
||||
this.running = false;
|
||||
}
|
||||
},
|
||||
|
||||
ignite: function() {
|
||||
if (!this.running) {
|
||||
this.running = true;
|
||||
Ext.AnimationQueue.start(this.handleFrame, this);
|
||||
Ext.draw.Draw.updateIOS();
|
||||
}
|
||||
}
|
||||
});
|
427
vendor/touch/src/draw/Color.js
vendored
Normal file
427
vendor/touch/src/draw/Color.js
vendored
Normal file
|
@ -0,0 +1,427 @@
|
|||
(function () {
|
||||
/**
|
||||
* Represents an RGB color and provides helper functions on it e.g. to get
|
||||
* color components in HSL color space.
|
||||
*/
|
||||
Ext.define('Ext.draw.Color', {
|
||||
statics: {
|
||||
colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
|
||||
rgbToHexRe: /\s*rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
|
||||
rgbaToHexRe: /\s*rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\.\d]+)\)/,
|
||||
hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/
|
||||
},
|
||||
|
||||
isColor: true,
|
||||
/**
|
||||
* @cfg {Number} lightnessFactor
|
||||
*
|
||||
* The default factor to compute the lighter or darker color.
|
||||
*/
|
||||
lightnessFactor: 0.2,
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Number} red Red component (0..255)
|
||||
* @param {Number} green Green component (0..255)
|
||||
* @param {Number} blue Blue component (0..255)
|
||||
* @param {Number} [alpha=1] (optional) Alpha component (0..1)
|
||||
*/
|
||||
constructor: function (red, green, blue, alpha) {
|
||||
this.setRGB(red, green, blue, alpha);
|
||||
},
|
||||
|
||||
setRGB: function (red, green, blue, alpha) {
|
||||
var me = this;
|
||||
me.r = Math.min(255, Math.max(0, red));
|
||||
me.g = Math.min(255, Math.max(0, green));
|
||||
me.b = Math.min(255, Math.max(0, blue));
|
||||
if (alpha === undefined) {
|
||||
me.a = 1;
|
||||
} else {
|
||||
me.a = Math.min(1, Math.max(0, alpha));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the gray value (0 to 255) of the color.
|
||||
*
|
||||
* The gray value is calculated using the formula r*0.3 + g*0.59 + b*0.11.
|
||||
*
|
||||
* @return {Number}
|
||||
*/
|
||||
getGrayscale: function () {
|
||||
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
||||
return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the equivalent HSL components of the color.
|
||||
* @param {Array} [target] Optional array to receive the values.
|
||||
* @return {Array}
|
||||
*/
|
||||
getHSL: function (target) {
|
||||
var me = this,
|
||||
r = me.r / 255,
|
||||
g = me.g / 255,
|
||||
b = me.b / 255,
|
||||
max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b),
|
||||
delta = max - min,
|
||||
h,
|
||||
s = 0,
|
||||
l = 0.5 * (max + min);
|
||||
|
||||
// min==max means achromatic (hue is undefined)
|
||||
if (min !== max) {
|
||||
s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
|
||||
if (r === max) {
|
||||
h = 60 * (g - b) / delta;
|
||||
} else if (g === max) {
|
||||
h = 120 + 60 * (b - r) / delta;
|
||||
} else {
|
||||
h = 240 + 60 * (r - g) / delta;
|
||||
}
|
||||
if (h < 0) {
|
||||
h += 360;
|
||||
}
|
||||
if (h >= 360) {
|
||||
h -= 360;
|
||||
}
|
||||
}
|
||||
if (target) {
|
||||
target[0] = h;
|
||||
target[1] = s;
|
||||
target[2] = l;
|
||||
} else {
|
||||
target = [h, s, l];
|
||||
}
|
||||
return target;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set current color based on the specified HSL values.
|
||||
*
|
||||
* @param {Number} h Hue component (0..359)
|
||||
* @param {Number} s Saturation component (0..1)
|
||||
* @param {Number} l Lightness component (0..1)
|
||||
* @return this
|
||||
*/
|
||||
setHSL: function (h, s, l) {
|
||||
var c, x, m,
|
||||
abs = Math.abs,
|
||||
floor = Math.floor;
|
||||
h = (h % 360 + 360 ) % 360;
|
||||
s = s > 1 ? 1 : s < 0 ? 0 : s;
|
||||
l = l > 1 ? 1 : l < 0 ? 0 : l;
|
||||
if (s === 0 || h === null) {
|
||||
l *= 255;
|
||||
this.setRGB(l, l, l);
|
||||
}
|
||||
else {
|
||||
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
|
||||
// C is the chroma
|
||||
// X is the second largest component
|
||||
// m is the lightness adjustment
|
||||
h /= 60;
|
||||
c = s * (1 - abs(2 * l - 1));
|
||||
x = c * (1 - abs(h - 2 * floor(h / 2) - 1));
|
||||
m = l - c / 2;
|
||||
m *= 255;
|
||||
c *= 255;
|
||||
x *= 255;
|
||||
switch (floor(h)) {
|
||||
case 0:
|
||||
this.setRGB(c + m, x + m, m);
|
||||
break;
|
||||
case 1:
|
||||
this.setRGB(x + m, c + m, m);
|
||||
break;
|
||||
case 2:
|
||||
this.setRGB(m, c + m, x + m);
|
||||
break;
|
||||
case 3:
|
||||
this.setRGB(m, x + m, c + m);
|
||||
break;
|
||||
case 4:
|
||||
this.setRGB(x + m, m, c + m);
|
||||
break;
|
||||
case 5:
|
||||
this.setRGB(c + m, m, x + m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a new color that is lighter than this color.
|
||||
* @param {Number} [factor=0.2] Lighter factor (0..1).
|
||||
* @return {Ext.draw.Color}
|
||||
*/
|
||||
createLighter: function (factor) {
|
||||
var hsl = this.getHSL();
|
||||
factor = factor || this.lightnessFactor;
|
||||
// COMPAT Ext.util.Numbers -> Ext.Number
|
||||
hsl[2] = hsl[2] + factor;
|
||||
if (hsl[2] > 1) {
|
||||
hsl[2] = 1;
|
||||
} else if (hsl[2] < 0) {
|
||||
hsl[2] = 0;
|
||||
}
|
||||
return Ext.draw.Color.fromHSL(hsl[0], hsl[1], hsl[2]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a new color that is darker than this color.
|
||||
* @param {Number} [factor=0.2] Darker factor (0..1).
|
||||
* @return {Ext.draw.Color}
|
||||
*/
|
||||
createDarker: function (factor) {
|
||||
factor = factor || this.lightnessFactor;
|
||||
return this.createLighter(-factor);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the color in the hex format, i.e. '#rrggbb'.
|
||||
* @return {String}
|
||||
*/
|
||||
toString: function () {
|
||||
if (this.a === 1) {
|
||||
var me = this,
|
||||
round = Math.round,
|
||||
r = round(me.r).toString(16),
|
||||
g = round(me.g).toString(16),
|
||||
b = round(me.b).toString(16);
|
||||
r = (r.length === 1) ? '0' + r : r;
|
||||
g = (g.length === 1) ? '0' + g : g;
|
||||
b = (b.length === 1) ? '0' + b : b;
|
||||
return ['#', r, g, b].join('');
|
||||
} else {
|
||||
return 'rgba(' + [Math.round(this.r), Math.round(this.g), Math.round(this.b), this.a.toFixed(15)].join(',') + ')';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert a color to hexadecimal format.
|
||||
*
|
||||
* @param {String/Array} color The color value (i.e 'rgb(255, 255, 255)', 'color: #ffffff').
|
||||
* Can also be an Array, in this case the function handles the first member.
|
||||
* @return {String} The color in hexadecimal format.
|
||||
*/
|
||||
toHex: function (color) {
|
||||
if (Ext.isArray(color)) {
|
||||
color = color[0];
|
||||
}
|
||||
if (!Ext.isString(color)) {
|
||||
return '';
|
||||
}
|
||||
if (color.substr(0, 1) === '#') {
|
||||
return color;
|
||||
}
|
||||
var digits = Ext.draw.Color.colorToHexRe.exec(color);
|
||||
|
||||
if (Ext.isArray(digits)) {
|
||||
var red = parseInt(digits[2], 10),
|
||||
green = parseInt(digits[3], 10),
|
||||
blue = parseInt(digits[4], 10),
|
||||
rgb = blue | (green << 8) | (red << 16);
|
||||
return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the string and set current color.
|
||||
*
|
||||
* Supported formats: '#rrggbb', '#rgb', and 'rgb(r,g,b)'.
|
||||
*
|
||||
* If the string is not recognized, an `undefined` will be returned instead.
|
||||
*
|
||||
* @param {String} str Color in string.
|
||||
* @return this
|
||||
*/
|
||||
setFromString: function (str) {
|
||||
var values, r, g, b, a = 1,
|
||||
parse = parseInt;
|
||||
|
||||
if (str === 'none') {
|
||||
this.r = this.g = this.b = this.a = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
if ((str.length === 4 || str.length === 7) && str.substr(0, 1) === '#') {
|
||||
values = str.match(Ext.draw.Color.hexRe);
|
||||
if (values) {
|
||||
r = parse(values[1], 16) >> 0;
|
||||
g = parse(values[2], 16) >> 0;
|
||||
b = parse(values[3], 16) >> 0;
|
||||
if (str.length === 4) {
|
||||
r += (r * 16);
|
||||
g += (g * 16);
|
||||
b += (b * 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((values = str.match(Ext.draw.Color.rgbToHexRe))) {
|
||||
r = +values[1];
|
||||
g = +values[2];
|
||||
b = +values[3];
|
||||
} else if ((values = str.match(Ext.draw.Color.rgbaToHexRe))) {
|
||||
r = +values[1];
|
||||
g = +values[2];
|
||||
b = +values[3];
|
||||
a = +values[4];
|
||||
} else {
|
||||
if (Ext.draw.Color.ColorList.hasOwnProperty(str.toLowerCase())) {
|
||||
return this.setFromString(Ext.draw.Color.ColorList[str.toLowerCase()]);
|
||||
}
|
||||
}
|
||||
if (typeof r === 'undefined') {
|
||||
return this;
|
||||
}
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
return this;
|
||||
}
|
||||
}, function () {
|
||||
var flyColor = new this();
|
||||
|
||||
//<deprecated product=touch since=2.2>
|
||||
this.createAlias({
|
||||
"getLighter": "createLighter",
|
||||
"getDarker": "createDarker"
|
||||
});
|
||||
//</deprecated>
|
||||
|
||||
this.addStatics({
|
||||
/**
|
||||
* Returns a flyweight instance of Ext.draw.Color.
|
||||
*
|
||||
* Can be called with either a CSS color string or with separate
|
||||
* arguments for red, green, blue, alpha.
|
||||
*
|
||||
* @param {Number/String} red Red component (0..255) or CSS color string.
|
||||
* @param {Number} [green] Green component (0..255)
|
||||
* @param {Number} [blue] Blue component (0..255)
|
||||
* @param {Number} [alpha=1] Alpha component (0..1)
|
||||
* @return {Ext.draw.Color}
|
||||
* @static
|
||||
*/
|
||||
fly: function (r, g, b, a) {
|
||||
switch (arguments.length) {
|
||||
case 1:
|
||||
flyColor.setFromString(r);
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
flyColor.setRGB(r, g, b, a);
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return flyColor;
|
||||
},
|
||||
|
||||
ColorList: {
|
||||
"aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "aqua": "#00ffff", "aquamarine": "#7fffd4", "azure": "#f0ffff",
|
||||
"beige": "#f5f5dc", "bisque": "#ffe4c4", "black": "#000000", "blanchedalmond": "#ffebcd", "blue": "#0000ff", "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", "darkkhaki": "#bdb76b", "darkmagenta": "#8b008b", "darkolivegreen": "#556b2f",
|
||||
"darkorange": "#ff8c00", "darkorchid": "#9932cc", "darkred": "#8b0000", "darksalmon": "#e9967a", "darkseagreen": "#8fbc8f", "darkslateblue": "#483d8b", "darkslategray": "#2f4f4f", "darkturquoise": "#00ced1",
|
||||
"darkviolet": "#9400d3", "deeppink": "#ff1493", "deepskyblue": "#00bfff", "dimgray": "#696969", "dodgerblue": "#1e90ff",
|
||||
"firebrick": "#b22222", "floralwhite": "#fffaf0", "forestgreen": "#228b22", "fuchsia": "#ff00ff",
|
||||
"gainsboro": "#dcdcdc", "ghostwhite": "#f8f8ff", "gold": "#ffd700", "goldenrod": "#daa520", "gray": "#808080", "green": "#008000", "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",
|
||||
"lightgray": "#d3d3d3", "lightgrey": "#d3d3d3", "lightgreen": "#90ee90", "lightpink": "#ffb6c1", "lightsalmon": "#ffa07a", "lightseagreen": "#20b2aa", "lightskyblue": "#87cefa", "lightslategray": "#778899", "lightsteelblue": "#b0c4de",
|
||||
"lightyellow": "#ffffe0", "lime": "#00ff00", "limegreen": "#32cd32", "linen": "#faf0e6",
|
||||
"magenta": "#ff00ff", "maroon": "#800000", "mediumaquamarine": "#66cdaa", "mediumblue": "#0000cd", "mediumorchid": "#ba55d3", "mediumpurple": "#9370d8", "mediumseagreen": "#3cb371", "mediumslateblue": "#7b68ee",
|
||||
"mediumspringgreen": "#00fa9a", "mediumturquoise": "#48d1cc", "mediumvioletred": "#c71585", "midnightblue": "#191970", "mintcream": "#f5fffa", "mistyrose": "#ffe4e1", "moccasin": "#ffe4b5",
|
||||
"navajowhite": "#ffdead", "navy": "#000080",
|
||||
"oldlace": "#fdf5e6", "olive": "#808000", "olivedrab": "#6b8e23", "orange": "#ffa500", "orangered": "#ff4500", "orchid": "#da70d6",
|
||||
"palegoldenrod": "#eee8aa", "palegreen": "#98fb98", "paleturquoise": "#afeeee", "palevioletred": "#d87093", "papayawhip": "#ffefd5", "peachpuff": "#ffdab9", "peru": "#cd853f", "pink": "#ffc0cb", "plum": "#dda0dd", "powderblue": "#b0e0e6", "purple": "#800080",
|
||||
"red": "#ff0000", "rosybrown": "#bc8f8f", "royalblue": "#4169e1",
|
||||
"saddlebrown": "#8b4513", "salmon": "#fa8072", "sandybrown": "#f4a460", "seagreen": "#2e8b57", "seashell": "#fff5ee", "sienna": "#a0522d", "silver": "#c0c0c0", "skyblue": "#87ceeb", "slateblue": "#6a5acd", "slategray": "#708090", "snow": "#fffafa", "springgreen": "#00ff7f", "steelblue": "#4682b4",
|
||||
"tan": "#d2b48c", "teal": "#008080", "thistle": "#d8bfd8", "tomato": "#ff6347", "turquoise": "#40e0d0",
|
||||
"violet": "#ee82ee",
|
||||
"wheat": "#f5deb3", "white": "#ffffff", "whitesmoke": "#f5f5f5",
|
||||
"yellow": "#ffff00", "yellowgreen": "#9acd32"
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new color based on the specified HSL values.
|
||||
*
|
||||
* @param {Number} h Hue component (0..359)
|
||||
* @param {Number} s Saturation component (0..1)
|
||||
* @param {Number} l Lightness component (0..1)
|
||||
* @return {Ext.draw.Color}
|
||||
* @static
|
||||
*/
|
||||
fromHSL: function (h, s, l) {
|
||||
return (new this(0, 0, 0, 0)).setHSL(h, s, l);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse the string and create a new color.
|
||||
*
|
||||
* Supported formats: '#rrggbb', '#rgb', and 'rgb(r,g,b)'.
|
||||
*
|
||||
* If the string is not recognized, an undefined will be returned instead.
|
||||
*
|
||||
* @param {String} string Color in string.
|
||||
* @returns {Ext.draw.Color}
|
||||
* @static
|
||||
*/
|
||||
fromString: function (string) {
|
||||
return (new this(0, 0, 0, 0)).setFromString(string);
|
||||
},
|
||||
|
||||
/**
|
||||
* Convenience method for creating a color.
|
||||
*
|
||||
* Can be called with several different combinations of arguments:
|
||||
*
|
||||
* // Ext.draw.Color is returned unchanged.
|
||||
* Ext.draw.Color.create(new Ext.draw.color(255, 0, 0, 0));
|
||||
*
|
||||
* // CSS color string.
|
||||
* Ext.draw.Color.create("red");
|
||||
*
|
||||
* // Array of red, green, blue, alpha
|
||||
* Ext.draw.Color.create([255, 0, 0, 0]);
|
||||
*
|
||||
* // Separate arguments of red, green, blue, alpha
|
||||
* Ext.draw.Color.create(255, 0, 0, 0);
|
||||
*
|
||||
* // Returns black when no arguments given.
|
||||
* Ext.draw.Color.create();
|
||||
*
|
||||
* @param {Ext.draw.Color/String/Number[]/Number} [red] Red component (0..255),
|
||||
* CSS color string or array of all components.
|
||||
* @param {Number} [green] Green component (0..255)
|
||||
* @param {Number} [blue] Blue component (0..255)
|
||||
* @param {Number} [alpha=1] Alpha component (0..1)
|
||||
* @return {Ext.draw.Color}
|
||||
* @static
|
||||
*/
|
||||
create: function (arg) {
|
||||
if (arg instanceof this) {
|
||||
return arg;
|
||||
} else if (Ext.isArray(arg)) {
|
||||
return new Ext.draw.Color(arg[0], arg[1], arg[2], arg[3]);
|
||||
} else if (Ext.isString(arg)) {
|
||||
return Ext.draw.Color.fromString(arg);
|
||||
} else if (arguments.length > 2) {
|
||||
return new Ext.draw.Color(arguments[0], arguments[1], arguments[2], arguments[3]);
|
||||
} else {
|
||||
return new Ext.draw.Color(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
354
vendor/touch/src/draw/Component.js
vendored
Normal file
354
vendor/touch/src/draw/Component.js
vendored
Normal file
|
@ -0,0 +1,354 @@
|
|||
/**
|
||||
* The Draw Component is a surface in which sprites can be rendered. The Draw Component
|
||||
* manages and holds a `Surface` instance: an interface that has
|
||||
* an SVG or VML implementation depending on the browser capabilities and where
|
||||
* Sprites can be appended.
|
||||
* One way to create a draw component is:
|
||||
*
|
||||
* var drawComponent = new Ext.draw.Component({
|
||||
* fullscreen: true,
|
||||
* sprites: [{
|
||||
* type: 'circle',
|
||||
* fill: '#79BB3F',
|
||||
* radius: 100,
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* }]
|
||||
* });
|
||||
*
|
||||
* In this case we created a draw component and added a sprite to it.
|
||||
* The *type* of the sprite is *circle* so if you run this code you'll see a yellow-ish
|
||||
* circle in a Window. When setting `viewBox` to `false` we are responsible for setting the object's position and
|
||||
* dimensions accordingly.
|
||||
*
|
||||
* You can also add sprites by using the surface's add method:
|
||||
*
|
||||
* drawComponent.getSurface('main').add({
|
||||
* type: 'circle',
|
||||
* fill: 'blue',
|
||||
* radius: 100,
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* });
|
||||
*
|
||||
* For more information on Sprites, the core elements added to a draw component's surface,
|
||||
* refer to the {@link Ext.draw.sprite.Sprite} documentation.
|
||||
*/
|
||||
Ext.define('Ext.draw.Component', {
|
||||
|
||||
extend: 'Ext.Container',
|
||||
xtype: 'draw',
|
||||
defaultType: 'surface',
|
||||
|
||||
requires: [
|
||||
'Ext.draw.Surface',
|
||||
'Ext.draw.engine.Svg',
|
||||
'Ext.draw.engine.Canvas',
|
||||
'Ext.draw.gradient.GradientDefinition'
|
||||
],
|
||||
engine: 'Ext.draw.engine.Canvas',
|
||||
statics: {
|
||||
WATERMARK: 'Powered by <span style="color:#22E962; font-weight: 900">Sencha Touch</span> <span style="color:#75cdff; font-weight: 900">GPLv3</span>'
|
||||
},
|
||||
config: {
|
||||
cls: 'x-draw-component',
|
||||
|
||||
/**
|
||||
* @deprecated 2.2.0 Please implement custom resize event handler.
|
||||
* Resize the draw component by the content size of the main surface.
|
||||
*
|
||||
* __Note:__ It is applied only when there is only one surface.
|
||||
*/
|
||||
autoSize: false,
|
||||
|
||||
/**
|
||||
* @deprecated 2.2.0 Please implement custom resize event handler.
|
||||
* Pan/Zoom the content in main surface to fit the component size.
|
||||
*
|
||||
* __Note:__ It is applied only when there is only one surface.
|
||||
*/
|
||||
viewBox: false,
|
||||
|
||||
/**
|
||||
* @deprecated 2.2.0 Please implement custom resize event handler.
|
||||
* Fit the main surface to the size of component.
|
||||
*
|
||||
* __Note:__ It is applied only when there is only one surface.
|
||||
*/
|
||||
fitSurface: true,
|
||||
|
||||
/**
|
||||
* @cfg {Function} [resizeHandler] The resize function that can be configured to have a behavior.
|
||||
*
|
||||
* __Note:__ since resize events trigger {@link #renderFrame} calls automatically,
|
||||
* return `false` from the resize function, if it also calls `renderFrame`, to prevent double rendering.
|
||||
*/
|
||||
resizeHandler: null,
|
||||
|
||||
background: null,
|
||||
|
||||
sprites: null,
|
||||
|
||||
/**
|
||||
* @cfg {Object[]} gradients
|
||||
* Defines a set of gradients that can be used as color properties
|
||||
* (fillStyle and strokeStyle, but not shadowColor) in sprites.
|
||||
* The gradients array is an array of objects with the following properties:
|
||||
* - **id** - string - The unique name of the gradient.
|
||||
* - **type** - string, optional - The type of the gradient. Available types are: 'linear', 'radial'. Defaults to 'linear'.
|
||||
* - **angle** - number, optional - The angle of the gradient in degrees.
|
||||
* - **stops** - array - An array of objects with 'color' and 'offset' properties, where 'offset' is a real number from 0 to 1.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* gradients: [{
|
||||
* id: 'gradientId1',
|
||||
* type: 'linear',
|
||||
* angle: 45,
|
||||
* stops: [{
|
||||
* offset: 0,
|
||||
* color: 'red'
|
||||
* }, {
|
||||
* offset: 1,
|
||||
* color: 'yellow'
|
||||
* }]
|
||||
* }, {
|
||||
* id: 'gradientId2',
|
||||
* type: 'radial',
|
||||
* stops: [{
|
||||
* offset: 0,
|
||||
* color: '#555',
|
||||
* }, {
|
||||
* offset: 1,
|
||||
* color: '#ddd',
|
||||
* }]
|
||||
* }]
|
||||
*
|
||||
* Then the sprites can use 'gradientId1' and 'gradientId2' by setting the color attributes to those ids, for example:
|
||||
*
|
||||
* sprite.setAttributes({
|
||||
* fillStyle: 'url(#gradientId1)',
|
||||
* strokeStyle: 'url(#gradientId2)'
|
||||
* });
|
||||
*/
|
||||
gradients: []
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
config = config || {};
|
||||
this.callSuper(arguments);
|
||||
this.frameCallbackId = Ext.draw.Animator.addFrameCallback('renderFrame', this);
|
||||
},
|
||||
|
||||
applyGradients: function (gradients) {
|
||||
var result = [],
|
||||
i, n, gradient, offset;
|
||||
if (!Ext.isArray(gradients)) {
|
||||
return result;
|
||||
}
|
||||
for (i = 0, n = gradients.length; i < n; i++) {
|
||||
gradient = gradients[i];
|
||||
if (!Ext.isObject(gradient)) {
|
||||
continue;
|
||||
}
|
||||
// ExtJS only supported linear gradients, so we didn't have to specify their type
|
||||
if (typeof gradient.type !== 'string') {
|
||||
gradient.type = 'linear';
|
||||
}
|
||||
if (gradient.angle) {
|
||||
gradient.degrees = gradient.angle;
|
||||
delete gradient.angle;
|
||||
}
|
||||
// Convert ExtJS stops object to Touch stops array
|
||||
if (Ext.isObject(gradient.stops)) {
|
||||
gradient.stops = (function (stops) {
|
||||
var result = [], stop;
|
||||
for (offset in stops) {
|
||||
stop = stops[offset];
|
||||
stop.offset = offset / 100;
|
||||
result.push(stop);
|
||||
}
|
||||
return result;
|
||||
})(gradient.stops);
|
||||
}
|
||||
result.push(gradient);
|
||||
}
|
||||
Ext.draw.gradient.GradientDefinition.add(result);
|
||||
return result;
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
var me = this;
|
||||
me.callSuper();
|
||||
me.element.on('resize', 'onResize', this);
|
||||
},
|
||||
|
||||
applySprites: function (sprites) {
|
||||
// Never update
|
||||
if (!sprites) {
|
||||
return;
|
||||
}
|
||||
|
||||
sprites = Ext.Array.from(sprites);
|
||||
|
||||
var ln = sprites.length,
|
||||
i, surface;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
if (sprites[i].surface instanceof Ext.draw.Surface) {
|
||||
surface = sprites[i].surface;
|
||||
} else if (Ext.isString(sprites[i].surface)) {
|
||||
surface = this.getSurface(sprites[i].surface);
|
||||
} else {
|
||||
surface = this.getSurface('main');
|
||||
}
|
||||
surface.add(sprites[i]);
|
||||
}
|
||||
},
|
||||
|
||||
getElementConfig: function () {
|
||||
return {
|
||||
reference: 'element',
|
||||
className: 'x-container',
|
||||
children: [
|
||||
{
|
||||
reference: 'innerElement',
|
||||
className: 'x-inner',
|
||||
children: [
|
||||
{
|
||||
reference: 'watermarkElement',
|
||||
cls: 'x-chart-watermark',
|
||||
html: Ext.draw.Component.WATERMARK,
|
||||
style: Ext.draw.Component.WATERMARK ? '': 'display: none'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
updateBackground: function (background) {
|
||||
this.element.setStyle({
|
||||
background: background
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Place water mark after resize.
|
||||
*/
|
||||
onPlaceWatermark: function () {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
onResize: function () {
|
||||
var me = this,
|
||||
size = me.element.getSize(),
|
||||
resizeHandler = me.getResizeHandler() || me.resizeHandler,
|
||||
result;
|
||||
me.fireEvent('resize', me, size);
|
||||
result = resizeHandler.call(me, size);
|
||||
if (result !== false) {
|
||||
me.renderFrame();
|
||||
me.onPlaceWatermark();
|
||||
}
|
||||
},
|
||||
|
||||
resizeHandler: function (size) {
|
||||
var me = this;
|
||||
|
||||
//<deprecated product=touch since=2.2>
|
||||
var surfaces = me.getItems(),
|
||||
surface, bbox, mat, zoomX, zoomY, zoom;
|
||||
|
||||
if (surfaces.length === 1) {
|
||||
surface = surfaces.get(0);
|
||||
if (me.getAutoSize()) {
|
||||
bbox = surface.getItems().getBBox();
|
||||
mat = new Ext.draw.Matrix();
|
||||
mat.prepend(1, 0, 0, 1, -bbox.x, -bbox.y);
|
||||
surface.matrix = mat;
|
||||
surface.inverseMatrix = mat.inverse();
|
||||
surface.setRegion([0, 0, bbox.width, bbox.height]);
|
||||
} else if (me.getViewBox()) {
|
||||
bbox = surface.getItems().getBBox();
|
||||
zoomX = size.width / bbox.width;
|
||||
zoomY = size.height / bbox.height;
|
||||
zoom = Math.min(zoomX, zoomY);
|
||||
mat = new Ext.draw.Matrix();
|
||||
mat.prepend(
|
||||
zoom, 0, 0, zoom,
|
||||
size.width * 0.5 + (-bbox.x - bbox.width * 0.5) * zoom,
|
||||
size.height * 0.5 + (-bbox.y - bbox.height * 0.5) * zoom);
|
||||
surface.matrix = mat;
|
||||
surface.inverseMatrix = mat.inverse();
|
||||
surface.setRegion([0, 0, size.width, size.height]);
|
||||
} else if (me.getFitSurface()) {
|
||||
surface.setRegion([0, 0, size.width, size.height]);
|
||||
}
|
||||
} else if (!me.getFitSurface()) {
|
||||
return;
|
||||
}
|
||||
//</deprecated>
|
||||
|
||||
me.getItems().each(function (surface) {
|
||||
surface.setRegion([0, 0, size.width, size.height]);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a surface by the given id or create one if it doesn't exist.
|
||||
* @param {String} [id="main"]
|
||||
* @return {Ext.draw.Surface}
|
||||
*/
|
||||
getSurface: function (id) {
|
||||
id = this.getId() + '-' + (id || 'main');
|
||||
var me = this,
|
||||
surfaces = me.getItems(),
|
||||
surface = surfaces.get(id),
|
||||
size;
|
||||
|
||||
if (!surface) {
|
||||
surface = me.add({xclass: me.engine, id: id});
|
||||
if (me.getFitSurface()) {
|
||||
size = me.element.getSize();
|
||||
surface.setRegion([0, 0, size.width, size.height]);
|
||||
}
|
||||
surface.renderFrame();
|
||||
}
|
||||
return surface;
|
||||
},
|
||||
|
||||
/**
|
||||
* Render all the surfaces in the component.
|
||||
*/
|
||||
renderFrame: function () {
|
||||
var me = this,
|
||||
i, ln, bbox,
|
||||
surfaces = me.getItems();
|
||||
|
||||
for (i = 0, ln = surfaces.length; i < ln; i++) {
|
||||
surfaces.items[i].renderFrame();
|
||||
}
|
||||
//<deprecated product=touch since=2.2>
|
||||
// TODO: Throw a deprecation message
|
||||
if (surfaces.length === 1 && me.getAutoSize()) {
|
||||
bbox = me.getSurface().getItems().getBBox();
|
||||
me.setSize(Math.ceil(bbox.width) + 1, Math.ceil(bbox.height) + 1);
|
||||
}
|
||||
//</deprecated>
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
Ext.draw.Animator.removeFrameCallback(this.frameCallbackId);
|
||||
this.callSuper();
|
||||
}
|
||||
|
||||
}, function () {
|
||||
if (location.search.match('svg')) {
|
||||
Ext.draw.Component.prototype.engine = 'Ext.draw.engine.Svg';
|
||||
} else if ((Ext.os.is.BlackBerry && Ext.os.version.getMajor() === 10) || (Ext.browser.is.AndroidStock4 && (Ext.os.version.getMinor() === 1 || Ext.os.version.getMinor() === 2 || Ext.os.version.getMinor() === 3))) {
|
||||
// http://code.google.com/p/android/issues/detail?id=37529
|
||||
Ext.draw.Component.prototype.engine = 'Ext.draw.engine.Svg';
|
||||
}
|
||||
});
|
261
vendor/touch/src/draw/Draw.js
vendored
Normal file
261
vendor/touch/src/draw/Draw.js
vendored
Normal file
|
@ -0,0 +1,261 @@
|
|||
(function () {
|
||||
if (!Ext.global.Float32Array) {
|
||||
// Typed Array polyfill
|
||||
var Float32Array = function (array) {
|
||||
if (typeof array === 'number') {
|
||||
this.length = array;
|
||||
} else if ('length' in array) {
|
||||
this.length = array.length;
|
||||
for (var i = 0, len = array.length; i < len; i++) {
|
||||
this[i] = +array[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Float32Array.prototype = [];
|
||||
Ext.global.Float32Array = Float32Array;
|
||||
}
|
||||
})();
|
||||
|
||||
/**
|
||||
* Utility class providing mathematics functionalities through all the draw package.
|
||||
*/
|
||||
Ext.define('Ext.draw.Draw', {
|
||||
singleton: true,
|
||||
|
||||
radian: Math.PI / 180,
|
||||
pi2: Math.PI * 2,
|
||||
|
||||
/**
|
||||
* Function that returns its first element.
|
||||
* @param {Mixed} a
|
||||
* @return {Mixed}
|
||||
*/
|
||||
reflectFn: function (a) {
|
||||
return a;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converting degrees to radians.
|
||||
* @param {Number} degrees
|
||||
* @return {Number}
|
||||
*/
|
||||
rad: function (degrees) {
|
||||
return degrees % 360 * Math.PI / 180;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converting radians to degrees.
|
||||
* @param {Number} radian
|
||||
* @return {Number}
|
||||
*/
|
||||
degrees: function (radian) {
|
||||
return radian * 180 / Math.PI % 360;
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} bbox1
|
||||
* @param {Object} bbox2
|
||||
* @param {Number} [padding]
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isBBoxIntersect: function (bbox1, bbox2, padding) {
|
||||
padding = padding || 0;
|
||||
return (Math.max(bbox1.x, bbox2.x) - padding > Math.min(bbox1.x + bbox1.width, bbox2.x + bbox2.width)) ||
|
||||
(Math.max(bbox1.y, bbox2.y) - padding > Math.min(bbox1.y + bbox1.height, bbox2.y + bbox2.height));
|
||||
},
|
||||
|
||||
/**
|
||||
* Natural cubic spline interpolation.
|
||||
* This algorithm runs in linear time.
|
||||
*
|
||||
* @param {Array} points Array of numbers.
|
||||
*/
|
||||
spline: function (points) {
|
||||
var i, j, ln = points.length,
|
||||
nd, d, y, ny,
|
||||
r = 0,
|
||||
zs = new Float32Array(points.length),
|
||||
result = new Float32Array(points.length * 3 - 2);
|
||||
|
||||
zs[0] = 0;
|
||||
zs[ln - 1] = 0;
|
||||
|
||||
for (i = 1; i < ln - 1; i++) {
|
||||
zs[i] = (points[i + 1] + points[i - 1] - 2 * points[i]) - zs[i - 1];
|
||||
r = 1 / (4 - r);
|
||||
zs[i] *= r;
|
||||
}
|
||||
|
||||
for (i = ln - 2; i > 0; i--) {
|
||||
r = 3.732050807568877 + 48.248711305964385 / (-13.928203230275537 + Math.pow(0.07179676972449123, i));
|
||||
zs[i] -= zs[i + 1] * r;
|
||||
}
|
||||
|
||||
ny = points[0];
|
||||
nd = ny - zs[0];
|
||||
for (i = 0, j = 0; i < ln - 1; j += 3) {
|
||||
y = ny;
|
||||
d = nd;
|
||||
i++;
|
||||
ny = points[i];
|
||||
nd = ny - zs[i];
|
||||
result[j] = y;
|
||||
result[j + 1] = (nd + 2 * d) / 3;
|
||||
result[j + 2] = (nd * 2 + d) / 3;
|
||||
}
|
||||
result[j] = ny;
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Calculates bezier curve control anchor points for a particular point in a path, with a
|
||||
* smoothing curve applied. The smoothness of the curve is controlled by the 'value' parameter.
|
||||
* Note that this algorithm assumes that the line being smoothed is normalized going from left
|
||||
* to right; it makes special adjustments assuming this orientation.
|
||||
*
|
||||
* @param {Number} prevX X coordinate of the previous point in the path
|
||||
* @param {Number} prevY Y coordinate of the previous point in the path
|
||||
* @param {Number} curX X coordinate of the current point in the path
|
||||
* @param {Number} curY Y coordinate of the current point in the path
|
||||
* @param {Number} nextX X coordinate of the next point in the path
|
||||
* @param {Number} nextY Y coordinate of the next point in the path
|
||||
* @param {Number} value A value to control the smoothness of the curve; this is used to
|
||||
* divide the distance between points, so a value of 2 corresponds to
|
||||
* half the distance between points (a very smooth line) while higher values
|
||||
* result in less smooth curves. Defaults to 4.
|
||||
* @return {Object} Object containing x1, y1, x2, y2 bezier control anchor points; x1 and y1
|
||||
* are the control point for the curve toward the previous path point, and
|
||||
* x2 and y2 are the control point for the curve toward the next path point.
|
||||
*/
|
||||
getAnchors: function (prevX, prevY, curX, curY, nextX, nextY, value) {
|
||||
value = value || 4;
|
||||
var PI = Math.PI,
|
||||
halfPI = PI / 2,
|
||||
abs = Math.abs,
|
||||
sin = Math.sin,
|
||||
cos = Math.cos,
|
||||
atan = Math.atan,
|
||||
control1Length, control2Length, control1Angle, control2Angle,
|
||||
control1X, control1Y, control2X, control2Y, alpha;
|
||||
|
||||
// Find the length of each control anchor line, by dividing the horizontal distance
|
||||
// between points by the value parameter.
|
||||
control1Length = (curX - prevX) / value;
|
||||
control2Length = (nextX - curX) / value;
|
||||
|
||||
// Determine the angle of each control anchor line. If the middle point is a vertical
|
||||
// turnaround then we force it to a flat horizontal angle to prevent the curve from
|
||||
// dipping above or below the middle point. Otherwise we use an angle that points
|
||||
// toward the previous/next target point.
|
||||
if ((curY >= prevY && curY >= nextY) || (curY <= prevY && curY <= nextY)) {
|
||||
control1Angle = control2Angle = halfPI;
|
||||
} else {
|
||||
control1Angle = atan((curX - prevX) / abs(curY - prevY));
|
||||
if (prevY < curY) {
|
||||
control1Angle = PI - control1Angle;
|
||||
}
|
||||
control2Angle = atan((nextX - curX) / abs(curY - nextY));
|
||||
if (nextY < curY) {
|
||||
control2Angle = PI - control2Angle;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust the calculated angles so they point away from each other on the same line
|
||||
alpha = halfPI - ((control1Angle + control2Angle) % (PI * 2)) / 2;
|
||||
if (alpha > halfPI) {
|
||||
alpha -= PI;
|
||||
}
|
||||
control1Angle += alpha;
|
||||
control2Angle += alpha;
|
||||
|
||||
// Find the control anchor points from the angles and length
|
||||
control1X = curX - control1Length * sin(control1Angle);
|
||||
control1Y = curY + control1Length * cos(control1Angle);
|
||||
control2X = curX + control2Length * sin(control2Angle);
|
||||
control2Y = curY + control2Length * cos(control2Angle);
|
||||
|
||||
// One last adjustment, make sure that no control anchor point extends vertically past
|
||||
// its target prev/next point, as that results in curves dipping above or below and
|
||||
// bending back strangely. If we find this happening we keep the control angle but
|
||||
// reduce the length of the control line so it stays within bounds.
|
||||
if ((curY > prevY && control1Y < prevY) || (curY < prevY && control1Y > prevY)) {
|
||||
control1X += abs(prevY - control1Y) * (control1X - curX) / (control1Y - curY);
|
||||
control1Y = prevY;
|
||||
}
|
||||
if ((curY > nextY && control2Y < nextY) || (curY < nextY && control2Y > nextY)) {
|
||||
control2X -= abs(nextY - control2Y) * (control2X - curX) / (control2Y - curY);
|
||||
control2Y = nextY;
|
||||
}
|
||||
|
||||
return {
|
||||
x1: control1X,
|
||||
y1: control1Y,
|
||||
x2: control2X,
|
||||
y2: control2Y
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Given coordinates of the points, calculates coordinates of a Bezier curve that goes through them.
|
||||
* @param dataX x-coordinates of the points.
|
||||
* @param dataY y-coordinates of the points.
|
||||
* @param value A value to control the smoothness of the curve.
|
||||
* @return {Object} Object holding two arrays, for x and y coordinates of the curve.
|
||||
*/
|
||||
smooth: function (dataX, dataY, value) {
|
||||
var ln = dataX.length,
|
||||
prevX, prevY,
|
||||
curX, curY,
|
||||
nextX, nextY,
|
||||
x, y,
|
||||
smoothX = [], smoothY = [],
|
||||
i, anchors;
|
||||
|
||||
for (i = 0; i < ln - 1; i++) {
|
||||
prevX = dataX[i];
|
||||
prevY = dataY[i];
|
||||
if (i === 0) {
|
||||
x = prevX;
|
||||
y = prevY;
|
||||
smoothX.push(x);
|
||||
smoothY.push(y);
|
||||
if (ln === 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
curX = dataX[i+1];
|
||||
curY = dataY[i+1];
|
||||
nextX = dataX[i+2];
|
||||
nextY = dataY[i+2];
|
||||
if (isNaN(nextX) || isNaN(nextY)) {
|
||||
smoothX.push(x, curX, curX);
|
||||
smoothY.push(y, curY, curY);
|
||||
break;
|
||||
}
|
||||
anchors = this.getAnchors(prevX, prevY, curX, curY, nextX, nextY, value);
|
||||
smoothX.push(x, anchors.x1, curX);
|
||||
smoothY.push(y, anchors.y1, curY);
|
||||
x = anchors.x2;
|
||||
y = anchors.y2;
|
||||
}
|
||||
return {
|
||||
smoothX: smoothX,
|
||||
smoothY: smoothY
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @method
|
||||
* @private
|
||||
* Work around for iOS.
|
||||
* Nested 3d-transforms seems to prevent the redraw inside it until some event is fired.
|
||||
*/
|
||||
updateIOS: Ext.os.is.iOS ? function () {
|
||||
Ext.getBody().createChild({id: 'frame-workaround', style: 'position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; background: rgba(0,0,0,0.001); z-index: 100000'});
|
||||
Ext.draw.Animator.schedule(function () {Ext.get('frame-workaround').destroy();});
|
||||
} : Ext.emptyFn
|
||||
});
|
80
vendor/touch/src/draw/LimitedCache.js
vendored
Normal file
80
vendor/touch/src/draw/LimitedCache.js
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
/**
|
||||
* Limited cache is a size limited cache container that stores limited number of objects.
|
||||
*
|
||||
* When {@link #get} is called, the container will try to find the object in the list.
|
||||
* If failed it will call the {@link #feeder} to create that object. If there are too many
|
||||
* objects in the container, the old ones are removed.
|
||||
*
|
||||
* __Note:__ This is not using a Least Recently Used policy due to simplicity and performance consideration.
|
||||
*/
|
||||
Ext.define("Ext.draw.LimitedCache", {
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Number}
|
||||
* The amount limit of the cache.
|
||||
*/
|
||||
limit: 40,
|
||||
|
||||
/**
|
||||
* @cfg {Function}
|
||||
* Function that generates the object when look-up failed.
|
||||
* @return {Number}
|
||||
*/
|
||||
feeder: function () {
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @cfg {Object}
|
||||
* The scope for {@link #feeder}
|
||||
*/
|
||||
scope: null
|
||||
},
|
||||
cache: null,
|
||||
|
||||
constructor: function (config) {
|
||||
this.cache = {};
|
||||
this.cache.list = [];
|
||||
this.cache.tail = 0;
|
||||
this.initConfig(config);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a cached object.
|
||||
* @param {String} id
|
||||
* @param {Mixed...} args Arguments appended to feeder.
|
||||
* @return {Object}
|
||||
*/
|
||||
get: function (id) {
|
||||
// TODO: Implement cache hit optimization
|
||||
var cache = this.cache,
|
||||
limit = this.getLimit(),
|
||||
feeder = this.getFeeder(),
|
||||
scope = this.getScope() || this;
|
||||
|
||||
if (cache[id]) {
|
||||
return cache[id].value;
|
||||
}
|
||||
if (cache.list[cache.tail]) {
|
||||
delete cache[cache.list[cache.tail].cacheId];
|
||||
}
|
||||
cache[id] = cache.list[cache.tail] = {
|
||||
value: feeder.apply(scope, Array.prototype.slice.call(arguments, 1)),
|
||||
cacheId: id
|
||||
};
|
||||
cache.tail++;
|
||||
if (cache.tail === limit) {
|
||||
cache.tail = 0;
|
||||
}
|
||||
return cache[id].value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all the objects.
|
||||
*/
|
||||
clear: function () {
|
||||
this.cache = {};
|
||||
this.cache.list = [];
|
||||
this.cache.tail = 0;
|
||||
}
|
||||
});
|
809
vendor/touch/src/draw/Matrix.js
vendored
Normal file
809
vendor/touch/src/draw/Matrix.js
vendored
Normal file
|
@ -0,0 +1,809 @@
|
|||
/**
|
||||
* Utility class to calculate [affine transformation](http://en.wikipedia.org/wiki/Affine_transformation) matrix.
|
||||
*
|
||||
* This class is compatible with SVGMatrix except:
|
||||
*
|
||||
* 1. Ext.draw.Matrix is not read only.
|
||||
* 2. Using Number as its components rather than floats.
|
||||
*
|
||||
* Using this class to reduce the severe numeric problem with HTML Canvas and SVG transformation.
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.draw.Matrix', {
|
||||
|
||||
statics: {
|
||||
/**
|
||||
* @static
|
||||
* Return the affine matrix that transform two points (x0, y0) and (x1, y1) to (x0p, y0p) and (x1p, y1p)
|
||||
* @param {Number} x0
|
||||
* @param {Number} y0
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @param {Number} x0p
|
||||
* @param {Number} y0p
|
||||
* @param {Number} x1p
|
||||
* @param {Number} y1p
|
||||
*/
|
||||
createAffineMatrixFromTwoPair: function (x0, y0, x1, y1, x0p, y0p, x1p, y1p) {
|
||||
var dx = x1 - x0,
|
||||
dy = y1 - y0,
|
||||
dxp = x1p - x0p,
|
||||
dyp = y1p - y0p,
|
||||
r = 1 / (dx * dx + dy * dy),
|
||||
a = dx * dxp + dy * dyp,
|
||||
b = dxp * dy - dx * dyp,
|
||||
c = -a * x0 - b * y0,
|
||||
f = b * x0 - a * y0;
|
||||
|
||||
return new this(a * r, -b * r, b * r, a * r, c * r + x0p, f * r + y0p);
|
||||
},
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Return the affine matrix that transform two points (x0, y0) and (x1, y1) to (x0p, y0p) and (x1p, y1p)
|
||||
* @param {Number} x0
|
||||
* @param {Number} y0
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @param {Number} x0p
|
||||
* @param {Number} y0p
|
||||
* @param {Number} x1p
|
||||
* @param {Number} y1p
|
||||
*/
|
||||
createPanZoomFromTwoPair: function (x0, y0, x1, y1, x0p, y0p, x1p, y1p) {
|
||||
if (arguments.length === 2) {
|
||||
return this.createPanZoomFromTwoPair.apply(this, x0.concat(y0));
|
||||
}
|
||||
var dx = x1 - x0,
|
||||
dy = y1 - y0,
|
||||
cx = (x0 + x1) * 0.5,
|
||||
cy = (y0 + y1) * 0.5,
|
||||
dxp = x1p - x0p,
|
||||
dyp = y1p - y0p,
|
||||
cxp = (x0p + x1p) * 0.5,
|
||||
cyp = (y0p + y1p) * 0.5,
|
||||
r = dx * dx + dy * dy,
|
||||
rp = dxp * dxp + dyp * dyp,
|
||||
scale = Math.sqrt(rp / r);
|
||||
|
||||
return new this(scale, 0, 0, scale, cxp - scale * cx, cyp - scale * cy);
|
||||
},
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Create a flyweight to wrap the given array.
|
||||
* The flyweight will directly refer the object and the elements can be changed by other methods.
|
||||
*
|
||||
* Do not hold the instance of flyweight matrix.
|
||||
*
|
||||
* @param {Array} elements
|
||||
* @return {Ext.draw.Matrix}
|
||||
*/
|
||||
fly: (function () {
|
||||
var flyMatrix = null,
|
||||
simplefly = function (elements) {
|
||||
flyMatrix.elements = elements;
|
||||
return flyMatrix;
|
||||
};
|
||||
|
||||
return function (elements) {
|
||||
if (!flyMatrix) {
|
||||
flyMatrix = new Ext.draw.Matrix();
|
||||
}
|
||||
flyMatrix.elements = elements;
|
||||
Ext.draw.Matrix.fly = simplefly;
|
||||
return flyMatrix;
|
||||
};
|
||||
})(),
|
||||
|
||||
/**
|
||||
* @static
|
||||
* Create a matrix from `mat`. If `mat` is already a matrix, returns it.
|
||||
* @param {Mixed} mat
|
||||
* @return {Ext.draw.Matrix}
|
||||
*/
|
||||
create: function (mat) {
|
||||
if (mat instanceof this) {
|
||||
return mat;
|
||||
}
|
||||
return new this(mat);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an affine transform matrix.
|
||||
*
|
||||
* @param {Number} xx Coefficient from x to x
|
||||
* @param {Number} xy Coefficient from x to y
|
||||
* @param {Number} yx Coefficient from y to x
|
||||
* @param {Number} yy Coefficient from y to y
|
||||
* @param {Number} dx Offset of x
|
||||
* @param {Number} dy Offset of y
|
||||
*/
|
||||
constructor: function (xx, xy, yx, yy, dx, dy) {
|
||||
if (xx && xx.length === 6) {
|
||||
this.elements = xx.slice();
|
||||
} else if (xx !== undefined) {
|
||||
this.elements = [xx, xy, yx, yy, dx, dy];
|
||||
} else {
|
||||
this.elements = [1, 0, 0, 1, 0, 0];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepend a matrix onto the current.
|
||||
*
|
||||
* __Note:__ The given transform will come after the current one.
|
||||
*
|
||||
* @param {Number} xx Coefficient from x to x.
|
||||
* @param {Number} xy Coefficient from x to y.
|
||||
* @param {Number} yx Coefficient from y to x.
|
||||
* @param {Number} yy Coefficient from y to y.
|
||||
* @param {Number} dx Offset of x.
|
||||
* @param {Number} dy Offset of y.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
prepend: function (xx, xy, yx, yy, dx, dy) {
|
||||
var elements = this.elements,
|
||||
xx0 = elements[0],
|
||||
xy0 = elements[1],
|
||||
yx0 = elements[2],
|
||||
yy0 = elements[3],
|
||||
dx0 = elements[4],
|
||||
dy0 = elements[5];
|
||||
|
||||
elements[0] = xx * xx0 + yx * xy0;
|
||||
elements[1] = xy * xx0 + yy * xy0;
|
||||
elements[2] = xx * yx0 + yx * yy0;
|
||||
elements[3] = xy * yx0 + yy * yy0;
|
||||
elements[4] = xx * dx0 + yx * dy0 + dx;
|
||||
elements[5] = xy * dx0 + yy * dy0 + dy;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Prepend a matrix onto the current.
|
||||
*
|
||||
* __Note:__ The given transform will come after the current one.
|
||||
* @param {Ext.draw.Matrix} matrix
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
prependMatrix: function (matrix) {
|
||||
return this.prepend.apply(this, matrix.elements);
|
||||
},
|
||||
|
||||
/**
|
||||
* Postpend a matrix onto the current.
|
||||
*
|
||||
* __Note:__ The given transform will come before the current one.
|
||||
*
|
||||
* @param {Number} xx Coefficient from x to x.
|
||||
* @param {Number} xy Coefficient from x to y.
|
||||
* @param {Number} yx Coefficient from y to x.
|
||||
* @param {Number} yy Coefficient from y to y.
|
||||
* @param {Number} dx Offset of x.
|
||||
* @param {Number} dy Offset of y.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
append: function (xx, xy, yx, yy, dx, dy) {
|
||||
var elements = this.elements,
|
||||
xx0 = elements[0],
|
||||
xy0 = elements[1],
|
||||
yx0 = elements[2],
|
||||
yy0 = elements[3],
|
||||
dx0 = elements[4],
|
||||
dy0 = elements[5];
|
||||
|
||||
elements[0] = xx * xx0 + xy * yx0;
|
||||
elements[1] = xx * xy0 + xy * yy0;
|
||||
elements[2] = yx * xx0 + yy * yx0;
|
||||
elements[3] = yx * xy0 + yy * yy0;
|
||||
elements[4] = dx * xx0 + dy * yx0 + dx0;
|
||||
elements[5] = dx * xy0 + dy * yy0 + dy0;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Postpend a matrix onto the current.
|
||||
*
|
||||
* __Note:__ The given transform will come before the current one.
|
||||
*
|
||||
* @param {Ext.draw.Matrix} matrix
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
appendMatrix: function (matrix) {
|
||||
return this.append.apply(this, matrix.elements);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the elements of a Matrix
|
||||
* @param {Number} xx
|
||||
* @param {Number} xy
|
||||
* @param {Number} yx
|
||||
* @param {Number} yy
|
||||
* @param {Number} dx
|
||||
* @param {Number} dy
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
set: function (xx, xy, yx, yy, dx, dy) {
|
||||
var elements = this.elements;
|
||||
|
||||
elements[0] = xx;
|
||||
elements[1] = xy;
|
||||
elements[2] = yx;
|
||||
elements[3] = yy;
|
||||
elements[4] = dx;
|
||||
elements[5] = dy;
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a new matrix represents the opposite transformation of the current one.
|
||||
*
|
||||
* @param {Ext.draw.Matrix} [target] A target matrix. If present, it will receive
|
||||
* the result of inversion to avoid creating a new object.
|
||||
*
|
||||
* @return {Ext.draw.Matrix}
|
||||
*/
|
||||
inverse: function (target) {
|
||||
var elements = this.elements,
|
||||
a = elements[0],
|
||||
b = elements[1],
|
||||
c = elements[2],
|
||||
d = elements[3],
|
||||
e = elements[4],
|
||||
f = elements[5],
|
||||
rDim = 1 / (a * d - b * c);
|
||||
|
||||
a *= rDim;
|
||||
b *= rDim;
|
||||
c *= rDim;
|
||||
d *= rDim;
|
||||
if (target) {
|
||||
target.set(d, -b, -c, a, c * f - d * e, b * e - a * f);
|
||||
return target;
|
||||
} else {
|
||||
return new Ext.draw.Matrix(d, -b, -c, a, c * f - d * e, b * e - a * f);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Translate the matrix.
|
||||
*
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Boolean} [prepend] If `true`, this will transformation be prepended to the matrix.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
translate: function (x, y, prepend) {
|
||||
if (prepend) {
|
||||
return this.prepend(1, 0, 0, 1, x, y);
|
||||
} else {
|
||||
return this.append(1, 0, 0, 1, x, y);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scale the matrix.
|
||||
*
|
||||
* @param {Number} sx
|
||||
* @param {Number} sy
|
||||
* @param {Number} scx
|
||||
* @param {Number} scy
|
||||
* @param {Boolean} [prepend] If `true`, this will transformation be prepended to the matrix.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
scale: function (sx, sy, scx, scy, prepend) {
|
||||
var me = this;
|
||||
|
||||
// null or undefined
|
||||
if (sy == null) {
|
||||
sy = sx;
|
||||
}
|
||||
if (scx === undefined) {
|
||||
scx = 0;
|
||||
}
|
||||
if (scy === undefined) {
|
||||
scy = 0;
|
||||
}
|
||||
|
||||
if (prepend) {
|
||||
return me.prepend(sx, 0, 0, sy, scx - scx * sx, scy - scy * sy);
|
||||
} else {
|
||||
return me.append(sx, 0, 0, sy, scx - scx * sx, scy - scy * sy);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotate the matrix.
|
||||
*
|
||||
* @param {Number} angle Radians to rotate
|
||||
* @param {Number|null} rcx Center of rotation.
|
||||
* @param {Number|null} rcy Center of rotation.
|
||||
* @param {Boolean} [prepend] If `true`, this will transformation be prepended to the matrix.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
rotate: function (angle, rcx, rcy, prepend) {
|
||||
var me = this,
|
||||
cos = Math.cos(angle),
|
||||
sin = Math.sin(angle);
|
||||
|
||||
rcx = rcx || 0;
|
||||
rcy = rcy || 0;
|
||||
|
||||
if (prepend) {
|
||||
return me.prepend(
|
||||
cos, sin,
|
||||
-sin, cos,
|
||||
rcx - cos * rcx + rcy * sin,
|
||||
rcy - cos * rcy - rcx * sin
|
||||
);
|
||||
} else {
|
||||
return me.append(
|
||||
cos, sin,
|
||||
-sin, cos,
|
||||
rcx - cos * rcx + rcy * sin,
|
||||
rcy - cos * rcy - rcx * sin
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotate the matrix by the angle of a vector.
|
||||
*
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Boolean} [prepend] If `true`, this will transformation be prepended to the matrix.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
rotateFromVector: function (x, y, prepend) {
|
||||
var me = this,
|
||||
d = Math.sqrt(x * x + y * y),
|
||||
cos = x / d,
|
||||
sin = y / d;
|
||||
if (prepend) {
|
||||
return me.prepend(cos, sin, -sin, cos, 0, 0);
|
||||
} else {
|
||||
return me.append(cos, sin, -sin, cos, 0, 0);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Clone this matrix.
|
||||
* @return {Ext.draw.Matrix}
|
||||
*/
|
||||
clone: function () {
|
||||
return new Ext.draw.Matrix(this.elements);
|
||||
},
|
||||
|
||||
/**
|
||||
* Horizontally flip the matrix
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
flipX: function () {
|
||||
return this.append(-1, 0, 0, 1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Vertically flip the matrix
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
flipY: function () {
|
||||
return this.append(1, 0, 0, -1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Skew the matrix
|
||||
* @param {Number} angle
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
skewX: function (angle) {
|
||||
return this.append(1, Math.tan(angle), 0, -1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Skew the matrix
|
||||
* @param {Number} angle
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
skewY: function (angle) {
|
||||
return this.append(1, 0, Math.tan(angle), -1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the matrix to identical.
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
reset: function () {
|
||||
return this.set(1, 0, 0, 1, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Split Matrix to `{{devicePixelRatio,c,0},{b,devicePixelRatio,0},{0,0,1}}.{{xx,0,dx},{0,yy,dy},{0,0,1}}`
|
||||
* @return {Object} Object with b,c,d=devicePixelRatio,xx,yy,dx,dy
|
||||
*/
|
||||
precisionCompensate: function (devicePixelRatio, comp) {
|
||||
var elements = this.elements,
|
||||
x2x = elements[0],
|
||||
x2y = elements[1],
|
||||
y2x = elements[2],
|
||||
y2y = elements[3],
|
||||
newDx = elements[4],
|
||||
newDy = elements[5],
|
||||
r = x2y * y2x - x2x * y2y;
|
||||
|
||||
comp.b = devicePixelRatio * x2y / x2x;
|
||||
comp.c = devicePixelRatio * y2x / y2y;
|
||||
comp.d = devicePixelRatio;
|
||||
comp.xx = x2x / devicePixelRatio;
|
||||
comp.yy = y2y / devicePixelRatio;
|
||||
comp.dx = (newDy * x2x * y2x - newDx * x2x * y2y) / r / devicePixelRatio;
|
||||
comp.dy = (newDx * x2y * y2y - newDy * x2x * y2y) / r / devicePixelRatio;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Split Matrix to `{{1,c,0},{b,d,0},{0,0,1}}.{{xx,0,dx},{0,xx,dy},{0,0,1}}`
|
||||
* @return {Object} Object with b,c,d,xx,yy=xx,dx,dy
|
||||
*/
|
||||
precisionCompensateRect: function (devicePixelRatio, comp) {
|
||||
var elements = this.elements,
|
||||
x2x = elements[0],
|
||||
x2y = elements[1],
|
||||
y2x = elements[2],
|
||||
y2y = elements[3],
|
||||
newDx = elements[4],
|
||||
newDy = elements[5],
|
||||
yxOnXx = y2x / x2x;
|
||||
|
||||
comp.b = devicePixelRatio * x2y / x2x;
|
||||
comp.c = devicePixelRatio * yxOnXx;
|
||||
comp.d = devicePixelRatio * y2y / x2x;
|
||||
comp.xx = x2x / devicePixelRatio;
|
||||
comp.yy = x2x / devicePixelRatio;
|
||||
comp.dx = (newDy * y2x - newDx * y2y) / (x2y * yxOnXx - y2y) / devicePixelRatio;
|
||||
comp.dy = -(newDy * x2x - newDx * x2y) / (x2y * yxOnXx - y2y) / devicePixelRatio;
|
||||
},
|
||||
|
||||
/**
|
||||
* Transform point returning the x component of the result.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @return {Number} x component of the result.
|
||||
*/
|
||||
x: function (x, y) {
|
||||
var elements = this.elements;
|
||||
return x * elements[0] + y * elements[2] + elements[4];
|
||||
},
|
||||
|
||||
/**
|
||||
* Transform point returning the y component of the result.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @return {Number} y component of the result.
|
||||
*/
|
||||
y: function (x, y) {
|
||||
var elements = this.elements;
|
||||
|
||||
return x * elements[1] + y * elements[3] + elements[5];
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Number} i
|
||||
* @param {Number} j
|
||||
* @return {String}
|
||||
*/
|
||||
get: function (i, j) {
|
||||
return +this.elements[i + j * 2].toFixed(4);
|
||||
},
|
||||
|
||||
/**
|
||||
* Transform a point to a new array.
|
||||
* @param {Array} point
|
||||
* @return {Array}
|
||||
*/
|
||||
transformPoint: function (point) {
|
||||
var elements = this.elements;
|
||||
|
||||
return [
|
||||
point[0] * elements[0] + point[1] * elements[2] + elements[4],
|
||||
point[0] * elements[1] + point[1] * elements[3] + elements[5]
|
||||
];
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} bbox Given as `{x: Number, y: Number, width: Number, height: Number}`.
|
||||
* @param {Number} [radius]
|
||||
* @param {Object} [target] Optional target object to recieve the result.
|
||||
* Recommended to use it for better gc.
|
||||
*
|
||||
* @return {Object} Object with x, y, width and height.
|
||||
*/
|
||||
transformBBox: function (bbox, radius, target) {
|
||||
var elements = this.elements,
|
||||
l = bbox.x,
|
||||
t = bbox.y,
|
||||
w0 = bbox.width * 0.5,
|
||||
h0 = bbox.height * 0.5,
|
||||
xx = elements[0],
|
||||
xy = elements[1],
|
||||
yx = elements[2],
|
||||
yy = elements[3],
|
||||
cx = l + w0,
|
||||
cy = t + h0,
|
||||
w, h, scales;
|
||||
|
||||
if (radius) {
|
||||
w0 -= radius;
|
||||
h0 -= radius;
|
||||
scales = [
|
||||
Math.sqrt(elements[0] * elements[0] + elements[2] * elements[2]),
|
||||
Math.sqrt(elements[1] * elements[1] + elements[3] * elements[3])
|
||||
];
|
||||
w = Math.abs(w0 * xx) + Math.abs(h0 * yx) + Math.abs(scales[0] * radius);
|
||||
h = Math.abs(w0 * xy) + Math.abs(h0 * yy) + Math.abs(scales[1] * radius);
|
||||
} else {
|
||||
w = Math.abs(w0 * xx) + Math.abs(h0 * yx);
|
||||
h = Math.abs(w0 * xy) + Math.abs(h0 * yy);
|
||||
}
|
||||
|
||||
if (!target) {
|
||||
target = {};
|
||||
}
|
||||
|
||||
target.x = cx * xx + cy * yx + elements[4] - w;
|
||||
target.y = cx * xy + cy * yy + elements[5] - h;
|
||||
target.width = w + w;
|
||||
target.height = h + h;
|
||||
|
||||
return target;
|
||||
},
|
||||
|
||||
/**
|
||||
* Transform a list for points.
|
||||
*
|
||||
* __Note:__ will change the original list but not points inside it.
|
||||
* @param {Array} list
|
||||
* @return {Array} list
|
||||
*/
|
||||
transformList: function (list) {
|
||||
var elements = this.elements,
|
||||
xx = elements[0], yx = elements[2], dx = elements[4],
|
||||
xy = elements[1], yy = elements[3], dy = elements[5],
|
||||
ln = list.length,
|
||||
p, i;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
p = list[i];
|
||||
list[i] = [
|
||||
p[0] * xx + p[1] * yx + dx,
|
||||
p[0] * xy + p[1] * yy + dy
|
||||
];
|
||||
}
|
||||
return list;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether this matrix is an identity matrix (no transform).
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isIdentity: function () {
|
||||
var elements = this.elements;
|
||||
|
||||
return elements[0] === 1 &&
|
||||
elements[1] === 0 &&
|
||||
elements[2] === 0 &&
|
||||
elements[3] === 1 &&
|
||||
elements[4] === 0 &&
|
||||
elements[5] === 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if this matrix has the same values as another matrix.
|
||||
* @param {Ext.draw.Matrix} matrix
|
||||
* @return {Boolean}
|
||||
*/
|
||||
equals: function (matrix) {
|
||||
var elements = this.elements,
|
||||
elements2 = matrix.elements;
|
||||
|
||||
return elements[0] === elements2[0] &&
|
||||
elements[1] === elements2[1] &&
|
||||
elements[2] === elements2[2] &&
|
||||
elements[3] === elements2[3] &&
|
||||
elements[4] === elements2[4] &&
|
||||
elements[5] === elements2[5];
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an array of elements by horizontal order (xx,yx,dx,yx,yy,dy).
|
||||
* @return {Array}
|
||||
*/
|
||||
toArray: function () {
|
||||
var elements = this.elements;
|
||||
return [elements[0], elements[2], elements[4], elements[1], elements[3], elements[5]];
|
||||
},
|
||||
|
||||
/**
|
||||
* Create an array of elements by vertical order (xx,xy,yx,yy,dx,dy).
|
||||
* @return {Array|String}
|
||||
*/
|
||||
toVerticalArray: function () {
|
||||
return this.elements.slice();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an array of elements.
|
||||
* The numbers are rounded to keep only 4 decimals.
|
||||
* @return {Array}
|
||||
*/
|
||||
toString: function () {
|
||||
var me = this;
|
||||
return [me.get(0, 0), me.get(0, 1), me.get(1, 0), me.get(1, 1), me.get(2, 0), me.get(2, 1)].join(',');
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply the matrix to a drawing context.
|
||||
* @param {Object} ctx
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
toContext: function (ctx) {
|
||||
ctx.transform.apply(ctx, this.elements);
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a string that can be used as transform attribute in SVG.
|
||||
* @return {String}
|
||||
*/
|
||||
toSvg: function () {
|
||||
var elements = this.elements;
|
||||
// The reason why we cannot use `.join` is the `1e5` form is not accepted in svg.
|
||||
return "matrix(" +
|
||||
elements[0].toFixed(9) + ',' +
|
||||
elements[1].toFixed(9) + ',' +
|
||||
elements[2].toFixed(9) + ',' +
|
||||
elements[3].toFixed(9) + ',' +
|
||||
elements[4].toFixed(9) + ',' +
|
||||
elements[5].toFixed(9) +
|
||||
")";
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the x scale of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getScaleX: function () {
|
||||
var elements = this.elements;
|
||||
return Math.sqrt(elements[0] * elements[0] + elements[2] * elements[2]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the y scale of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getScaleY: function () {
|
||||
var elements = this.elements;
|
||||
return Math.sqrt(elements[1] * elements[1] + elements[3] * elements[3]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get x-to-x component of the matrix
|
||||
* @return {Number}
|
||||
*/
|
||||
getXX: function () {
|
||||
return this.elements[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get x-to-y component of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getXY: function () {
|
||||
return this.elements[1];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get y-to-x component of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getYX: function () {
|
||||
return this.elements[2];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get y-to-y component of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getYY: function () {
|
||||
return this.elements[3];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get offset x component of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getDX: function () {
|
||||
return this.elements[4];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get offset y component of the matrix.
|
||||
* @return {Number}
|
||||
*/
|
||||
getDY: function () {
|
||||
return this.elements[5];
|
||||
},
|
||||
|
||||
/**
|
||||
* Split matrix into Translate, Scale, Shear, and Rotate.
|
||||
* @return {Object}
|
||||
*/
|
||||
split: function () {
|
||||
var el = this.elements,
|
||||
xx = el[0],
|
||||
xy = el[1],
|
||||
yx = el[2],
|
||||
yy = el[3],
|
||||
out = {
|
||||
translateX: el[4],
|
||||
translateY: el[5]
|
||||
};
|
||||
out.scaleX = Math.sqrt(xx * xx + yx * yx);
|
||||
out.shear = (xx * xy + yx * yy) / out.scaleX;
|
||||
xy -= out.shear * xx;
|
||||
yy -= out.shear * yx;
|
||||
out.scaleY = Math.sqrt(xy * xy + yy * yy);
|
||||
out.shear /= out.scaleY;
|
||||
out.rotation = -Math.atan2(yx / out.scaleX, xy / out.scaleY);
|
||||
out.isSimple = Math.abs(out.shear) < 1e-9 && (!out.rotation || Math.abs(out.scaleX - out.scaleY) < 1e-9);
|
||||
return out;
|
||||
}
|
||||
}, function () {
|
||||
function registerName(properties, name, i) {
|
||||
properties[name] = {
|
||||
get: function () {
|
||||
return this.elements[i];
|
||||
},
|
||||
set: function (val) {
|
||||
this.elements[i] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Compatibility with SVGMatrix
|
||||
// https://developer.mozilla.org/en/DOM/SVGMatrix
|
||||
if (Object.defineProperties) {
|
||||
var properties = {};
|
||||
/**
|
||||
* @property {Number} a Get x-to-x component of the matrix. Avoid using it for performance consideration.
|
||||
* Use {@link #getXX} instead.
|
||||
*/
|
||||
registerName(properties, 'a', 0);
|
||||
|
||||
// TODO: Help me finish this.
|
||||
registerName(properties, 'b', 1);
|
||||
registerName(properties, 'c', 2);
|
||||
registerName(properties, 'd', 3);
|
||||
registerName(properties, 'e', 4);
|
||||
registerName(properties, 'f', 5);
|
||||
Object.defineProperties(this.prototype, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Postpend a matrix onto the current.
|
||||
*
|
||||
* __Note:__ The given transform will come before the current one.
|
||||
*
|
||||
* @method
|
||||
* @param {Ext.draw.Matrix} matrix
|
||||
* @return {Ext.draw.Matrix} this
|
||||
*/
|
||||
this.prototype.multiply = this.prototype.appendMatrix;
|
||||
// <deprecated product=touch since=2.2>
|
||||
this.prototype.postpend = this.prototype.append;
|
||||
this.prototype.postpendMatrix = this.prototype.appendMatrix;
|
||||
// </deprecated>
|
||||
});
|
1181
vendor/touch/src/draw/Path.js
vendored
Normal file
1181
vendor/touch/src/draw/Path.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
402
vendor/touch/src/draw/SegmentTree.js
vendored
Normal file
402
vendor/touch/src/draw/SegmentTree.js
vendored
Normal file
|
@ -0,0 +1,402 @@
|
|||
/**
|
||||
* This class we summarize the data and returns it when required.
|
||||
*/
|
||||
Ext.define("Ext.draw.SegmentTree", {
|
||||
|
||||
config: {
|
||||
strategy: "double"
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} result
|
||||
* @param {Number} last
|
||||
* @param {Number} dataX
|
||||
* @param {Number} dataOpen
|
||||
* @param {Number} dataHigh
|
||||
* @param {Number} dataLow
|
||||
* @param {Number} dataClose
|
||||
*/
|
||||
time: function (result, last, dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
||||
var start = 0, lastOffset, lastOffsetEnd,
|
||||
minimum = new Date(dataX[result.startIdx[0]]),
|
||||
maximum = new Date(dataX[result.endIdx[last - 1]]),
|
||||
extDate = Ext.Date,
|
||||
units = [
|
||||
[extDate.MILLI, 1, 'ms1', null],
|
||||
[extDate.MILLI, 2, 'ms2', 'ms1'],
|
||||
[extDate.MILLI, 5, 'ms5', 'ms1'],
|
||||
[extDate.MILLI, 10, 'ms10', 'ms5'],
|
||||
[extDate.MILLI, 50, 'ms50', 'ms10'],
|
||||
[extDate.MILLI, 100, 'ms100', 'ms50'],
|
||||
[extDate.MILLI, 500, 'ms500', 'ms100'],
|
||||
[extDate.SECOND, 1, 's1', 'ms500'],
|
||||
[extDate.SECOND, 10, 's10', 's1'],
|
||||
[extDate.SECOND, 30, 's30', 's10'],
|
||||
[extDate.MINUTE, 1, 'mi1', 's10'],
|
||||
[extDate.MINUTE, 5, 'mi5', 'mi1'],
|
||||
[extDate.MINUTE, 10, 'mi10', 'mi5'],
|
||||
[extDate.MINUTE, 30, 'mi30', 'mi10'],
|
||||
[extDate.HOUR, 1, 'h1', 'mi30'],
|
||||
[extDate.HOUR, 6, 'h6', 'h1'],
|
||||
[extDate.HOUR, 12, 'h12', 'h6'],
|
||||
[extDate.DAY, 1, 'd1', 'h12'],
|
||||
[extDate.DAY, 7, 'd7', 'd1'],
|
||||
[extDate.MONTH, 1, 'mo1', 'd1'],
|
||||
[extDate.MONTH, 3, 'mo3', 'mo1'],
|
||||
[extDate.MONTH, 6, 'mo6', 'mo3'],
|
||||
[extDate.YEAR, 1, 'y1', 'mo3'],
|
||||
[extDate.YEAR, 5, 'y5', 'y1'],
|
||||
[extDate.YEAR, 10, 'y10', 'y5'],
|
||||
[extDate.YEAR, 100, 'y100', 'y10']
|
||||
], unitIdx, currentUnit,
|
||||
plainStart = start,
|
||||
plainEnd = last,
|
||||
first = false,
|
||||
startIdxs = result.startIdx,
|
||||
endIdxs = result.endIdx,
|
||||
minIdxs = result.minIdx,
|
||||
maxIdxs = result.maxIdx,
|
||||
opens = result.open,
|
||||
closes = result.close,
|
||||
minXs = result.minX,
|
||||
minYs = result.minY,
|
||||
maxXs = result.maxX,
|
||||
maxYs = result.maxY,
|
||||
i, current;
|
||||
|
||||
for (unitIdx = 0; last > start + 1 && unitIdx < units.length; unitIdx++) {
|
||||
minimum = new Date(dataX[startIdxs[0]]);
|
||||
currentUnit = units[unitIdx];
|
||||
minimum = extDate.align(minimum, currentUnit[0], currentUnit[1]);
|
||||
if (extDate.diff(minimum, maximum, currentUnit[0]) > dataX.length * 2 * currentUnit[1]) {
|
||||
continue;
|
||||
}
|
||||
if (currentUnit[3] && result.map['time_' + currentUnit[3]]) {
|
||||
lastOffset = result.map['time_' + currentUnit[3]][0];
|
||||
lastOffsetEnd = result.map['time_' + currentUnit[3]][1];
|
||||
} else {
|
||||
lastOffset = plainStart;
|
||||
lastOffsetEnd = plainEnd;
|
||||
}
|
||||
|
||||
start = last;
|
||||
current = minimum;
|
||||
first = true;
|
||||
|
||||
startIdxs[last] = startIdxs[lastOffset];
|
||||
endIdxs[last] = endIdxs[lastOffset];
|
||||
minIdxs[last] = minIdxs[lastOffset];
|
||||
maxIdxs[last] = maxIdxs[lastOffset];
|
||||
opens[last] = opens[lastOffset];
|
||||
closes[last] = closes[lastOffset];
|
||||
minXs[last] = minXs[lastOffset];
|
||||
minYs[last] = minYs[lastOffset];
|
||||
maxXs[last] = maxXs[lastOffset];
|
||||
maxYs[last] = maxYs[lastOffset];
|
||||
current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);
|
||||
|
||||
for (i = lastOffset + 1; i < lastOffsetEnd; i++) {
|
||||
if (dataX[endIdxs[i]] < +current) {
|
||||
endIdxs[last] = endIdxs[i];
|
||||
closes[last] = closes[i];
|
||||
if (maxYs[i] > maxYs[last]) {
|
||||
maxYs[last] = maxYs[i];
|
||||
maxXs[last] = maxXs[i];
|
||||
maxIdxs[last] = maxIdxs[i];
|
||||
}
|
||||
if (minYs[i] < minYs[last]) {
|
||||
minYs[last] = minYs[i];
|
||||
minXs[last] = minXs[i];
|
||||
minIdxs[last] = minIdxs[i];
|
||||
}
|
||||
} else {
|
||||
last++;
|
||||
startIdxs[last] = startIdxs[i];
|
||||
endIdxs[last] = endIdxs[i];
|
||||
minIdxs[last] = minIdxs[i];
|
||||
maxIdxs[last] = maxIdxs[i];
|
||||
opens[last] = opens[i];
|
||||
closes[last] = closes[i];
|
||||
minXs[last] = minXs[i];
|
||||
minYs[last] = minYs[i];
|
||||
maxXs[last] = maxXs[i];
|
||||
maxYs[last] = maxYs[i];
|
||||
current = Ext.Date.add(current, currentUnit[0], currentUnit[1]);
|
||||
}
|
||||
}
|
||||
if (last > start) {
|
||||
result.map['time_' + currentUnit[2]] = [start, last];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} result
|
||||
* @param {Number} position
|
||||
* @param {Number} dataX
|
||||
* @param {Number} dataOpen
|
||||
* @param {Number} dataHigh
|
||||
* @param {Number} dataLow
|
||||
* @param {Number} dataClose
|
||||
*/
|
||||
"double": function (result, position, dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
||||
var offset = 0, lastOffset, step = 1,
|
||||
i,
|
||||
startIdx,
|
||||
endIdx,
|
||||
minIdx,
|
||||
maxIdx,
|
||||
open,
|
||||
close,
|
||||
minX,
|
||||
minY,
|
||||
maxX,
|
||||
maxY;
|
||||
while (position > offset + 1) {
|
||||
lastOffset = offset;
|
||||
offset = position;
|
||||
step += step;
|
||||
for (i = lastOffset; i < offset; i += 2) {
|
||||
if (i === offset - 1) {
|
||||
startIdx = result.startIdx[i];
|
||||
endIdx = result.endIdx[i];
|
||||
minIdx = result.minIdx[i];
|
||||
maxIdx = result.maxIdx[i];
|
||||
open = result.open[i];
|
||||
close = result.close[i];
|
||||
minX = result.minX[i];
|
||||
minY = result.minY[i];
|
||||
maxX = result.maxX[i];
|
||||
maxY = result.maxY[i];
|
||||
} else {
|
||||
|
||||
startIdx = result.startIdx[i];
|
||||
endIdx = result.endIdx[i + 1];
|
||||
open = result.open[i];
|
||||
close = result.close[i];
|
||||
if (result.minY[i] <= result.minY[i + 1]) {
|
||||
minIdx = result.minIdx[i];
|
||||
minX = result.minX[i];
|
||||
minY = result.minY[i];
|
||||
} else {
|
||||
minIdx = result.minIdx[i + 1];
|
||||
minX = result.minX[i + 1];
|
||||
minY = result.minY[i + 1];
|
||||
}
|
||||
if (result.maxY[i] >= result.maxY[i + 1]) {
|
||||
maxIdx = result.maxIdx[i];
|
||||
maxX = result.maxX[i];
|
||||
maxY = result.maxY[i];
|
||||
} else {
|
||||
maxIdx = result.maxIdx[i + 1];
|
||||
maxX = result.maxX[i + 1];
|
||||
maxY = result.maxY[i + 1];
|
||||
}
|
||||
}
|
||||
result.startIdx[position] = startIdx;
|
||||
result.endIdx[position] = endIdx;
|
||||
result.minIdx[position] = minIdx;
|
||||
result.maxIdx[position] = maxIdx;
|
||||
result.open[position] = open;
|
||||
result.close[position] = close;
|
||||
result.minX[position] = minX;
|
||||
result.minY[position] = minY;
|
||||
result.maxX[position] = maxX;
|
||||
result.maxY[position] = maxY;
|
||||
position++;
|
||||
}
|
||||
result.map['double_' + step] = [offset, position];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
none: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @param {Number} dataX
|
||||
* @param {Number} dataOpen
|
||||
* @param {Number} dataHigh
|
||||
* @param {Number} dataLow
|
||||
* @param {Number} dataClose
|
||||
* @return {Object}
|
||||
*/
|
||||
aggregateData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
||||
var length = dataX.length,
|
||||
startIdx = [],
|
||||
endIdx = [],
|
||||
minIdx = [],
|
||||
maxIdx = [],
|
||||
open = [],
|
||||
minX = [],
|
||||
minY = [],
|
||||
maxX = [],
|
||||
maxY = [],
|
||||
close = [],
|
||||
result = {
|
||||
startIdx: startIdx,
|
||||
endIdx: endIdx,
|
||||
minIdx: minIdx,
|
||||
maxIdx: maxIdx,
|
||||
open: open,
|
||||
minX: minX,
|
||||
minY: minY,
|
||||
maxX: maxX,
|
||||
maxY: maxY,
|
||||
close: close
|
||||
},
|
||||
i;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
startIdx[i] = i;
|
||||
endIdx[i] = i;
|
||||
minIdx[i] = i;
|
||||
maxIdx[i] = i;
|
||||
open[i] = dataOpen[i];
|
||||
minX[i] = dataX[i];
|
||||
minY[i] = dataLow[i];
|
||||
maxX[i] = dataX[i];
|
||||
maxY[i] = dataHigh[i];
|
||||
close[i] = dataClose[i];
|
||||
}
|
||||
|
||||
result.map = {
|
||||
original: [0, length]
|
||||
};
|
||||
if (length) {
|
||||
this[this.getStrategy()](result, length, dataX, dataOpen, dataHigh, dataLow, dataClose);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} items
|
||||
* @param {Number} start
|
||||
* @param {Number} end
|
||||
* @param {Number} key
|
||||
* @return {*}
|
||||
*/
|
||||
binarySearchMin: function (items, start, end, key) {
|
||||
var dx = this.dataX;
|
||||
if (key <= dx[items.startIdx[0]]) {
|
||||
return start;
|
||||
}
|
||||
if (key >= dx[items.startIdx[end - 1]]) {
|
||||
return end - 1;
|
||||
}
|
||||
while (start + 1 < end) {
|
||||
var mid = (start + end) >> 1,
|
||||
val = dx[items.startIdx[mid]];
|
||||
if (val === key) {
|
||||
return mid;
|
||||
} else if (val < key) {
|
||||
start = mid;
|
||||
} else {
|
||||
end = mid;
|
||||
}
|
||||
}
|
||||
return start;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} items
|
||||
* @param {Number} start
|
||||
* @param {Number} end
|
||||
* @param {Number} key
|
||||
* @return {*}
|
||||
*/
|
||||
binarySearchMax: function (items, start, end, key) {
|
||||
var dx = this.dataX;
|
||||
if (key <= dx[items.endIdx[0]]) {
|
||||
return start;
|
||||
}
|
||||
if (key >= dx[items.endIdx[end - 1]]) {
|
||||
return end - 1;
|
||||
}
|
||||
while (start + 1 < end) {
|
||||
var mid = (start + end) >> 1,
|
||||
val = dx[items.endIdx[mid]];
|
||||
if (val === key) {
|
||||
return mid;
|
||||
} else if (val < key) {
|
||||
start = mid;
|
||||
} else {
|
||||
end = mid;
|
||||
}
|
||||
}
|
||||
return end;
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
this.initConfig(config);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the data of the segment tree.
|
||||
* @param {Number} dataX
|
||||
* @param {Number} dataOpen
|
||||
* @param {Number} dataHigh
|
||||
* @param {Number} dataLow
|
||||
* @param {Number} dataClose
|
||||
*/
|
||||
setData: function (dataX, dataOpen, dataHigh, dataLow, dataClose) {
|
||||
if (!dataHigh) {
|
||||
dataClose = dataLow = dataHigh = dataOpen;
|
||||
}
|
||||
this.dataX = dataX;
|
||||
this.dataOpen = dataOpen;
|
||||
this.dataHigh = dataHigh;
|
||||
this.dataLow = dataLow;
|
||||
this.dataClose = dataClose;
|
||||
if (dataX.length === dataHigh.length &&
|
||||
dataX.length === dataLow.length) {
|
||||
this.cache = this.aggregateData(dataX, dataOpen, dataHigh, dataLow, dataClose);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the minimum range of data that fits the given range and step size.
|
||||
*
|
||||
* @param {Number} min
|
||||
* @param {Number} max
|
||||
* @param {Number} estStep
|
||||
* @return {Object} The aggregation information.
|
||||
* @return {Number} return.start
|
||||
* @return {Number} return.end
|
||||
* @return {Object} return.data The aggregated data
|
||||
*/
|
||||
getAggregation: function (min, max, estStep) {
|
||||
if (!this.cache) {
|
||||
return null;
|
||||
}
|
||||
var minStep = Infinity,
|
||||
range = this.dataX[this.dataX.length - 1] - this.dataX[0],
|
||||
cacheMap = this.cache.map,
|
||||
result = cacheMap.original,
|
||||
name, positions, ln, step, minIdx, maxIdx;
|
||||
|
||||
for (name in cacheMap) {
|
||||
positions = cacheMap[name];
|
||||
ln = positions[1] - positions[0] - 1;
|
||||
step = range / ln;
|
||||
if (estStep <= step && step < minStep) {
|
||||
result = positions;
|
||||
minStep = step;
|
||||
}
|
||||
}
|
||||
minIdx = Math.max(this.binarySearchMin(this.cache, result[0], result[1], min), result[0]);
|
||||
maxIdx = Math.min(this.binarySearchMax(this.cache, result[0], result[1], max) + 1, result[1]);
|
||||
return {
|
||||
data: this.cache,
|
||||
start: minIdx,
|
||||
end: maxIdx
|
||||
};
|
||||
}
|
||||
});
|
175
vendor/touch/src/draw/Solver.js
vendored
Normal file
175
vendor/touch/src/draw/Solver.js
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
(function () {
|
||||
var PI2_3 = 2.0943951023931953/* 120 Deg */,
|
||||
abs = Math.abs,
|
||||
sin = Math.cos,
|
||||
cos = Math.cos,
|
||||
acos = Math.acos,
|
||||
sqrt = Math.sqrt,
|
||||
exp = Math.exp,
|
||||
log = Math.log;
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Singleton Class that provides methods to solve cubic equation.
|
||||
*/
|
||||
Ext.define("Ext.draw.Solver", {
|
||||
singleton: true,
|
||||
/**
|
||||
* Cubic root of number
|
||||
* @param {Number} number
|
||||
*/
|
||||
cubicRoot: function (number) {
|
||||
if (number > 0) {
|
||||
return exp(log(number) / 3);
|
||||
} else if (number < 0) {
|
||||
return -exp(log(-number) / 3);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the function f(x) = a * x + b and solver for f(x) = y
|
||||
* @param {Number} a
|
||||
* @param {Number} b
|
||||
*/
|
||||
linearFunction: function (a, b) {
|
||||
var result;
|
||||
if (a === 0) {
|
||||
result = function (t) {
|
||||
return b;
|
||||
};
|
||||
result.solve = function (y) {
|
||||
// if y == d there should be a real root
|
||||
// but we can ignore it for geometry calculations.
|
||||
return [];
|
||||
};
|
||||
} else {
|
||||
result = function (t) {
|
||||
return a * t + b;
|
||||
};
|
||||
result.solve = function (y) {
|
||||
return [(y - b) / a];
|
||||
};
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the function f(x) = a * x ^ 2 + b * x + c and solver for f(x) = y
|
||||
*
|
||||
* @param {Number} a
|
||||
* @param {Number} b
|
||||
* @param {Number} c
|
||||
*/
|
||||
quadraticFunction: function (a, b, c) {
|
||||
var result;
|
||||
if (a === 0) {
|
||||
return this.linearFunction(b, c);
|
||||
} else {
|
||||
// Quadratic equation.
|
||||
result = function (t) {
|
||||
return (a * t + b) * t + c;
|
||||
};
|
||||
var delta0temp = b * b - 4 * a * c,
|
||||
delta = function (y) {
|
||||
return delta0temp + 4 * a * y;
|
||||
}, solveTemp0 = 1 / a * 0.5,
|
||||
solveTemp1 = -solveTemp0 * b;
|
||||
solveTemp0 = abs(solveTemp0);
|
||||
result.solve = function (y) {
|
||||
var deltaTemp = delta(y);
|
||||
if (deltaTemp < 0) {
|
||||
return [];
|
||||
}
|
||||
deltaTemp = sqrt(deltaTemp);
|
||||
// have to distinct roots here.
|
||||
return [solveTemp1 - deltaTemp * solveTemp0, solveTemp1 + deltaTemp * solveTemp0];
|
||||
};
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the function f(x) = a * x^3 + b * x^2 + c * x + d and solver for f(x) = y
|
||||
* @param {Number} a
|
||||
* @param {Number} b
|
||||
* @param {Number} c
|
||||
* @param {Number} d
|
||||
*/
|
||||
cubicFunction: function (a, b, c, d) {
|
||||
var result;
|
||||
if (a === 0) {
|
||||
return this.quadraticFunction(b, c, d);
|
||||
} else {
|
||||
result = function (t) {
|
||||
return ((a * t + b) * t + c) * t + d;
|
||||
};
|
||||
|
||||
var b_a_3 = b / a / 3,
|
||||
c_a = c / a,
|
||||
d_a = d / a,
|
||||
b2 = b_a_3 * b_a_3,
|
||||
deltaTemp0 = (b_a_3 * c_a - d_a) * 0.5 - b_a_3 * b2,
|
||||
deltaTemp1 = b2 - c_a / 3,
|
||||
deltaTemp13 = deltaTemp1 * deltaTemp1 * deltaTemp1;
|
||||
|
||||
if (deltaTemp1 === 0) {
|
||||
result.solve = function (y) {
|
||||
return [-b_a_3 + this.cubicRoot(deltaTemp0 * 2 + y / a)];
|
||||
};
|
||||
} else {
|
||||
if (deltaTemp1 > 0) {
|
||||
var deltaTemp1_2 = sqrt(deltaTemp1),
|
||||
deltaTemp13_2 = deltaTemp1_2 * deltaTemp1_2 * deltaTemp1_2;
|
||||
deltaTemp1_2 += deltaTemp1_2;
|
||||
}
|
||||
result.solve = function (y) {
|
||||
y /= a;
|
||||
var d0 = deltaTemp0 + y * 0.5,
|
||||
deltaTemp = d0 * d0 - deltaTemp13;
|
||||
if (deltaTemp > 0) {
|
||||
deltaTemp = sqrt(deltaTemp);
|
||||
return [-b_a_3 + this.cubicRoot(d0 + deltaTemp) + this.cubicRoot(d0 - deltaTemp)];
|
||||
} else if (deltaTemp === 0) {
|
||||
var cr = this.cubicRoot(d0),
|
||||
root0 = -b_a_3 - cr;
|
||||
if (d0 >= 0) {
|
||||
return [root0, root0, -b_a_3 + 2 * cr];
|
||||
} else {
|
||||
return [-b_a_3 + 2 * cr, root0, root0];
|
||||
}
|
||||
} else {
|
||||
var theta = acos(d0 / deltaTemp13_2) / 3,
|
||||
ra = deltaTemp1_2 * cos(theta) - b_a_3,
|
||||
rb = deltaTemp1_2 * cos(theta + PI2_3) - b_a_3,
|
||||
rc = deltaTemp1_2 * cos(theta - PI2_3) - b_a_3;
|
||||
if (ra < rb) {
|
||||
if (rb < rc) {
|
||||
return [ra, rb, rc];
|
||||
} else if (ra < rc) {
|
||||
return[ra, rc, rb];
|
||||
} else {
|
||||
return [rc, ra, rb];
|
||||
}
|
||||
} else {
|
||||
if (ra < rc) {
|
||||
return [rb, ra, rc];
|
||||
} else if (rb < rc) {
|
||||
return [rb, rc, ra];
|
||||
} else {
|
||||
return [rc, rb, ra];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
createBezierSolver: function (a, b, c, d) {
|
||||
return this.cubicFunction(3 * (b - c) + d - a, 3 * (a - 2 * b + c), 3 * (b - a), a);
|
||||
}
|
||||
});
|
||||
})();
|
622
vendor/touch/src/draw/Surface.js
vendored
Normal file
622
vendor/touch/src/draw/Surface.js
vendored
Normal file
|
@ -0,0 +1,622 @@
|
|||
/**
|
||||
* A Surface is an interface to render methods inside a draw {@link Ext.draw.Component}.
|
||||
* A Surface contains methods to render sprites, get bounding boxes of sprites, add
|
||||
* sprites to the canvas, initialize other graphic components, etc. One of the most used
|
||||
* methods for this class is the `add` method, to add Sprites to the surface.
|
||||
*
|
||||
* Most of the Surface methods are abstract and they have a concrete implementation
|
||||
* in Canvas or SVG engines.
|
||||
*
|
||||
* A Surface instance can be accessed as a property of a draw component. For example:
|
||||
*
|
||||
* drawComponent.getSurface('main').add({
|
||||
* type: 'circle',
|
||||
* fill: '#ffc',
|
||||
* radius: 100,
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* });
|
||||
*
|
||||
* The configuration object passed in the `add` method is the same as described in the {@link Ext.draw.sprite.Sprite}
|
||||
* class documentation.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* drawComponent.getSurface('main').add([
|
||||
* {
|
||||
* type: 'circle',
|
||||
* radius: 10,
|
||||
* fill: '#f00',
|
||||
* x: 10,
|
||||
* y: 10
|
||||
* },
|
||||
* {
|
||||
* type: 'circle',
|
||||
* radius: 10,
|
||||
* fill: '#0f0',
|
||||
* x: 50,
|
||||
* y: 50
|
||||
* },
|
||||
* {
|
||||
* type: 'circle',
|
||||
* radius: 10,
|
||||
* fill: '#00f',
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* },
|
||||
* {
|
||||
* type: 'rect',
|
||||
* radius: 10,
|
||||
* x: 10,
|
||||
* y: 10
|
||||
* },
|
||||
* {
|
||||
* type: 'rect',
|
||||
* radius: 10,
|
||||
* x: 50,
|
||||
* y: 50
|
||||
* },
|
||||
* {
|
||||
* type: 'rect',
|
||||
* radius: 10,
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* }
|
||||
* ]);
|
||||
*
|
||||
*/
|
||||
Ext.define('Ext.draw.Surface', {
|
||||
extend: 'Ext.Component',
|
||||
xtype: 'surface',
|
||||
|
||||
requires: [
|
||||
'Ext.draw.sprite.*',
|
||||
'Ext.draw.gradient.*',
|
||||
'Ext.draw.sprite.AttributeDefinition',
|
||||
'Ext.draw.Matrix',
|
||||
'Ext.draw.Draw'
|
||||
],
|
||||
|
||||
uses: [
|
||||
"Ext.draw.engine.Canvas"
|
||||
],
|
||||
|
||||
defaultIdPrefix: 'ext-surface-',
|
||||
|
||||
/**
|
||||
* The reported device pixel density.
|
||||
*/
|
||||
devicePixelRatio: window.devicePixelRatio || 1,
|
||||
|
||||
statics: {
|
||||
/**
|
||||
* Stably sort the list of sprites by their zIndex.
|
||||
* TODO: Improve the performance. Reduce gc impact.
|
||||
* @param {Array} list
|
||||
*/
|
||||
stableSort: function (list) {
|
||||
if (list.length < 2) {
|
||||
return;
|
||||
}
|
||||
var keys = {}, sortedKeys, result = [], i, ln, zIndex;
|
||||
|
||||
for (i = 0, ln = list.length; i < ln; i++) {
|
||||
zIndex = list[i].attr.zIndex;
|
||||
if (!keys[zIndex]) {
|
||||
keys[zIndex] = [list[i]];
|
||||
} else {
|
||||
keys[zIndex].push(list[i]);
|
||||
}
|
||||
}
|
||||
sortedKeys = Ext.Object.getKeys(keys).sort(function (a, b) {return a - b;});
|
||||
for (i = 0, ln = sortedKeys.length; i < ln; i++) {
|
||||
result.push.apply(result, keys[sortedKeys[i]]);
|
||||
}
|
||||
for (i = 0, ln = list.length; i < ln; i++) {
|
||||
list[i] = result[i];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Array}
|
||||
* The region of the surface related to its component.
|
||||
*/
|
||||
region: null,
|
||||
|
||||
/**
|
||||
* @cfg {Object}
|
||||
* Background sprite config of the surface.
|
||||
*/
|
||||
background: null,
|
||||
|
||||
/**
|
||||
* @cfg {Array}
|
||||
* Array of sprite instances.
|
||||
*/
|
||||
items: [],
|
||||
|
||||
/**
|
||||
* @cfg {Boolean}
|
||||
* Indicates whether the surface needs redraw.
|
||||
*/
|
||||
dirty: false
|
||||
},
|
||||
|
||||
dirtyPredecessor: 0,
|
||||
|
||||
constructor: function (config) {
|
||||
var me = this;
|
||||
|
||||
me.predecessors = [];
|
||||
me.successors = [];
|
||||
// The `pendingRenderFrame` flag is used to indicate that `predecessors` (surfaces that should render first)
|
||||
// are dirty, and to call `renderFrame` when all `predecessors` have their `renderFrame` called
|
||||
// (i.e. not dirty anymore).
|
||||
me.pendingRenderFrame = false;
|
||||
me.map = {};
|
||||
|
||||
me.callSuper([config]);
|
||||
me.matrix = new Ext.draw.Matrix();
|
||||
me.inverseMatrix = me.matrix.inverse(me.inverseMatrix);
|
||||
me.resetTransform();
|
||||
},
|
||||
|
||||
/**
|
||||
* Round the number to align to the pixels on device.
|
||||
* @param {Number} num The number to align.
|
||||
* @return {Number} The resultant alignment.
|
||||
*/
|
||||
roundPixel: function (num) {
|
||||
return Math.round(this.devicePixelRatio * num) / this.devicePixelRatio;
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark the surface to render after another surface is updated.
|
||||
* @param {Ext.draw.Surface} surface The surface to wait for.
|
||||
*/
|
||||
waitFor: function (surface) {
|
||||
var me = this,
|
||||
predecessors = me.predecessors;
|
||||
if (!Ext.Array.contains(predecessors, surface)) {
|
||||
predecessors.push(surface);
|
||||
surface.successors.push(me);
|
||||
if (surface._dirty) {
|
||||
me.dirtyPredecessor++;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setDirty: function (dirty) {
|
||||
if (this._dirty !== dirty) {
|
||||
var successors = this.successors, successor,
|
||||
i, ln = successors.length;
|
||||
for (i = 0; i < ln; i++) {
|
||||
successor = successors[i];
|
||||
if (dirty) {
|
||||
successor.dirtyPredecessor++;
|
||||
successor.setDirty(true);
|
||||
} else {
|
||||
successor.dirtyPredecessor--;
|
||||
if (successor.dirtyPredecessor === 0 && successor.pendingRenderFrame) {
|
||||
successor.renderFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
this._dirty = dirty;
|
||||
}
|
||||
},
|
||||
|
||||
applyElement: function (newElement, oldElement) {
|
||||
if (oldElement) {
|
||||
oldElement.set(newElement);
|
||||
} else {
|
||||
oldElement = Ext.Element.create(newElement);
|
||||
}
|
||||
this.setDirty(true);
|
||||
return oldElement;
|
||||
},
|
||||
|
||||
applyBackground: function (background, oldBackground) {
|
||||
this.setDirty(true);
|
||||
if (Ext.isString(background)) {
|
||||
background = { fillStyle: background };
|
||||
}
|
||||
return Ext.factory(background, Ext.draw.sprite.Rect, oldBackground);
|
||||
},
|
||||
|
||||
applyRegion: function (region, oldRegion) {
|
||||
if (oldRegion && region[0] === oldRegion[0] && region[1] === oldRegion[1] && region[2] === oldRegion[2] && region[3] === oldRegion[3]) {
|
||||
return;
|
||||
}
|
||||
if (Ext.isArray(region)) {
|
||||
return [region[0], region[1], region[2], region[3]];
|
||||
} else if (Ext.isObject(region)) {
|
||||
return [
|
||||
region.x || region.left,
|
||||
region.y || region.top,
|
||||
region.width || (region.right - region.left),
|
||||
region.height || (region.bottom - region.top)
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
updateRegion: function (region) {
|
||||
var me = this,
|
||||
l = region[0],
|
||||
t = region[1],
|
||||
r = l + region[2],
|
||||
b = t + region[3],
|
||||
background = this.getBackground(),
|
||||
element = me.element;
|
||||
|
||||
element.setBox({
|
||||
top: Math.floor(t),
|
||||
left: Math.floor(l),
|
||||
width: Math.ceil(r - Math.floor(l)),
|
||||
height: Math.ceil(b - Math.floor(t))
|
||||
});
|
||||
|
||||
if (background) {
|
||||
background.setAttributes({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: Math.ceil(r - Math.floor(l)),
|
||||
height: Math.ceil(b - Math.floor(t))
|
||||
});
|
||||
}
|
||||
me.setDirty(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the matrix of the surface.
|
||||
*/
|
||||
resetTransform: function () {
|
||||
this.matrix.set(1, 0, 0, 1, 0, 0);
|
||||
this.inverseMatrix.set(1, 0, 0, 1, 0, 0);
|
||||
this.setDirty(true);
|
||||
},
|
||||
|
||||
updateComponent: function (component, oldComponent) {
|
||||
if (component) {
|
||||
component.element.dom.appendChild(this.element.dom);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the sprite by id or index.
|
||||
* It will first try to find a sprite with the given id, otherwise will try to use the id as an index.
|
||||
* @param {String|Number} id
|
||||
* @returns {Ext.draw.sprite.Sprite}
|
||||
*/
|
||||
get: function (id) {
|
||||
return this.map[id] || this.items[id];
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a Sprite to the surface.
|
||||
* You can put any number of object as parameter.
|
||||
* See {@link Ext.draw.sprite.Sprite} for the configuration object to be passed into this method.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* drawComponent.surface.add({
|
||||
* type: 'circle',
|
||||
* fill: '#ffc',
|
||||
* radius: 100,
|
||||
* x: 100,
|
||||
* y: 100
|
||||
* });
|
||||
*
|
||||
*/
|
||||
add: function () {
|
||||
var me = this,
|
||||
args = Array.prototype.slice.call(arguments),
|
||||
argIsArray = Ext.isArray(args[0]),
|
||||
results = [],
|
||||
sprite, sprites, items, i, ln;
|
||||
|
||||
items = Ext.Array.clean(argIsArray ? args[0] : args);
|
||||
if (!items.length) {
|
||||
return results;
|
||||
}
|
||||
sprites = me.prepareItems(items);
|
||||
|
||||
for (i = 0, ln = sprites.length; i < ln; i++) {
|
||||
sprite = sprites[i];
|
||||
me.map[sprite.getId()] = sprite;
|
||||
results.push(sprite);
|
||||
sprite.setParent(this);
|
||||
me.onAdd(sprite);
|
||||
}
|
||||
|
||||
items = me.getItems();
|
||||
if (items) {
|
||||
items.push.apply(items, results);
|
||||
}
|
||||
|
||||
me.dirtyZIndex = true;
|
||||
me.setDirty(true);
|
||||
|
||||
if (!argIsArray && results.length === 1) {
|
||||
return results[0];
|
||||
} else {
|
||||
return results;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Invoked when a sprite is added to the surface.
|
||||
* @param {Ext.draw.sprite.Sprite} sprite The sprite to be added.
|
||||
*/
|
||||
onAdd: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Remove a given sprite from the surface, optionally destroying the sprite in the process.
|
||||
* You can also call the sprite own `remove` method.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* drawComponent.surface.remove(sprite);
|
||||
* // or...
|
||||
* sprite.remove();
|
||||
*
|
||||
* @param {Ext.draw.sprite.Sprite} sprite
|
||||
* @param {Boolean} [destroySprite=false]
|
||||
*/
|
||||
remove: function (sprite, destroySprite) {
|
||||
if (sprite) {
|
||||
delete this.map[sprite.getId()];
|
||||
if (destroySprite) {
|
||||
sprite.destroy();
|
||||
} else {
|
||||
sprite.setParent(null);
|
||||
Ext.Array.remove(this.getItems(), sprite);
|
||||
}
|
||||
this.dirtyZIndex = true;
|
||||
this.setDirty(true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all sprites from the surface, optionally destroying the sprites in the process.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* drawComponent.getSurface('main').removeAll();
|
||||
*
|
||||
* @param {Boolean} [destroySprites=false]
|
||||
*/
|
||||
removeAll: function (destroySprites) {
|
||||
var items = this.getItems(),
|
||||
i = items.length;
|
||||
if (destroySprites) {
|
||||
while (i > 0) {
|
||||
items[--i].destroy();
|
||||
}
|
||||
} else {
|
||||
while (i > 0) {
|
||||
items[--i].setParent(null);
|
||||
}
|
||||
}
|
||||
items.length = 0;
|
||||
this.map = {};
|
||||
this.dirtyZIndex = true;
|
||||
},
|
||||
|
||||
// @private
|
||||
applyItems: function (items) {
|
||||
if (this.getItems()) {
|
||||
this.removeAll(true);
|
||||
}
|
||||
return Ext.Array.from(this.add(items));
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Initialize and apply defaults to surface items.
|
||||
*/
|
||||
prepareItems: function (items) {
|
||||
items = [].concat(items);
|
||||
// Make sure defaults are applied and item is initialized
|
||||
|
||||
var me = this,
|
||||
item, i, ln, j,
|
||||
removeSprite = function (sprite) {
|
||||
this.remove(sprite, false);
|
||||
};
|
||||
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
item = items[i];
|
||||
if (!(item instanceof Ext.draw.sprite.Sprite)) {
|
||||
// Temporary, just take in configs...
|
||||
item = items[i] = me.createItem(item);
|
||||
}
|
||||
item.on('beforedestroy', removeSprite, me);
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private Creates an item and appends it to the surface. Called
|
||||
* as an internal method when calling `add`.
|
||||
*/
|
||||
createItem: function (config) {
|
||||
var sprite = Ext.create(config.xclass || 'sprite.' + config.type, config);
|
||||
return sprite;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the minimal bounding box that contains all the sprites bounding boxes in the given list of sprites.
|
||||
* @param {Ext.draw.sprite.Sprite[]|Ext.draw.sprite.Sprite} sprites
|
||||
* @param {Boolean} [isWithoutTransform=false]
|
||||
* @returns {{x: Number, y: Number, width: number, height: number}}
|
||||
*/
|
||||
getBBox: function (sprites, isWithoutTransform) {
|
||||
var sprites = Ext.Array.from(sprites),
|
||||
left = Infinity,
|
||||
right = -Infinity,
|
||||
top = Infinity,
|
||||
bottom = -Infinity,
|
||||
sprite, bbox, i, ln;
|
||||
|
||||
for (i = 0, ln = sprites.length; i < ln; i++) {
|
||||
sprite = sprites[i];
|
||||
bbox = sprite.getBBox(isWithoutTransform);
|
||||
if (left > bbox.x) {
|
||||
left = bbox.x;
|
||||
}
|
||||
if (right < bbox.x + bbox.width) {
|
||||
right = bbox.x + bbox.width;
|
||||
}
|
||||
if (top > bbox.y) {
|
||||
top = bbox.y;
|
||||
}
|
||||
if (bottom < bbox.y + bbox.height) {
|
||||
bottom = bbox.y + bbox.height;
|
||||
}
|
||||
}
|
||||
return {
|
||||
x: left,
|
||||
y: top,
|
||||
width: right - left,
|
||||
height: bottom - top
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Empty the surface content (without touching the sprites.)
|
||||
*/
|
||||
clear: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Order the items by their z-index if any of that has been changed since last sort.
|
||||
*/
|
||||
orderByZIndex: function () {
|
||||
var me = this,
|
||||
items = me.getItems(),
|
||||
dirtyZIndex = false,
|
||||
i, ln;
|
||||
|
||||
if (me.getDirty()) {
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
if (items[i].attr.dirtyZIndex) {
|
||||
dirtyZIndex = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dirtyZIndex) {
|
||||
// sort by zIndex
|
||||
Ext.draw.Surface.stableSort(items);
|
||||
this.setDirty(true);
|
||||
}
|
||||
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
items[i].attr.dirtyZIndex = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Force the element to redraw.
|
||||
*/
|
||||
repaint: function () {
|
||||
var me = this;
|
||||
me.repaint = Ext.emptyFn;
|
||||
setTimeout(function () {
|
||||
delete me.repaint;
|
||||
me.element.repaint();
|
||||
}, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers the re-rendering of the canvas.
|
||||
*/
|
||||
renderFrame: function () {
|
||||
if (!this.element) {
|
||||
return;
|
||||
}
|
||||
if (this.dirtyPredecessor > 0) {
|
||||
this.pendingRenderFrame = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var me = this,
|
||||
region = this.getRegion(),
|
||||
background = me.getBackground(),
|
||||
items = me.getItems(),
|
||||
item, i, ln;
|
||||
|
||||
// Cannot render before the surface is placed.
|
||||
if (!region) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This will also check the dirty flags of the sprites.
|
||||
me.orderByZIndex();
|
||||
if (me.getDirty()) {
|
||||
me.clear();
|
||||
me.clearTransform();
|
||||
|
||||
if (background) {
|
||||
me.renderSprite(background);
|
||||
}
|
||||
|
||||
for (i = 0, ln = items.length; i < ln; i++) {
|
||||
item = items[i];
|
||||
if (false === me.renderSprite(item)) {
|
||||
return;
|
||||
}
|
||||
item.attr.textPositionCount = me.textPosition;
|
||||
}
|
||||
|
||||
me.setDirty(false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Renders a single sprite into the surface.
|
||||
* Do not call it from outside `renderFrame` method.
|
||||
*
|
||||
* @param {Ext.draw.sprite.Sprite} sprite The Sprite to be rendered.
|
||||
* @return {Boolean} returns `false` to stop the rendering to continue.
|
||||
*/
|
||||
renderSprite: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Clears the current transformation state on the surface.
|
||||
*/
|
||||
clearTransform: Ext.emptyFn,
|
||||
|
||||
/**
|
||||
* Returns 'true' if the surface is dirty.
|
||||
* @return {Boolean} 'true' if the surface is dirty
|
||||
*/
|
||||
getDirty: function () {
|
||||
return this._dirty;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys the surface. This is done by removing all components from it and
|
||||
* also removing its reference to a DOM element.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* drawComponent.surface.destroy();
|
||||
*/
|
||||
destroy: function () {
|
||||
var me = this;
|
||||
me.removeAll();
|
||||
me.setBackground(null);
|
||||
me.predecessors = null;
|
||||
me.successors = null;
|
||||
me.callSuper();
|
||||
}
|
||||
});
|
||||
|
||||
|
143
vendor/touch/src/draw/TextMeasurer.js
vendored
Normal file
143
vendor/touch/src/draw/TextMeasurer.js
vendored
Normal file
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* Utility class to provide a way to *approximately* measure the dimension of texts without a drawing context.
|
||||
*/
|
||||
Ext.define("Ext.draw.TextMeasurer", {
|
||||
singleton: true,
|
||||
|
||||
uses: ['Ext.draw.engine.Canvas'],
|
||||
|
||||
measureDiv: null,
|
||||
measureCache: {},
|
||||
|
||||
/**
|
||||
* @private Measure the size of a text with specific font by using DOM to measure it.
|
||||
* Could be very expensive therefore should be used lazily.
|
||||
* @param {String} text
|
||||
* @param {String} font
|
||||
* @return {Object} An object with `width` and `height` properties.
|
||||
* @return {Number} return.width
|
||||
* @return {Number} return.height
|
||||
*/
|
||||
actualMeasureText: function (text, font) {
|
||||
var me = Ext.draw.TextMeasurer,
|
||||
measureDiv = me.measureDiv,
|
||||
FARAWAY = 100000,
|
||||
size;
|
||||
|
||||
if (!measureDiv) {
|
||||
var parent = Ext.Element.create({
|
||||
style: {
|
||||
"overflow": "hidden",
|
||||
"position": "relative",
|
||||
"float": "left", // DO NOT REMOVE THE QUOTE OR IT WILL BREAK COMPRESSOR
|
||||
"width": 0,
|
||||
"height": 0
|
||||
}
|
||||
});
|
||||
me.measureDiv = measureDiv = Ext.Element.create({});
|
||||
measureDiv.setStyle({
|
||||
"position": 'absolute',
|
||||
"x": FARAWAY,
|
||||
"y": FARAWAY,
|
||||
"z-index": -FARAWAY,
|
||||
"white-space": "nowrap",
|
||||
"display": 'block',
|
||||
"padding": 0,
|
||||
"margin": 0
|
||||
});
|
||||
Ext.getBody().appendChild(parent);
|
||||
parent.appendChild(measureDiv);
|
||||
}
|
||||
if (font) {
|
||||
measureDiv.setStyle({
|
||||
font: font,
|
||||
lineHeight: 'normal'
|
||||
});
|
||||
}
|
||||
measureDiv.setText('(' + text + ')');
|
||||
size = measureDiv.getSize();
|
||||
measureDiv.setText('()');
|
||||
size.width -= measureDiv.getSize().width;
|
||||
return size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Measure a single-line text with specific font.
|
||||
* This will split the text to characters and add up their size.
|
||||
* That may *not* be the exact size of the text as it is displayed.
|
||||
* @param {String} text
|
||||
* @param {String} font
|
||||
* @return {Object} An object with `width` and `height` properties.
|
||||
* @return {Number} return.width
|
||||
* @return {Number} return.height
|
||||
*/
|
||||
measureTextSingleLine: function (text, font) {
|
||||
text = text.toString();
|
||||
var cache = this.measureCache,
|
||||
chars = text.split(''),
|
||||
width = 0,
|
||||
height = 0,
|
||||
cachedItem, charactor, i, ln, size;
|
||||
|
||||
if (!cache[font]) {
|
||||
cache[font] = {};
|
||||
}
|
||||
cache = cache[font];
|
||||
|
||||
if (cache[text]) {
|
||||
return cache[text];
|
||||
}
|
||||
|
||||
for (i = 0, ln = chars.length; i < ln; i++) {
|
||||
charactor = chars[i];
|
||||
if (!(cachedItem = cache[charactor])) {
|
||||
size = this.actualMeasureText(charactor, font);
|
||||
cachedItem = cache[charactor] = size;
|
||||
}
|
||||
width += cachedItem.width;
|
||||
height = Math.max(height, cachedItem.height);
|
||||
}
|
||||
return cache[text] = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Measure a text with specific font.
|
||||
* This will split the text to lines and add up their size.
|
||||
* That may *not* be the exact size of the text as it is displayed.
|
||||
* @param {String} text
|
||||
* @param {String} font
|
||||
* @return {Object} An object with `width`, `height` and `sizes` properties.
|
||||
* @return {Number} return.width
|
||||
* @return {Number} return.height
|
||||
* @return {Array} return.sizes Results of individual line measurements, in case of multiline text.
|
||||
*/
|
||||
measureText: function (text, font) {
|
||||
var lines = text.split('\n'),
|
||||
ln = lines.length,
|
||||
height = 0,
|
||||
width = 0,
|
||||
line, i,
|
||||
sizes;
|
||||
|
||||
if (ln === 1) {
|
||||
return this.measureTextSingleLine(text, font);
|
||||
}
|
||||
|
||||
sizes = [];
|
||||
for (i = 0; i < ln; i++) {
|
||||
line = this.measureTextSingleLine(lines[i], font);
|
||||
sizes.push(line);
|
||||
height += line.height;
|
||||
width = Math.max(width, line.width);
|
||||
}
|
||||
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
sizes: sizes
|
||||
};
|
||||
}
|
||||
});
|
139
vendor/touch/src/draw/TimingFunctions.js
vendored
Normal file
139
vendor/touch/src/draw/TimingFunctions.js
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
(function () {
|
||||
var pow = Math.pow,
|
||||
sin = Math.sin,
|
||||
cos = Math.cos,
|
||||
sqrt = Math.sqrt,
|
||||
pi = Math.PI,
|
||||
easings, addEasing, poly, createPoly, easing, i, l;
|
||||
|
||||
//create polynomial easing equations
|
||||
poly = ['quad', 'cubic', 'quart', 'quint'];
|
||||
|
||||
//create other easing equations
|
||||
easings = {
|
||||
pow: function (p, x) {
|
||||
return pow(p, x[0] || 6);
|
||||
},
|
||||
|
||||
expo: function (p) {
|
||||
return pow(2, 8 * (p - 1));
|
||||
},
|
||||
|
||||
circ: function (p) {
|
||||
return 1 - sqrt(1 - p * p);
|
||||
},
|
||||
|
||||
sine: function (p) {
|
||||
return 1 - sin((1 - p) * pi / 2);
|
||||
},
|
||||
|
||||
back: function (p, n) {
|
||||
n = n || 1.616;
|
||||
return p * p * ((n + 1) * p - n);
|
||||
},
|
||||
|
||||
bounce: function (p) {
|
||||
var value;
|
||||
for (var a = 0, b = 1; 1; a += b, b /= 2) {
|
||||
if (p >= (7 - 4 * a) / 11) {
|
||||
value = b * b - pow((11 - 6 * a - 11 * p) / 4, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
elastic: function (p, x) {
|
||||
return pow(2, 10 * --p) * cos(20 * p * pi * (x || 1) / 3);
|
||||
}
|
||||
};
|
||||
|
||||
//Add easeIn, easeOut, easeInOut options to all easing equations.
|
||||
addEasing = function (easing, params) {
|
||||
params = params && params.length ? params : [ params ];
|
||||
return Ext.apply(easing, {
|
||||
|
||||
easeIn: function (pos) {
|
||||
return easing(pos, params);
|
||||
},
|
||||
|
||||
easeOut: function (pos) {
|
||||
return 1 - easing(1 - pos, params);
|
||||
},
|
||||
|
||||
easeInOut: function (pos) {
|
||||
return (pos <= 0.5) ? easing(2 * pos, params) / 2
|
||||
: (2 - easing(2 * (1 - pos), params)) / 2;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//Append the polynomial equations with easing support to the EasingPrototype.
|
||||
createPoly = function (times) {
|
||||
return function (p) {
|
||||
return pow(p, times);
|
||||
};
|
||||
};
|
||||
|
||||
for (i = 0, l = poly.length; i < l; ++i) {
|
||||
easings[poly[i]] = createPoly(i + 2);
|
||||
}
|
||||
|
||||
//Add linear interpolator
|
||||
easings.linear = function (x) {
|
||||
return x;
|
||||
};
|
||||
|
||||
for (easing in easings) {
|
||||
if (easings.hasOwnProperty(easing)) {
|
||||
addEasing(easings[easing]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
* Contains transition equations such as `Quad`, `Cubic`, `Quart`, `Quint`,
|
||||
* `Expo`, `Circ`, `Pow`, `Sine`, `Back`, `Bounce`, `Elastic`, etc.
|
||||
*
|
||||
* Contains transition equations such as `Quad`, `Cubic`, `Quart`, `Quint`, `Expo`, `Circ`, `Pow`, `Sine`, `Back`, `Bounce`, `Elastic`, etc.
|
||||
* Each transition also contains methods for applying this function as ease in, ease out or ease in and out accelerations.
|
||||
*
|
||||
* var fx = Ext.create('Ext.draw.fx.Sprite', {
|
||||
* sprite: sprite,
|
||||
* duration: 1000,
|
||||
* easing: 'backOut'
|
||||
* });
|
||||
*/
|
||||
Ext.define('Ext.draw.TimingFunctions', {
|
||||
singleton: true,
|
||||
easingMap: {
|
||||
linear: easings.linear,
|
||||
easeIn: easings.quad.easeIn,
|
||||
easeOut: easings.quad.easeOut,
|
||||
easeInOut: easings.quad.easeInOut,
|
||||
backIn: easings.back,
|
||||
backOut: function (x, n) {
|
||||
return 1 - easings.back(1 - x, n);
|
||||
},
|
||||
backInOut: function (x, n) {
|
||||
if (x < 0.5) {
|
||||
return easings.back(x * 2, n) * 0.5;
|
||||
} else {
|
||||
return 1 - easings.back((1 - x) * 2, n) * 0.5;
|
||||
}
|
||||
},
|
||||
elasticIn: function (x, n) {
|
||||
return 1 - easings.elastic(1 - x, n);
|
||||
},
|
||||
elasticOut: easings.elastic,
|
||||
bounceIn: easings.bounce,
|
||||
bounceOut: function (x) {
|
||||
return 1 - easings.bounce(1 - x);
|
||||
}
|
||||
}
|
||||
}, function () {
|
||||
Ext.apply(this, easings);
|
||||
});
|
||||
|
||||
})();
|
||||
|
822
vendor/touch/src/draw/engine/Canvas.js
vendored
Normal file
822
vendor/touch/src/draw/engine/Canvas.js
vendored
Normal file
|
@ -0,0 +1,822 @@
|
|||
/**
|
||||
* Provides specific methods to draw with 2D Canvas element.
|
||||
*/
|
||||
Ext.define('Ext.draw.engine.Canvas', {
|
||||
extend: 'Ext.draw.Surface',
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Boolean} highPrecision
|
||||
* True to have the canvas use JavaScript Number instead of single precision floating point for transforms.
|
||||
*
|
||||
* For example, when using huge data to plot line series, the transform matrix of the canvas will have
|
||||
* a big element. Due to the implementation of SVGMatrix, the elements are restored by 32-bits floats, which
|
||||
* will work incorrectly. To compensate that, we enable the canvas context to perform all the transform by
|
||||
* JavaScript. Do not use it if you are not encountering 32-bits floating point errors problem since it will
|
||||
* have a performance penalty.
|
||||
*/
|
||||
highPrecision: false
|
||||
},
|
||||
requires: ['Ext.draw.Animator'],
|
||||
|
||||
statics: {
|
||||
contextOverrides: {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
setGradientBBox: function (bbox) {
|
||||
this.bbox = bbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the subpaths of the current default path or the given path with the current fill style.
|
||||
* @ignore
|
||||
*/
|
||||
fill: function () {
|
||||
var fillStyle = this.fillStyle,
|
||||
fillGradient = this.fillGradient,
|
||||
fillOpacity = this.fillOpacity,
|
||||
rgba = 'rgba(0, 0, 0, 0)',
|
||||
rgba0 = 'rgba(0, 0, 0, 0.0)',
|
||||
bbox = this.bbox,
|
||||
alpha = this.globalAlpha;
|
||||
|
||||
if (fillStyle !== rgba && fillStyle !== rgba0 && fillOpacity !== 0) {
|
||||
if (fillGradient && bbox) {
|
||||
this.fillStyle = fillGradient.generateGradient(this, bbox);
|
||||
}
|
||||
|
||||
if (fillOpacity !== 1) {
|
||||
this.globalAlpha = alpha * fillOpacity;
|
||||
}
|
||||
this.$fill();
|
||||
if (fillOpacity !== 1) {
|
||||
this.globalAlpha = alpha;
|
||||
}
|
||||
|
||||
if (fillGradient && bbox) {
|
||||
this.fillStyle = fillStyle;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Strokes the subpaths of the current default path or the given path with the current stroke style.
|
||||
* @ignore
|
||||
*/
|
||||
stroke: function () {
|
||||
var strokeStyle = this.strokeStyle,
|
||||
strokeGradient = this.strokeGradient,
|
||||
strokeOpacity = this.strokeOpacity,
|
||||
rgba = 'rgba(0, 0, 0, 0)',
|
||||
rgba0 = 'rgba(0, 0, 0, 0.0)',
|
||||
bbox = this.bbox,
|
||||
alpha = this.globalAlpha;
|
||||
|
||||
if (strokeStyle !== rgba && strokeStyle !== rgba0 && strokeOpacity !== 0) {
|
||||
if (strokeGradient && bbox) {
|
||||
this.strokeStyle = strokeGradient.generateGradient(this, bbox);
|
||||
}
|
||||
|
||||
if (strokeOpacity !== 1) {
|
||||
this.globalAlpha = alpha * strokeOpacity;
|
||||
}
|
||||
this.$stroke();
|
||||
if (strokeOpacity !== 1) {
|
||||
this.globalAlpha = alpha;
|
||||
}
|
||||
|
||||
if (strokeGradient && bbox) {
|
||||
this.strokeStyle = strokeStyle;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
fillStroke: function (attr, transformFillStroke) {
|
||||
var ctx = this,
|
||||
fillStyle = this.fillStyle,
|
||||
fillOpacity = this.fillOpacity,
|
||||
strokeStyle = this.strokeStyle,
|
||||
strokeOpacity = this.strokeOpacity,
|
||||
shadowColor = ctx.shadowColor,
|
||||
shadowBlur = ctx.shadowBlur,
|
||||
rgba = 'rgba(0, 0, 0, 0)',
|
||||
rgba0 = 'rgba(0, 0, 0, 0.0)';
|
||||
|
||||
if (transformFillStroke === undefined) {
|
||||
transformFillStroke = attr.transformFillStroke;
|
||||
}
|
||||
|
||||
if (!transformFillStroke) {
|
||||
attr.inverseMatrix.toContext(ctx);
|
||||
}
|
||||
if (fillStyle !== rgba && fillStyle !== rgba0 && fillOpacity !== 0) {
|
||||
ctx.fill();
|
||||
ctx.shadowColor = 'rgba(0,0,0,0)';
|
||||
ctx.shadowBlur = 0;
|
||||
}
|
||||
if (strokeStyle !== rgba && strokeStyle !== rgba0 && strokeOpacity !== 0) {
|
||||
ctx.stroke();
|
||||
}
|
||||
ctx.shadowColor = shadowColor;
|
||||
ctx.shadowBlur = shadowBlur;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds points to the subpath such that the arc described by the circumference of the
|
||||
* ellipse described by the arguments, starting at the given start angle and ending at
|
||||
* the given end angle, going in the given direction (defaulting to clockwise), is added
|
||||
* to the path, connected to the previous point by a straight line.
|
||||
* @ignore
|
||||
*/
|
||||
ellipse: function (cx, cy, rx, ry, rotation, start, end, anticlockwise) {
|
||||
var cos = Math.cos(rotation),
|
||||
sin = Math.sin(rotation);
|
||||
this.transform(cos * rx, sin * rx, -sin * ry, cos * ry, cx, cy);
|
||||
this.arc(0, 0, 1, start, end, anticlockwise);
|
||||
this.transform(
|
||||
cos / rx, -sin / ry,
|
||||
sin / rx, cos / ry,
|
||||
-(cos * cx + sin * cy) / rx, (sin * cx - cos * cy) / ry);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uses the given path commands to begin a new path on the canvas.
|
||||
* @ignore
|
||||
*/
|
||||
appendPath: function (path) {
|
||||
var me = this,
|
||||
i = 0, j = 0,
|
||||
types = path.types,
|
||||
coords = path.coords,
|
||||
ln = path.types.length;
|
||||
me.beginPath();
|
||||
for (; i < ln; i++) {
|
||||
switch (types[i]) {
|
||||
case "M":
|
||||
me.moveTo(coords[j], coords[j + 1]);
|
||||
j += 2;
|
||||
break;
|
||||
case "L":
|
||||
me.lineTo(coords[j], coords[j + 1]);
|
||||
j += 2;
|
||||
break;
|
||||
case "C":
|
||||
me.bezierCurveTo(
|
||||
coords[j], coords[j + 1],
|
||||
coords[j + 2], coords[j + 3],
|
||||
coords[j + 4], coords[j + 5]
|
||||
);
|
||||
j += 6;
|
||||
break;
|
||||
case "Z":
|
||||
me.closePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
splitThreshold: 1800,
|
||||
|
||||
getElementConfig: function () {
|
||||
return {
|
||||
reference: 'element',
|
||||
style: {
|
||||
position: 'absolute'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
reference: 'innerElement',
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'relative'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Creates the canvas element.
|
||||
*/
|
||||
createCanvas: function () {
|
||||
var canvas = Ext.Element.create({
|
||||
tag: 'canvas',
|
||||
cls: 'x-surface'
|
||||
}),
|
||||
overrides = Ext.draw.engine.Canvas.contextOverrides,
|
||||
ctx = canvas.dom.getContext('2d'),
|
||||
backingStoreRatio = ctx.webkitBackingStorePixelRatio ||
|
||||
ctx.mozBackingStorePixelRatio ||
|
||||
ctx.msBackingStorePixelRatio ||
|
||||
ctx.oBackingStorePixelRatio ||
|
||||
ctx.backingStorePixelRatio || 1,
|
||||
name;
|
||||
|
||||
// Windows Phone does not currently support backingStoreRatio
|
||||
this.devicePixelRatio /= (Ext.os.is.WindowsPhone) ? window.innerWidth / window.screen.width : backingStoreRatio;
|
||||
|
||||
if (ctx.ellipse) {
|
||||
delete overrides.ellipse;
|
||||
}
|
||||
|
||||
for (name in overrides) {
|
||||
ctx['$' + name] = ctx[name];
|
||||
}
|
||||
Ext.apply(ctx, overrides);
|
||||
|
||||
if (this.getHighPrecision()) {
|
||||
this.enablePrecisionCompensation(ctx);
|
||||
} else {
|
||||
this.disablePrecisionCompensation(ctx);
|
||||
}
|
||||
|
||||
this.innerElement.appendChild(canvas);
|
||||
this.canvases.push(canvas);
|
||||
this.contexts.push(ctx);
|
||||
},
|
||||
|
||||
/**
|
||||
* Initialize the canvas element.
|
||||
*/
|
||||
initElement: function () {
|
||||
this.callSuper();
|
||||
this.canvases = [];
|
||||
this.contexts = [];
|
||||
this.createCanvas();
|
||||
this.activeCanvases = 0;
|
||||
},
|
||||
|
||||
updateHighPrecision: function (pc) {
|
||||
var contexts = this.contexts,
|
||||
ln = contexts.length,
|
||||
i, context;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
context = contexts[i];
|
||||
if (pc) {
|
||||
this.enablePrecisionCompensation(context);
|
||||
} else {
|
||||
this.disablePrecisionCompensation(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
precisionMethods: {
|
||||
rect: false,
|
||||
fillRect: false,
|
||||
strokeRect: false,
|
||||
clearRect: false,
|
||||
moveTo: false,
|
||||
lineTo: false,
|
||||
arc: false,
|
||||
arcTo: false,
|
||||
save: false,
|
||||
restore: false,
|
||||
updatePrecisionCompensate: false,
|
||||
setTransform: false,
|
||||
transform: false,
|
||||
scale: false,
|
||||
translate: false,
|
||||
rotate: false,
|
||||
quadraticCurveTo: false,
|
||||
bezierCurveTo: false,
|
||||
createLinearGradient: false,
|
||||
createRadialGradient: false,
|
||||
fillText: false,
|
||||
strokeText: false,
|
||||
drawImage: false
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Clears canvas of compensation for canvas' use of single precision floating point.
|
||||
* @param {CanvasRenderingContext2D} ctx The canvas context.
|
||||
*/
|
||||
disablePrecisionCompensation: function (ctx) {
|
||||
var precisionMethods = this.precisionMethods,
|
||||
name;
|
||||
|
||||
for (name in precisionMethods) {
|
||||
delete ctx[name];
|
||||
}
|
||||
|
||||
this.setDirty(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Compensate for canvas' use of single precision floating point.
|
||||
* @param {CanvasRenderingContext2D} ctx The canvas context.
|
||||
*/
|
||||
enablePrecisionCompensation: function (ctx) {
|
||||
var surface = this,
|
||||
xx = 1, yy = 1,
|
||||
dx = 0, dy = 0,
|
||||
matrix = new Ext.draw.Matrix(),
|
||||
transStack = [],
|
||||
comp = {},
|
||||
originalCtx = ctx.constructor.prototype;
|
||||
|
||||
/**
|
||||
* @class CanvasRenderingContext2D
|
||||
* @ignore
|
||||
*/
|
||||
var override = {
|
||||
/**
|
||||
* Adds a new closed subpath to the path, representing the given rectangle.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
rect: function (x, y, w, h) {
|
||||
return originalCtx.rect.call(this, x * xx + dx, y * yy + dy, w * xx, h * yy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Paints the given rectangle onto the canvas, using the current fill style.
|
||||
* @ignore
|
||||
*/
|
||||
fillRect: function (x, y, w, h) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.fillRect.call(this, x * xx + dx, y * yy + dy, w * xx, h * yy);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Paints the box that outlines the given rectangle onto the canvas, using the current stroke style.
|
||||
* @ignore
|
||||
*/
|
||||
strokeRect: function (x, y, w, h) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.strokeRect.call(this, x * xx + dx, y * yy + dy, w * xx, h * yy);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears all pixels on the canvas in the given rectangle to transparent black.
|
||||
* @ignore
|
||||
*/
|
||||
clearRect: function (x, y, w, h) {
|
||||
return originalCtx.clearRect.call(this, x * xx + dx, y * yy + dy, w * xx, h * yy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new subpath with the given point.
|
||||
* @ignore
|
||||
*/
|
||||
moveTo: function (x, y) {
|
||||
return originalCtx.moveTo.call(this, x * xx + dx, y * yy + dy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given point to the current subpath, connected to the previous one by a straight line.
|
||||
* @ignore
|
||||
*/
|
||||
lineTo: function (x, y) {
|
||||
return originalCtx.lineTo.call(this, x * xx + dx, y * yy + dy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds points to the subpath such that the arc described by the circumference of the
|
||||
* circle described by the arguments, starting at the given start angle and ending at
|
||||
* the given end angle, going in the given direction (defaulting to clockwise), is added
|
||||
* to the path, connected to the previous point by a straight line.
|
||||
* @ignore
|
||||
*/
|
||||
arc: function (x, y, radius, startAngle, endAngle, anticlockwise) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.arc.call(this, x * xx + dx, y * xx + dy, radius * xx, startAngle, endAngle, anticlockwise);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an arc with the given control points and radius to the current subpath,
|
||||
* connected to the previous point by a straight line. If two radii are provided, the
|
||||
* first controls the width of the arc's ellipse, and the second controls the height. If
|
||||
* only one is provided, or if they are the same, the arc is from a circle.
|
||||
*
|
||||
* In the case of an ellipse, the rotation argument controls the clockwise inclination
|
||||
* of the ellipse relative to the x-axis.
|
||||
* @ignore
|
||||
*/
|
||||
arcTo: function (x1, y1, x2, y2, radius) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.arcTo.call(this, x1 * xx + dx, y1 * yy + dy, x2 * xx + dx, y2 * yy + dy, radius * xx);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Pushes the context state to the state stack.
|
||||
* @ignore
|
||||
*/
|
||||
save: function () {
|
||||
transStack.push(matrix);
|
||||
matrix = matrix.clone();
|
||||
return originalCtx.save.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Pops the state stack and restores the state.
|
||||
* @ignore
|
||||
*/
|
||||
restore: function () {
|
||||
matrix = transStack.pop();
|
||||
originalCtx.restore.call(this);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
updatePrecisionCompensate: function () {
|
||||
matrix.precisionCompensate(surface.devicePixelRatio, comp);
|
||||
xx = comp.xx;
|
||||
yy = comp.yy;
|
||||
dx = comp.dx;
|
||||
dy = comp.dy;
|
||||
return originalCtx.setTransform.call(this, surface.devicePixelRatio, comp.b, comp.c, comp.d, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
updatePrecisionCompensateRect: function () {
|
||||
matrix.precisionCompensateRect(surface.devicePixelRatio, comp);
|
||||
xx = comp.xx;
|
||||
yy = comp.yy;
|
||||
dx = comp.dx;
|
||||
dy = comp.dy;
|
||||
return originalCtx.setTransform.call(this, surface.devicePixelRatio, comp.b, comp.c, comp.d, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the transformation matrix to the matrix given by the arguments as described below.
|
||||
* @ignore
|
||||
*/
|
||||
setTransform: function (x2x, x2y, y2x, y2y, newDx, newDy) {
|
||||
matrix.set(x2x, x2y, y2x, y2y, newDx, newDy);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the transformation matrix to apply the matrix given by the arguments as described below.
|
||||
* @ignore
|
||||
*/
|
||||
transform: function (x2x, x2y, y2x, y2y, newDx, newDy) {
|
||||
matrix.append(x2x, x2y, y2x, y2y, newDx, newDy);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales the transformation matrix.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
scale: function (sx, sy) {
|
||||
return this.transform(sx, 0, 0, sy, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Translates the transformation matrix.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
translate: function (dx, dy) {
|
||||
return this.transform(1, 0, 0, 1, dx, dy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotates the transformation matrix.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
rotate: function (radians) {
|
||||
var cos = Math.cos(radians),
|
||||
sin = Math.sin(radians);
|
||||
return this.transform(cos, sin, -sin, cos, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given point to the current subpath, connected to the previous one by a
|
||||
* quadratic Bézier curve with the given control point.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
quadraticCurveTo: function (cx, cy, x, y) {
|
||||
return originalCtx.quadraticCurveTo.call(this,
|
||||
cx * xx + dx,
|
||||
cy * yy + dy,
|
||||
x * xx + dx,
|
||||
y * yy + dy
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given point to the current subpath, connected to the previous one by a cubic
|
||||
* Bézier curve with the given control points.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
bezierCurveTo: function (c1x, c1y, c2x, c2y, x, y) {
|
||||
return originalCtx.bezierCurveTo.call(this,
|
||||
c1x * xx + dx,
|
||||
c1y * yy + dy,
|
||||
c2x * xx + dx,
|
||||
c2y * yy + dy,
|
||||
x * xx + dx,
|
||||
y * yy + dy
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an object that represents a linear gradient that paints along the line given
|
||||
* by the coordinates represented by the arguments.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
createLinearGradient: function (x0, y0, x1, y1) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
var grad = originalCtx.createLinearGradient.call(this,
|
||||
x0 * xx + dx,
|
||||
y0 * yy + dy,
|
||||
x1 * xx + dx,
|
||||
y1 * yy + dy
|
||||
);
|
||||
this.updatePrecisionCompensate();
|
||||
return grad;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a CanvasGradient object that represents a radial gradient that paints along
|
||||
* the cone given by the circles represented by the arguments. If either of the radii
|
||||
* are negative, throws an IndexSizeError exception.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
createRadialGradient: function (x0, y0, r0, x1, y1, r1) {
|
||||
this.updatePrecisionCompensateRect();
|
||||
var grad = originalCtx.createLinearGradient.call(this,
|
||||
x0 * xx + dx,
|
||||
y0 * xx + dy,
|
||||
r0 * xx,
|
||||
x1 * xx + dx,
|
||||
y1 * xx + dy,
|
||||
r1 * xx
|
||||
);
|
||||
this.updatePrecisionCompensate();
|
||||
return grad;
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the given text at the given position. If a maximum width is provided, the text
|
||||
* will be scaled to fit that width if necessary.
|
||||
* @ignore
|
||||
*/
|
||||
fillText: function (text, x, y, maxWidth) {
|
||||
originalCtx.setTransform.apply(this, matrix.elements);
|
||||
if (typeof maxWidth === 'undefined') {
|
||||
originalCtx.fillText.call(this, text, x, y);
|
||||
} else {
|
||||
originalCtx.fillText.call(this, text, x, y, maxWidth);
|
||||
}
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Strokes the given text at the given position. If a
|
||||
* maximum width is provided, the text will be scaled to
|
||||
* fit that width if necessary.
|
||||
* @ignore
|
||||
*/
|
||||
strokeText: function (text, x, y, maxWidth) {
|
||||
originalCtx.setTransform.apply(this, matrix.elements);
|
||||
if (typeof maxWidth === 'undefined') {
|
||||
originalCtx.strokeText.call(this, text, x, y);
|
||||
} else {
|
||||
originalCtx.strokeText.call(this, text, x, y, maxWidth);
|
||||
}
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the subpaths of the current default path or the given path with the current fill style.
|
||||
* @ignore
|
||||
*/
|
||||
fill: function () {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.fill.call(this);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Strokes the subpaths of the current default path or the given path with the current stroke style.
|
||||
* @ignore
|
||||
*/
|
||||
stroke: function () {
|
||||
this.updatePrecisionCompensateRect();
|
||||
originalCtx.stroke.call(this);
|
||||
this.updatePrecisionCompensate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the given image onto the canvas. If the first argument isn't an img, canvas,
|
||||
* or video element, throws a TypeMismatchError exception. If the image has no image
|
||||
* data, throws an InvalidStateError exception. If the one of the source rectangle
|
||||
* dimensions is zero, throws an IndexSizeError exception. If the image isn't yet fully
|
||||
* decoded, then nothing is drawn.
|
||||
* @return {*}
|
||||
* @ignore
|
||||
*/
|
||||
drawImage: function (img_elem, arg1, arg2, arg3, arg4, dst_x, dst_y, dw, dh) {
|
||||
switch (arguments.length) {
|
||||
case 3:
|
||||
return originalCtx.drawImage.call(this, img_elem, arg1 * xx + dx, arg2 * yy + dy);
|
||||
case 5:
|
||||
return originalCtx.drawImage.call(this, img_elem, arg1 * xx + dx, arg2 * yy + dy, arg3 * xx, arg4 * yy);
|
||||
case 9:
|
||||
return originalCtx.drawImage.call(this, img_elem, arg1, arg2, arg3, arg4, dst_x * xx + dx, dst_y * yy * dy, dw * xx, dh * yy);
|
||||
}
|
||||
}
|
||||
};
|
||||
Ext.apply(ctx, override);
|
||||
this.setDirty(true);
|
||||
},
|
||||
|
||||
// Continue docs for the Canvas class
|
||||
/** @class Ext.draw.engine.Canvas */
|
||||
|
||||
updateRegion: function (region) {
|
||||
this.callSuper([region]);
|
||||
|
||||
var me = this,
|
||||
l = Math.floor(region[0]),
|
||||
t = Math.floor(region[1]),
|
||||
r = Math.ceil(region[0] + region[2]),
|
||||
b = Math.ceil(region[1] + region[3]),
|
||||
devicePixelRatio = me.devicePixelRatio,
|
||||
w = r - l,
|
||||
h = b - t,
|
||||
splitThreshold = Math.round(me.splitThreshold / devicePixelRatio),
|
||||
splits = Math.ceil(w / splitThreshold),
|
||||
activeCanvases = me.activeCanvases,
|
||||
i, offsetX, dom, leftWidth;
|
||||
|
||||
for (i = 0, offsetX = 0; i < splits; i++, offsetX += splitThreshold) {
|
||||
if (i >= me.canvases.length) {
|
||||
me.createCanvas();
|
||||
}
|
||||
dom = me.canvases[i].dom;
|
||||
dom.style.left = offsetX + 'px';
|
||||
if (h * devicePixelRatio !== dom.height) {
|
||||
dom.height = h * devicePixelRatio;
|
||||
dom.style.height = h + 'px';
|
||||
}
|
||||
leftWidth = Math.min(splitThreshold, w - offsetX);
|
||||
if (leftWidth * devicePixelRatio !== dom.width) {
|
||||
dom.width = leftWidth * devicePixelRatio;
|
||||
dom.style.width = leftWidth + 'px';
|
||||
}
|
||||
me.applyDefaults(me.contexts[i]);
|
||||
}
|
||||
|
||||
for (; i < activeCanvases; i++) {
|
||||
dom = me.canvases[i].dom;
|
||||
dom.width = 0;
|
||||
dom.height = 0;
|
||||
}
|
||||
me.activeCanvases = splits;
|
||||
me.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clearTransform: function () {
|
||||
var me = this,
|
||||
activeCanvases = me.activeCanvases,
|
||||
i, ctx;
|
||||
|
||||
for (i = 0; i < activeCanvases; i++) {
|
||||
ctx = me.contexts[i];
|
||||
ctx.translate(-me.splitThreshold * i, 0);
|
||||
ctx.scale(me.devicePixelRatio, me.devicePixelRatio);
|
||||
me.matrix.toContext(ctx);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @inheritdoc
|
||||
*/
|
||||
renderSprite: function (sprite) {
|
||||
var me = this,
|
||||
region = me._region,
|
||||
surfaceMatrix = me.matrix,
|
||||
parent = sprite._parent,
|
||||
matrix = Ext.draw.Matrix.fly([1, 0, 0, 1, 0, 0]),
|
||||
bbox, i, offsetX, ctx, width, left = 0, top, right = region[2], bottom;
|
||||
|
||||
while (parent && (parent !== me)) {
|
||||
matrix.prependMatrix(parent.matrix || parent.attr && parent.attr.matrix);
|
||||
parent = parent.getParent();
|
||||
}
|
||||
matrix.prependMatrix(surfaceMatrix);
|
||||
bbox = sprite.getBBox();
|
||||
if (bbox) {
|
||||
bbox = matrix.transformBBox(bbox);
|
||||
}
|
||||
|
||||
sprite.preRender(me);
|
||||
|
||||
if (sprite.attr.hidden || sprite.attr.globalAlpha === 0) {
|
||||
sprite.setDirty(false);
|
||||
return;
|
||||
}
|
||||
|
||||
top = 0;
|
||||
bottom = top + region[3];
|
||||
|
||||
for (i = 0, offsetX = 0; i < me.activeCanvases; i++, offsetX += me.splitThreshold / me.devicePixelRatio) {
|
||||
ctx = me.contexts[i];
|
||||
width = Math.min(region[2] - offsetX, me.splitThreshold / me.devicePixelRatio);
|
||||
left = offsetX;
|
||||
right = left + width;
|
||||
|
||||
if (bbox) {
|
||||
if (bbox.x > right ||
|
||||
bbox.x + bbox.width < left ||
|
||||
bbox.y > bottom ||
|
||||
bbox.y + bbox.height < top) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
ctx.save();
|
||||
// Set attributes to context.
|
||||
sprite.useAttributes(ctx, region);
|
||||
// Render shape
|
||||
if (false === sprite.render(me, ctx, [left, top, width, bottom - top], region)) {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
sprite.setDirty(false);
|
||||
},
|
||||
|
||||
applyDefaults: function (ctx) {
|
||||
ctx.strokeStyle = 'rgba(0,0,0,0)';
|
||||
ctx.fillStyle = 'rgba(0,0,0,0)';
|
||||
ctx.textAlign = 'start';
|
||||
ctx.textBaseline = 'top';
|
||||
ctx.miterLimit = 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear: function () {
|
||||
var me = this,
|
||||
activeCanvases = this.activeCanvases,
|
||||
i, canvas, ctx;
|
||||
for (i = 0; i < activeCanvases; i++) {
|
||||
canvas = me.canvases[i].dom;
|
||||
ctx = me.contexts[i];
|
||||
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
me.setDirty(true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys the Canvas element and prepares it for Garbage Collection.
|
||||
*/
|
||||
destroy: function () {
|
||||
var me = this,
|
||||
i, ln = me.canvases.length;
|
||||
for (i = 0; i < ln; i++) {
|
||||
me.contexts[i] = null;
|
||||
me.canvases[i].destroy();
|
||||
me.canvases[i] = null;
|
||||
}
|
||||
delete me.contexts;
|
||||
delete me.canvases;
|
||||
me.callSuper(arguments);
|
||||
}
|
||||
}, function () {
|
||||
if (Ext.os.is.Android4 && Ext.browser.is.Chrome) {
|
||||
this.prototype.splitThreshold = 3000;
|
||||
} else if (Ext.os.is.Android) {
|
||||
this.prototype.splitThreshold = 1e10;
|
||||
}
|
||||
});
|
195
vendor/touch/src/draw/engine/Svg.js
vendored
Normal file
195
vendor/touch/src/draw/engine/Svg.js
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* @class Ext.draw.engine.Svg
|
||||
* @extends Ext.draw.Surface
|
||||
*
|
||||
* SVG engine.
|
||||
*/
|
||||
Ext.define('Ext.draw.engine.Svg', {
|
||||
extend: 'Ext.draw.Surface',
|
||||
requires: ['Ext.draw.engine.SvgContext'],
|
||||
|
||||
statics: {
|
||||
BBoxTextCache: {}
|
||||
},
|
||||
|
||||
config: {
|
||||
/**
|
||||
* Nothing needs to be done in high precision mode.
|
||||
*/
|
||||
highPrecision: false
|
||||
},
|
||||
|
||||
getElementConfig: function () {
|
||||
return {
|
||||
reference: 'element',
|
||||
style: {
|
||||
position: 'absolute'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
reference: 'innerElement',
|
||||
style: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'relative'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
tag: 'svg',
|
||||
reference: 'svgElement',
|
||||
namespace: "http://www.w3.org/2000/svg",
|
||||
version: 1.1,
|
||||
cls: 'x-surface'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
var me = this;
|
||||
me.callSuper([config]);
|
||||
me.mainGroup = me.createSvgNode("g");
|
||||
me.defElement = me.createSvgNode("defs");
|
||||
// me.svgElement is assigned in element creation of Ext.Component.
|
||||
me.svgElement.appendChild(me.mainGroup);
|
||||
me.svgElement.appendChild(me.defElement);
|
||||
me.ctx = new Ext.draw.engine.SvgContext(me);
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a DOM element under the SVG namespace of the given type.
|
||||
* @param {String} type The type of the SVG DOM element.
|
||||
* @return {*} The created element.
|
||||
*/
|
||||
createSvgNode: function (type) {
|
||||
var node = document.createElementNS("http://www.w3.org/2000/svg", type);
|
||||
return Ext.get(node);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Returns the SVG DOM element at the given position. If it does not already exist or is a different element tag
|
||||
* it will be created and inserted into the DOM.
|
||||
* @param {Ext.dom.Element} group The parent DOM element.
|
||||
* @param {String} tag The SVG element tag.
|
||||
* @param {Number} position The position of the element in the DOM.
|
||||
* @return {Ext.dom.Element} The SVG element.
|
||||
*/
|
||||
getSvgElement: function (group, tag, position) {
|
||||
var element;
|
||||
if (group.dom.childNodes.length > position) {
|
||||
element = group.dom.childNodes[position];
|
||||
if (element.tagName === tag) {
|
||||
return Ext.get(element);
|
||||
} else {
|
||||
Ext.destroy(element);
|
||||
}
|
||||
}
|
||||
|
||||
element = Ext.get(this.createSvgNode(tag));
|
||||
if (position === 0) {
|
||||
group.insertFirst(element);
|
||||
} else {
|
||||
element.insertAfter(Ext.fly(group.dom.childNodes[position - 1]));
|
||||
}
|
||||
element.cache = {};
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Applies attributes to the given element.
|
||||
* @param {Ext.dom.Element} element The DOM element to be applied.
|
||||
* @param {Object} attributes The attributes to apply to the element.
|
||||
*/
|
||||
setElementAttributes: function (element, attributes) {
|
||||
var dom = element.dom,
|
||||
cache = element.cache,
|
||||
name, value;
|
||||
for (name in attributes) {
|
||||
value = attributes[name];
|
||||
if (cache[name] !== value) {
|
||||
cache[name] = value;
|
||||
dom.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Gets the next reference element under the SVG 'defs' tag.
|
||||
* @param {String} tagName The type of reference element.
|
||||
* @return {Ext.dom.Element} The reference element.
|
||||
*/
|
||||
getNextDef: function (tagName) {
|
||||
return this.getSvgElement(this.defElement, tagName, this.defPosition++);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clearTransform: function () {
|
||||
var me = this;
|
||||
me.mainGroup.set({transform: me.matrix.toSvg()});
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
clear: function () {
|
||||
this.ctx.clear();
|
||||
this.defPosition = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
renderSprite: function (sprite) {
|
||||
var me = this,
|
||||
region = me.getRegion(),
|
||||
ctx = me.ctx;
|
||||
if (sprite.attr.hidden || sprite.attr.opacity === 0) {
|
||||
ctx.save();
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
sprite.element = ctx.save();
|
||||
sprite.preRender(this);
|
||||
sprite.useAttributes(ctx, region);
|
||||
if (false === sprite.render(this, ctx, [0, 0, region[2], region[3]])) {
|
||||
return false;
|
||||
}
|
||||
sprite.setDirty(false);
|
||||
} finally {
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys the Canvas element and prepares it for Garbage Collection.
|
||||
*/
|
||||
destroy: function (path, matrix, band) {
|
||||
var me = this;
|
||||
me.ctx.destroy();
|
||||
me.mainGroup.destroy();
|
||||
delete me.mainGroup;
|
||||
delete me.ctx;
|
||||
me.callSuper(arguments);
|
||||
},
|
||||
|
||||
remove: function (sprite, destroySprite) {
|
||||
if (sprite && sprite.element) {
|
||||
//if sprite has an associated svg element remove it from the surface
|
||||
if (this.ctx) {
|
||||
this.ctx.removeElement(sprite.element);
|
||||
} else {
|
||||
sprite.element.destroy();
|
||||
}
|
||||
sprite.element = null;
|
||||
}
|
||||
this.callSuper(arguments);
|
||||
}
|
||||
});
|
718
vendor/touch/src/draw/engine/SvgContext.js
vendored
Normal file
718
vendor/touch/src/draw/engine/SvgContext.js
vendored
Normal file
|
@ -0,0 +1,718 @@
|
|||
/**
|
||||
* @class Ext.draw.engine.SvgContext
|
||||
*
|
||||
* A class that imitates a canvas context but generates svg elements instead.
|
||||
*/
|
||||
Ext.define('Ext.draw.engine.SvgContext', {
|
||||
/**
|
||||
* @private
|
||||
* Properties to be saved/restored in `save` and `restore` method.
|
||||
*/
|
||||
toSave: ["strokeOpacity", "strokeStyle", "fillOpacity", "fillStyle", "globalAlpha", "lineWidth", "lineCap",
|
||||
"lineJoin", "lineDash", "lineDashOffset", "miterLimit", "shadowOffsetX", "shadowOffsetY", "shadowBlur",
|
||||
"shadowColor", "globalCompositeOperation", "position"],
|
||||
|
||||
"strokeOpacity": 1,
|
||||
"strokeStyle": "none",
|
||||
"fillOpacity": 1,
|
||||
"fillStyle": "none",
|
||||
"lineDash": [],
|
||||
"lineDashOffset": 0,
|
||||
"globalAlpha": 1,
|
||||
"lineWidth": 1,
|
||||
"lineCap": "butt",
|
||||
"lineJoin": "miter",
|
||||
"miterLimit": 10,
|
||||
"shadowOffsetX": 0,
|
||||
"shadowOffsetY": 0,
|
||||
"shadowBlur": 0,
|
||||
"shadowColor": "none",
|
||||
"globalCompositeOperation": "src",
|
||||
|
||||
urlStringRe: /^url\(#([\w\-]+)\)$/,
|
||||
|
||||
constructor: function (SvgSurface) {
|
||||
this.surface = SvgSurface;
|
||||
this.status = [];
|
||||
this.matrix = new Ext.draw.Matrix();
|
||||
this.path = null;
|
||||
this.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the context.
|
||||
*/
|
||||
clear: function () {
|
||||
this.group = this.surface.mainGroup;
|
||||
this.position = 0;
|
||||
this.path = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {String} tag
|
||||
* @return {*}
|
||||
*/
|
||||
getElement: function (tag) {
|
||||
return this.surface.getSvgElement(this.group, tag, this.position++);
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Destroys the DOM element and all associated gradients.
|
||||
*
|
||||
* @param element {HTMLElement|Ext.dom.Element|String} DOM element.
|
||||
*/
|
||||
removeElement: function (element) {
|
||||
var element = Ext.fly(element),
|
||||
fill, stroke, fillMatch, strokeMatch,
|
||||
gradients, gradient, key;
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
if (element.dom.tagName === 'g') {
|
||||
gradients = element.dom.gradients;
|
||||
for (key in gradients) {
|
||||
gradients[key].destroy();
|
||||
}
|
||||
} else {
|
||||
fill = element.getAttribute('fill');
|
||||
stroke = element.getAttribute('stroke');
|
||||
fillMatch = fill && fill.match(this.urlStringRe);
|
||||
strokeMatch = stroke && stroke.match(this.urlStringRe);
|
||||
if (fillMatch && fillMatch[1]) {
|
||||
gradient = Ext.fly(fillMatch[1]);
|
||||
if (gradient) {
|
||||
gradient.destroy();
|
||||
}
|
||||
}
|
||||
if (strokeMatch && strokeMatch[1]) {
|
||||
gradient = Ext.fly(strokeMatch[1]);
|
||||
if (gradient) {
|
||||
gradient.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
element.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* Pushes the context state to the state stack.
|
||||
*/
|
||||
save: function () {
|
||||
var toSave = this.toSave,
|
||||
obj = {},
|
||||
group = this.getElement('g'),
|
||||
key, i;
|
||||
|
||||
for (i = 0; i < toSave.length; i++) {
|
||||
key = toSave[i];
|
||||
if (key in this) {
|
||||
obj[key] = this[key];
|
||||
}
|
||||
}
|
||||
this.position = 0;
|
||||
obj.matrix = this.matrix.clone();
|
||||
this.status.push(obj);
|
||||
this.group = group;
|
||||
return group;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pops the state stack and restores the state.
|
||||
*/
|
||||
restore: function () {
|
||||
var toSave = this.toSave,
|
||||
obj = this.status.pop(),
|
||||
children = this.group.dom.childNodes,
|
||||
key, i;
|
||||
|
||||
// Removing extra DOM elements that were not reused.
|
||||
while (children.length > this.position) {
|
||||
this.removeElement(children[children.length - 1]);
|
||||
}
|
||||
for (i = 0; i < toSave.length; i++) {
|
||||
key = toSave[i];
|
||||
if (key in obj) {
|
||||
this[key] = obj[key];
|
||||
} else {
|
||||
delete this[key];
|
||||
}
|
||||
}
|
||||
|
||||
this.setTransform.apply(this, obj.matrix.elements);
|
||||
this.group = this.group.getParent();
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the transformation matrix to apply the matrix given by the arguments as described below.
|
||||
* @param {Number} xx
|
||||
* @param {Number} yx
|
||||
* @param {Number} xy
|
||||
* @param {Number} yy
|
||||
* @param {Number} dx
|
||||
* @param {Number} dy
|
||||
*/
|
||||
transform: function (xx, yx, xy, yy, dx, dy) {
|
||||
if (this.path) {
|
||||
var inv = Ext.draw.Matrix.fly([xx, yx, xy, yy, dx, dy]).inverse();
|
||||
this.path.transform(inv);
|
||||
}
|
||||
this.matrix.append(xx, yx, xy, yy, dx, dy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the transformation matrix to the matrix given by the arguments as described below.
|
||||
* @param {Number} xx
|
||||
* @param {Number} yx
|
||||
* @param {Number} xy
|
||||
* @param {Number} yy
|
||||
* @param {Number} dx
|
||||
* @param {Number} dy
|
||||
*/
|
||||
setTransform: function (xx, yx, xy, yy, dx, dy) {
|
||||
if (this.path) {
|
||||
this.path.transform(this.matrix);
|
||||
}
|
||||
this.matrix.reset();
|
||||
this.transform(xx, yx, xy, yy, dx, dy);
|
||||
},
|
||||
|
||||
/**
|
||||
* Scales the current context by the specified horizontal (x) and vertical (y) factors.
|
||||
* @param {Number} x The horizontal scaling factor, where 1 equals unity or 100% scale.
|
||||
* @param {Number} y The vertical scaling factor.
|
||||
*/
|
||||
scale: function (x, y) {
|
||||
this.transform(x, 0, 0, y, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rotates the current context coordinates (that is, a transformation matrix).
|
||||
* @param {Number} angle The rotation angle, in radians.
|
||||
*/
|
||||
rotate: function (angle) {
|
||||
var xx = Math.cos(angle),
|
||||
yx = Math.sin(angle),
|
||||
xy = -Math.sin(angle),
|
||||
yy = Math.cos(angle);
|
||||
this.transform(xx, yx, xy, yy, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies values to move the origin point in a canvas.
|
||||
* @param {Number} x The value to add to horizontal (or x) coordinates.
|
||||
* @param {Number} y The value to add to vertical (or y) coordinates.
|
||||
*/
|
||||
translate: function (x, y) {
|
||||
this.transform(1, 0, 0, 1, x, y);
|
||||
},
|
||||
|
||||
setGradientBBox: function (bbox) {
|
||||
this.bbox = bbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the current default path.
|
||||
*/
|
||||
beginPath: function () {
|
||||
this.path = new Ext.draw.Path();
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a new subpath with the given point.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
moveTo: function (x, y) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.moveTo(x, y);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given point to the current subpath, connected to the previous one by a straight line.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
lineTo: function (x, y) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.lineTo(x, y);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new closed subpath to the path, representing the given rectangle.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} width
|
||||
* @param {Number} height
|
||||
*/
|
||||
rect: function (x, y, width, height) {
|
||||
this.moveTo(x, y);
|
||||
this.lineTo(x + width, y);
|
||||
this.lineTo(x + width, y + height);
|
||||
this.lineTo(x, y + height);
|
||||
this.closePath();
|
||||
},
|
||||
|
||||
/**
|
||||
* Paints the box that outlines the given rectangle onto the canvas, using the current stroke style.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} width
|
||||
* @param {Number} height
|
||||
*/
|
||||
strokeRect: function (x, y, width, height) {
|
||||
this.beginPath();
|
||||
this.rect(x, y, width, height);
|
||||
this.stroke();
|
||||
},
|
||||
|
||||
/**
|
||||
* Paints the given rectangle onto the canvas, using the current fill style.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} width
|
||||
* @param {Number} height
|
||||
*/
|
||||
fillRect: function (x, y, width, height) {
|
||||
this.beginPath();
|
||||
this.rect(x, y, width, height);
|
||||
this.fill();
|
||||
},
|
||||
|
||||
/**
|
||||
* Marks the current subpath as closed, and starts a new subpath with a point the same as the start and end of the newly closed subpath.
|
||||
*/
|
||||
closePath: function () {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.closePath();
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Arc command using svg parameters.
|
||||
* @param {Number} r1
|
||||
* @param {Number} r2
|
||||
* @param {Number} rotation
|
||||
* @param {Number} large
|
||||
* @param {Number} swipe
|
||||
* @param {Number} x2
|
||||
* @param {Number} y2
|
||||
*/
|
||||
arcSvg: function (r1, r2, rotation, large, swipe, x2, y2) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.arcSvg(r1, r2, rotation, large, swipe, x2, y2);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds points to the subpath such that the arc described by the circumference of the circle described by the arguments, starting at the given start angle and ending at the given end angle, going in the given direction (defaulting to clockwise), is added to the path, connected to the previous point by a straight line.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} radius
|
||||
* @param {Number} startAngle
|
||||
* @param {Number} endAngle
|
||||
* @param {Number} anticlockwise
|
||||
*/
|
||||
arc: function (x, y, radius, startAngle, endAngle, anticlockwise) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.arc(x, y, radius, startAngle, endAngle, anticlockwise);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds points to the subpath such that the arc described by the circumference of the ellipse described by the arguments, starting at the given start angle and ending at the given end angle, going in the given direction (defaulting to clockwise), is added to the path, connected to the previous point by a straight line.
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
* @param {Number} radiusX
|
||||
* @param {Number} radiusY
|
||||
* @param {Number} rotation
|
||||
* @param {Number} startAngle
|
||||
* @param {Number} endAngle
|
||||
* @param {Number} anticlockwise
|
||||
*/
|
||||
ellipse: function (x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
|
||||
* If two radii are provided, the first controls the width of the arc's ellipse, and the second controls the height. If only one is provided, or if they are the same, the arc is from a circle.
|
||||
* In the case of an ellipse, the rotation argument controls the clockwise inclination of the ellipse relative to the x-axis.
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @param {Number} x2
|
||||
* @param {Number} y2
|
||||
* @param {Number} radiusX
|
||||
* @param {Number} radiusY
|
||||
* @param {Number} rotation
|
||||
*/
|
||||
arcTo: function (x1, y1, x2, y2, radiusX, radiusY, rotation) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.arcTo(x1, y1, x2, y2, radiusX, radiusY, rotation);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the given point to the current subpath, connected to the previous one by a cubic Bézier curve with the given control points.
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @param {Number} x2
|
||||
* @param {Number} y2
|
||||
* @param {Number} x3
|
||||
* @param {Number} y3
|
||||
*/
|
||||
bezierCurveTo: function (x1, y1, x2, y2, x3, y3) {
|
||||
if (!this.path) {
|
||||
this.beginPath();
|
||||
}
|
||||
this.path.bezierCurveTo(x1, y1, x2, y2, x3, y3);
|
||||
this.path.element = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Strokes the given text at the given position. If a maximum width is provided, the text will be scaled to fit that width if necessary.
|
||||
* @param {String} text
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
strokeText: function (text, x, y) {
|
||||
text = String(text);
|
||||
if (this.strokeStyle) {
|
||||
var element = this.getElement('text'),
|
||||
tspan = this.surface.getSvgElement(element, 'tspan', 0);
|
||||
this.surface.setElementAttributes(element, {
|
||||
"x": x,
|
||||
"y": y,
|
||||
"transform": this.matrix.toSvg(),
|
||||
"stroke": this.strokeStyle,
|
||||
"fill": "none",
|
||||
"opacity": this.globalAlpha,
|
||||
"stroke-opacity": this.strokeOpacity,
|
||||
"style": "font: " + this.font
|
||||
});
|
||||
if (this.lineDash.length) {
|
||||
this.surface.setElementAttributes(element, {
|
||||
"stroke-dasharray": this.lineDash.join(','),
|
||||
"stroke-dashoffset": this.lineDashOffset
|
||||
});
|
||||
}
|
||||
if (tspan.dom.firstChild) {
|
||||
tspan.dom.removeChild(tspan.dom.firstChild);
|
||||
}
|
||||
this.surface.setElementAttributes(tspan, {
|
||||
"alignment-baseline": "middle",
|
||||
"baseline-shift": "-50%"
|
||||
});
|
||||
tspan.appendChild(document.createTextNode(Ext.String.htmlDecode(text)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the given text at the given position. If a maximum width is provided, the text will be scaled to fit that width if necessary.
|
||||
* @param {String} text
|
||||
* @param {Number} x
|
||||
* @param {Number} y
|
||||
*/
|
||||
fillText: function (text, x, y) {
|
||||
text = String(text);
|
||||
if (this.fillStyle) {
|
||||
var element = this.getElement('text'),
|
||||
tspan = this.surface.getSvgElement(element, 'tspan', 0);
|
||||
this.surface.setElementAttributes(element, {
|
||||
"x": x,
|
||||
"y": y,
|
||||
"transform": this.matrix.toSvg(),
|
||||
"fill": this.fillStyle,
|
||||
"opacity": this.globalAlpha,
|
||||
"fill-opacity": this.fillOpacity,
|
||||
"style": "font: " + this.font
|
||||
});
|
||||
if (tspan.dom.firstChild) {
|
||||
tspan.dom.removeChild(tspan.dom.firstChild);
|
||||
}
|
||||
this.surface.setElementAttributes(tspan, {
|
||||
"alignment-baseline": "middle",
|
||||
"baseline-shift": "-50%"
|
||||
});
|
||||
tspan.appendChild(document.createTextNode(Ext.String.htmlDecode(text)));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the given image onto the canvas.
|
||||
* If the first argument isn't an img, canvas, or video element, throws a TypeMismatchError exception. If the image has no image data, throws an InvalidStateError exception. If the one of the source rectangle dimensions is zero, throws an IndexSizeError exception. If the image isn't yet fully decoded, then nothing is drawn.
|
||||
* @param {HTMLElement} image
|
||||
* @param {Number} sx
|
||||
* @param {Number} sy
|
||||
* @param {Number} sw
|
||||
* @param {Number} sh
|
||||
* @param {Number} dx
|
||||
* @param {Number} dy
|
||||
* @param {Number} dw
|
||||
* @param {Number} dh
|
||||
*/
|
||||
drawImage: function (image, sx, sy, sw, sh, dx, dy, dw, dh) {
|
||||
var me = this,
|
||||
element = me.getElement('image'),
|
||||
x = sx, y = sy,
|
||||
width = typeof sw === 'undefined' ? image.width : sw,
|
||||
height = typeof sh === 'undefined' ? image.height : sh,
|
||||
viewBox = null;
|
||||
if (typeof dh !== 'undefined') {
|
||||
viewBox = sx + " " + sy + " " + sw + " " + sh;
|
||||
x = dx;
|
||||
y = dy;
|
||||
width = dw;
|
||||
height = dh;
|
||||
}
|
||||
element.dom.setAttributeNS("http:/" + "/www.w3.org/1999/xlink", "href", image.src);
|
||||
me.surface.setElementAttributes(element, {
|
||||
viewBox: viewBox,
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height,
|
||||
opacity: me.globalAlpha,
|
||||
transform: me.matrix.toSvg()
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fills the subpaths of the current default path or the given path with the current fill style.
|
||||
*/
|
||||
fill: function () {
|
||||
if (!this.path) {
|
||||
return;
|
||||
}
|
||||
if (this.fillStyle) {
|
||||
var path,
|
||||
fillGradient = this.fillGradient,
|
||||
bbox = this.bbox,
|
||||
element = this.path.element;
|
||||
if (!element) {
|
||||
path = this.path.toString();
|
||||
element = this.path.element = this.getElement('path');
|
||||
this.surface.setElementAttributes(element, {
|
||||
"d": path,
|
||||
"transform": this.matrix.toSvg()
|
||||
});
|
||||
}
|
||||
this.surface.setElementAttributes(element, {
|
||||
"fill": fillGradient && bbox ? fillGradient.generateGradient(this, bbox) : this.fillStyle,
|
||||
"fill-opacity": this.fillOpacity * this.globalAlpha
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Strokes the subpaths of the current default path or the given path with the current stroke style.
|
||||
*/
|
||||
stroke: function () {
|
||||
if (!this.path) {
|
||||
return;
|
||||
}
|
||||
if (this.strokeStyle) {
|
||||
var path,
|
||||
strokeGradient = this.strokeGradient,
|
||||
bbox = this.bbox,
|
||||
element = this.path.element;
|
||||
if (!element || !this.path.svgString) {
|
||||
path = this.path.toString();
|
||||
element = this.path.element = this.getElement('path');
|
||||
this.surface.setElementAttributes(element, {
|
||||
"fill": "none",
|
||||
"d": path,
|
||||
"transform": this.matrix.toSvg()
|
||||
});
|
||||
}
|
||||
this.surface.setElementAttributes(element, {
|
||||
"stroke": strokeGradient && bbox ? strokeGradient.generateGradient(this, bbox) : this.strokeStyle,
|
||||
"stroke-linecap": this.lineCap,
|
||||
"stroke-linejoin": this.lineJoin,
|
||||
"stroke-width": this.lineWidth,
|
||||
"stroke-opacity": this.strokeOpacity * this.globalAlpha
|
||||
});
|
||||
if (this.lineDash.length) {
|
||||
this.surface.setElementAttributes(element, {
|
||||
"stroke-dasharray": this.lineDash.join(','),
|
||||
"stroke-dashoffset": this.lineDashOffset
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*
|
||||
* Note: After the method guarantees the transform matrix will be inverted.
|
||||
* @param {Object} attr The attribute object
|
||||
* @param {Boolean} [transformFillStroke] Indicate whether to transform fill and stroke. If this is not
|
||||
* given, then uses `attr.transformFillStroke` instead.
|
||||
*/
|
||||
fillStroke: function (attr, transformFillStroke) {
|
||||
var ctx = this,
|
||||
fillStyle = ctx.fillStyle,
|
||||
strokeStyle = ctx.strokeStyle,
|
||||
fillOpacity = ctx.fillOpacity,
|
||||
strokeOpacity = ctx.strokeOpacity;
|
||||
|
||||
if (transformFillStroke === undefined) {
|
||||
transformFillStroke = attr.transformFillStroke;
|
||||
}
|
||||
|
||||
if (!transformFillStroke) {
|
||||
attr.inverseMatrix.toContext(ctx);
|
||||
}
|
||||
|
||||
if (fillStyle && fillOpacity !== 0) {
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
if (strokeStyle && strokeOpacity !== 0) {
|
||||
ctx.stroke();
|
||||
}
|
||||
},
|
||||
|
||||
appendPath: function (path) {
|
||||
this.path = path.clone();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an object that represents a linear gradient that paints along the line given by the coordinates represented by the arguments.
|
||||
* @param {Number} x0
|
||||
* @param {Number} y0
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @return {Ext.draw.engine.SvgContext.Gradient}
|
||||
*/
|
||||
createLinearGradient: function (x0, y0, x1, y1) {
|
||||
var me = this,
|
||||
element = me.surface.getNextDef('linearGradient'),
|
||||
gradients = me.group.dom.gradients || (me.group.dom.gradients = {}),
|
||||
gradient;
|
||||
me.surface.setElementAttributes(element, {
|
||||
"x1": x0,
|
||||
"y1": y0,
|
||||
"x2": x1,
|
||||
"y2": y1,
|
||||
"gradientUnits": "userSpaceOnUse"
|
||||
});
|
||||
gradient = new Ext.draw.engine.SvgContext.Gradient(me, me.surface, element);
|
||||
gradients[element.dom.id] = gradient;
|
||||
return gradient;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the circles represented by the arguments.
|
||||
* If either of the radii are negative, throws an IndexSizeError exception.
|
||||
* @param {Number} x0
|
||||
* @param {Number} y0
|
||||
* @param {Number} r0
|
||||
* @param {Number} x1
|
||||
* @param {Number} y1
|
||||
* @param {Number} r1
|
||||
* @return {Ext.draw.engine.SvgContext.Gradient}
|
||||
*/
|
||||
createRadialGradient: function (x0, y0, r0, x1, y1, r1) {
|
||||
var me = this,
|
||||
element = me.surface.getNextDef('radialGradient'),
|
||||
gradients = me.group.dom.gradients || (me.group.dom.gradients = {}),
|
||||
gradient;
|
||||
me.surface.setElementAttributes(element, {
|
||||
"fx": x0,
|
||||
"fy": y0,
|
||||
"cx": x1,
|
||||
"cy": y1,
|
||||
"r": r1,
|
||||
"gradientUnits": "userSpaceOnUse"
|
||||
});
|
||||
gradient = new Ext.draw.engine.SvgContext.Gradient(me, me.surface, element, r0 / r1);
|
||||
gradients[element.dom.id] = gradient;
|
||||
return gradient;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @class Ext.draw.engine.SvgContext.Gradient
|
||||
*/
|
||||
Ext.define("Ext.draw.engine.SvgContext.Gradient", {
|
||||
|
||||
statics: {
|
||||
map: {}
|
||||
},
|
||||
|
||||
constructor: function (ctx, surface, element, compression) {
|
||||
var map = this.statics().map,
|
||||
oldInstance;
|
||||
|
||||
// Because of the way Ext.draw.engine.Svg.getNextDef works,
|
||||
// there is no guarantee that an existing DOM element from the 'defs' section won't be used
|
||||
// for the 'element' param.
|
||||
oldInstance = map[element.dom.id];
|
||||
if (oldInstance) {
|
||||
oldInstance.element = null;
|
||||
}
|
||||
map[element.dom.id] = this;
|
||||
|
||||
this.ctx = ctx;
|
||||
this.surface = surface;
|
||||
this.element = element;
|
||||
this.position = 0;
|
||||
this.compression = compression || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a color stop with the given color to the gradient at the given offset. 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
|
||||
* @param {Number} offset
|
||||
* @param {String} color
|
||||
*/
|
||||
addColorStop: function (offset, color) {
|
||||
var stop = this.surface.getSvgElement(this.element, 'stop', this.position++),
|
||||
compression = this.compression;
|
||||
this.surface.setElementAttributes(stop, {
|
||||
"offset": (((1 - compression) * offset + compression) * 100).toFixed(2) + '%',
|
||||
"stop-color": color,
|
||||
"stop-opacity": Ext.draw.Color.fly(color).a.toFixed(15)
|
||||
});
|
||||
},
|
||||
|
||||
toString: function () {
|
||||
var children = this.element.dom.childNodes;
|
||||
// Removing surplus stops in case existing gradient element with more stops was reused.
|
||||
while (children.length > this.position) {
|
||||
Ext.fly(children[children.length - 1]).destroy();
|
||||
}
|
||||
return 'url(#' + this.element.getId() + ')';
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
var map = this.statics().map,
|
||||
element = this.element;
|
||||
if (element) {
|
||||
delete map[element.dom.id];
|
||||
element.destroy();
|
||||
}
|
||||
this.callSuper();
|
||||
}
|
||||
});
|
150
vendor/touch/src/draw/engine/SvgExporter.js
vendored
Normal file
150
vendor/touch/src/draw/engine/SvgExporter.js
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
/**
|
||||
* Exports an SVG document to an image. To do this,
|
||||
* the SVG string must be sent to a remote server and processed.
|
||||
*
|
||||
* # Sending the data
|
||||
*
|
||||
* A post request is made to the URL. The following fields are sent:
|
||||
*
|
||||
* + width: The width of the image
|
||||
* + height: The height of the image
|
||||
* + type: The image type to save as, see {@link #supportedTypes}
|
||||
* + svg: The svg string for the surface
|
||||
*
|
||||
* # The response
|
||||
*
|
||||
* It is expected that the user will be prompted with an image download.
|
||||
* As such, the following options should be set on the server:
|
||||
*
|
||||
* + Content-Disposition: 'attachment, filename="chart.png"'
|
||||
* + Content-Type: 'image/png'
|
||||
*
|
||||
* **Important**: By default, chart data is sent to a server operated
|
||||
* by Sencha to do data processing. You may change this default by
|
||||
* setting the {@link #defaultUrl} of this class.
|
||||
* In addition, please note that this service only creates PNG images.
|
||||
*/
|
||||
// TODO: can't use the canvas element to convert SVG to bitmap on the client, see:
|
||||
// TODO: http://stackoverflow.com/questions/18586808/canvas-todatauri-on-chrome-security-issue
|
||||
Ext.define('Ext.draw.engine.SvgExporter', {
|
||||
singleton: true,
|
||||
|
||||
/**
|
||||
* @property {String} [defaultUrl="http://svg.sencha.io"]
|
||||
* The default URL to submit the form request.
|
||||
*/
|
||||
defaultUrl: 'http://svg.sencha.io',
|
||||
|
||||
/**
|
||||
* @property {Array} [supportedTypes=["image/png", "image/jpeg"]]
|
||||
* A list of export types supported by the server
|
||||
*/
|
||||
supportedTypes: ['image/png', 'image/jpeg'],
|
||||
|
||||
/**
|
||||
* @property {String} [widthParam="width"]
|
||||
* The name of the width parameter to be sent to the server.
|
||||
* The Sencha IO server expects it to be the default value.
|
||||
*/
|
||||
widthParam: 'width',
|
||||
|
||||
/**
|
||||
* @property {String} [heightParam="height"]
|
||||
* The name of the height parameter to be sent to the server.
|
||||
* The Sencha IO server expects it to be the default value.
|
||||
*/
|
||||
heightParam: 'height',
|
||||
|
||||
/**
|
||||
* @property {String} [typeParam="type"]
|
||||
* The name of the type parameter to be sent to the server.
|
||||
* The Sencha IO server expects it to be the default value.
|
||||
*/
|
||||
typeParam: 'type',
|
||||
|
||||
/**
|
||||
* @property {String} [svgParam="svg"]
|
||||
* The name of the svg parameter to be sent to the server.
|
||||
* The Sencha IO server expects it to be the default value.
|
||||
*/
|
||||
svgParam: 'svg',
|
||||
|
||||
formCls: Ext.baseCSSPrefix + 'hide-display',
|
||||
|
||||
/**
|
||||
* Exports the surface to an image
|
||||
* @param {String} svg The SVG document.
|
||||
* @param {Object} [config] The following config options are supported:
|
||||
*
|
||||
* @param {Number} config.width A width to send to the server to for
|
||||
* configuring the image width (required)
|
||||
*
|
||||
* @param {Number} config.height A height to send to the server for
|
||||
* configuring the image height (required)
|
||||
*
|
||||
* @param {String} config.url The url to post the data to. Defaults to
|
||||
* the {@link #defaultUrl} configuration on the class.
|
||||
*
|
||||
* @param {String} config.type The type of image to export. See the
|
||||
* {@link #supportedTypes}
|
||||
*
|
||||
* @param {String} config.widthParam The name of the width parameter to send
|
||||
* to the server. Defaults to {@link #widthParam}
|
||||
*
|
||||
* @param {String} config.heightParam The name of the height parameter to send
|
||||
* to the server. Defaults to {@link #heightParam}
|
||||
*
|
||||
* @param {String} config.typeParam The name of the type parameter to send
|
||||
* to the server. Defaults to {@link #typeParam}
|
||||
*
|
||||
* @param {String} config.svgParam The name of the svg parameter to send
|
||||
* to the server. Defaults to {@link #svgParam}
|
||||
*
|
||||
* @return {Boolean} True if the surface was successfully sent to the server.
|
||||
*/
|
||||
generate: function(svg, config) {
|
||||
config = config || {};
|
||||
var me = this,
|
||||
type = config.type,
|
||||
form;
|
||||
|
||||
if (Ext.Array.indexOf(me.supportedTypes, type) === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
form = Ext.getBody().createChild({
|
||||
tag: 'form',
|
||||
method: 'POST',
|
||||
action: config.url || me.defaultUrl,
|
||||
cls: me.formCls,
|
||||
children: [{
|
||||
tag: 'input',
|
||||
type: 'hidden',
|
||||
name: config.widthParam || me.widthParam,
|
||||
value: config.width
|
||||
}, {
|
||||
tag: 'input',
|
||||
type: 'hidden',
|
||||
name: config.heightParam || me.heightParam,
|
||||
value: config.height
|
||||
}, {
|
||||
tag: 'input',
|
||||
type: 'hidden',
|
||||
name: config.typeParam || me.typeParam,
|
||||
value: type
|
||||
}, {
|
||||
tag: 'input',
|
||||
type: 'hidden',
|
||||
name: config.svgParam || me.svgParam
|
||||
}]
|
||||
});
|
||||
|
||||
// Assign the data on the value so it doesn't get messed up in the html insertion
|
||||
form.last(null, true).value = svg;
|
||||
|
||||
form.dom.submit();
|
||||
form.remove();
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
55
vendor/touch/src/draw/gradient/Gradient.js
vendored
Normal file
55
vendor/touch/src/draw/gradient/Gradient.js
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
* @class Ext.draw.gradient.Gradient
|
||||
*
|
||||
* Creates a gradient.
|
||||
*/
|
||||
Ext.define('Ext.draw.gradient.Gradient', {
|
||||
|
||||
isGradient: true,
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Array/Object} Defines the stops of the gradient.
|
||||
*/
|
||||
stops: []
|
||||
},
|
||||
|
||||
applyStops: function (newStops) {
|
||||
var stops = [],
|
||||
ln = newStops.length,
|
||||
i, stop, color;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
stop = newStops[i];
|
||||
color = Ext.draw.Color.fly(stop.color || 'none');
|
||||
stops.push({
|
||||
offset: Math.min(1, Math.max(0, 'offset' in stop ? stop.offset : stop.position || 0)),
|
||||
color: color.toString()
|
||||
});
|
||||
}
|
||||
stops.sort(function (a, b) {
|
||||
return a.offset - b.offset;
|
||||
});
|
||||
return stops;
|
||||
},
|
||||
|
||||
onClassExtended: function (subClass, member) {
|
||||
if (!member.alias && member.type) {
|
||||
member.alias = 'gradient.' + member.type;
|
||||
}
|
||||
},
|
||||
|
||||
constructor: function (config) {
|
||||
this.initConfig(config);
|
||||
},
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* Generates the gradient for the given context.
|
||||
* @param {Ext.draw.engine.SvgContext} ctx The context.
|
||||
* @param {Object} bbox
|
||||
* @return {Object}
|
||||
*/
|
||||
generateGradient: Ext.emptyFn
|
||||
|
||||
});
|
27
vendor/touch/src/draw/gradient/GradientDefinition.js
vendored
Normal file
27
vendor/touch/src/draw/gradient/GradientDefinition.js
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
Ext.define('Ext.draw.gradient.GradientDefinition', {
|
||||
singleton: true,
|
||||
|
||||
urlStringRe: /^url\(#([\w\-]+)\)$/,
|
||||
gradients: {},
|
||||
|
||||
add: function (gradients) {
|
||||
var store = this.gradients,
|
||||
i, n, gradient;
|
||||
for (i = 0, n = gradients.length; i < n; i++) {
|
||||
gradient = gradients[i];
|
||||
if (Ext.isString(gradient.id)) {
|
||||
store[gradient.id] = gradient;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: function (str) {
|
||||
var store = this.gradients,
|
||||
match = str.match(this.urlStringRe),
|
||||
gradient;
|
||||
if (match && match[1] && (gradient = store[match[1]])) {
|
||||
return gradient || str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
});
|
75
vendor/touch/src/draw/gradient/Linear.js
vendored
Normal file
75
vendor/touch/src/draw/gradient/Linear.js
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* Linear gradient.
|
||||
*
|
||||
* @example preview miniphone
|
||||
* var component = new Ext.draw.Component({
|
||||
* items: [{
|
||||
* type: 'circle',
|
||||
* cx: 50,
|
||||
* cy: 50,
|
||||
* r: 100,
|
||||
* fillStyle: {
|
||||
* type: 'linear',
|
||||
* degrees: 0,
|
||||
* stops: [
|
||||
* {
|
||||
* offset: 0,
|
||||
* color: 'white'
|
||||
* },
|
||||
* {
|
||||
* offset: 1,
|
||||
* color: 'blue'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(component);
|
||||
*/
|
||||
|
||||
Ext.define("Ext.draw.gradient.Linear", {
|
||||
extend: 'Ext.draw.gradient.Gradient',
|
||||
type: 'linear',
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Number} The degree of rotation of the gradient.
|
||||
*/
|
||||
degrees: 0
|
||||
},
|
||||
|
||||
setAngle: function (angle) {
|
||||
this.setDegrees(angle);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
generateGradient: function (ctx, bbox) {
|
||||
var angle = Ext.draw.Draw.rad(this.getDegrees()),
|
||||
cos = Math.cos(angle),
|
||||
sin = Math.sin(angle),
|
||||
w = bbox.width,
|
||||
h = bbox.height,
|
||||
cx = bbox.x + w * 0.5,
|
||||
cy = bbox.y + h * 0.5,
|
||||
stops = this.getStops(),
|
||||
ln = stops.length,
|
||||
gradient,
|
||||
l, i;
|
||||
|
||||
if (!isNaN(cx) && !isNaN(cy) && h > 0 && w > 0) {
|
||||
l = (Math.sqrt(h * h + w * w) * Math.abs(Math.cos(angle - Math.atan(h / w)))) / 2;
|
||||
gradient = ctx.createLinearGradient(
|
||||
cx + cos * l, cy + sin * l,
|
||||
cx - cos * l, cy - sin * l
|
||||
);
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
gradient.addColorStop(stops[i].offset, stops[i].color);
|
||||
}
|
||||
return gradient;
|
||||
}
|
||||
return 'none';
|
||||
}
|
||||
});
|
144
vendor/touch/src/draw/gradient/Radial.js
vendored
Normal file
144
vendor/touch/src/draw/gradient/Radial.js
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* Radial gradient.
|
||||
*
|
||||
* @example preview miniphone
|
||||
* var component = new Ext.draw.Component({
|
||||
* items: [{
|
||||
* type: 'circle',
|
||||
* cx: 50,
|
||||
* cy: 50,
|
||||
* r: 100,
|
||||
* fillStyle: {
|
||||
* type: 'radial',
|
||||
* start: {
|
||||
* x: 0,
|
||||
* y: 0,
|
||||
* r: 0
|
||||
* },
|
||||
* end: {
|
||||
* x: 0,
|
||||
* y: 0,
|
||||
* r: 1
|
||||
* },
|
||||
* stops: [
|
||||
* {
|
||||
* offset: 0,
|
||||
* color: 'white'
|
||||
* },
|
||||
* {
|
||||
* offset: 1,
|
||||
* color: 'blue'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }]
|
||||
* });
|
||||
* Ext.Viewport.setLayout('fit');
|
||||
* Ext.Viewport.add(component);
|
||||
*/
|
||||
Ext.define("Ext.draw.gradient.Radial", {
|
||||
extend: 'Ext.draw.gradient.Gradient',
|
||||
type: 'radial',
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Object} start The starting circle of the gradient.
|
||||
*/
|
||||
start: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: 0
|
||||
},
|
||||
/**
|
||||
* @cfg {Object} end The ending circle of the gradient.
|
||||
*/
|
||||
end: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
r: 1
|
||||
}
|
||||
},
|
||||
|
||||
applyStart: function (newStart, oldStart) {
|
||||
if (!oldStart) {
|
||||
return newStart;
|
||||
}
|
||||
var circle = {
|
||||
x: oldStart.x,
|
||||
y: oldStart.y,
|
||||
r: oldStart.r
|
||||
};
|
||||
|
||||
if ('x' in newStart) {
|
||||
circle.x = newStart.x;
|
||||
} else if ('centerX' in newStart) {
|
||||
circle.x = newStart.centerX;
|
||||
}
|
||||
|
||||
if ('y' in newStart) {
|
||||
circle.y = newStart.y;
|
||||
} else if ('centerY' in newStart) {
|
||||
circle.y = newStart.centerY;
|
||||
}
|
||||
|
||||
if ('r' in newStart) {
|
||||
circle.r = newStart.r;
|
||||
} else if ('radius' in newStart) {
|
||||
circle.r = newStart.radius;
|
||||
}
|
||||
return circle;
|
||||
},
|
||||
|
||||
applyEnd: function (newEnd, oldEnd) {
|
||||
if (!oldEnd) {
|
||||
return newEnd;
|
||||
}
|
||||
var circle = {
|
||||
x: oldEnd.x,
|
||||
y: oldEnd.y,
|
||||
r: oldEnd.r
|
||||
};
|
||||
|
||||
if ('x' in newEnd) {
|
||||
circle.x = newEnd.x;
|
||||
} else if ('centerX' in newEnd) {
|
||||
circle.x = newEnd.centerX;
|
||||
}
|
||||
|
||||
if ('y' in newEnd) {
|
||||
circle.y = newEnd.y;
|
||||
} else if ('centerY' in newEnd) {
|
||||
circle.y = newEnd.centerY;
|
||||
}
|
||||
|
||||
if ('r' in newEnd) {
|
||||
circle.r = newEnd.r;
|
||||
} else if ('radius' in newEnd) {
|
||||
circle.r = newEnd.radius;
|
||||
}
|
||||
return circle;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
generateGradient: function (ctx, bbox) {
|
||||
var start = this.getStart(),
|
||||
end = this.getEnd(),
|
||||
w = bbox.width * 0.5,
|
||||
h = bbox.height * 0.5,
|
||||
x = bbox.x + w,
|
||||
y = bbox.y + h,
|
||||
gradient = ctx.createRadialGradient(
|
||||
x + start.x * w, y + start.y * h, start.r * Math.max(w, h),
|
||||
x + end.x * w, y + end.y * h, end.r * Math.max(w, h)
|
||||
),
|
||||
stops = this.getStops(),
|
||||
ln = stops.length,
|
||||
i;
|
||||
|
||||
for (i = 0; i < ln; i++) {
|
||||
gradient.addColorStop(stops[i].offset, stops[i].color);
|
||||
}
|
||||
return gradient;
|
||||
}
|
||||
});
|
437
vendor/touch/src/draw/modifier/Animation.js
vendored
Normal file
437
vendor/touch/src/draw/modifier/Animation.js
vendored
Normal file
|
@ -0,0 +1,437 @@
|
|||
/**
|
||||
* The Animation modifier.
|
||||
*
|
||||
* Sencha Touch allows users to use transitional animation on sprites. Simply set the duration
|
||||
* and easing in the animation modifier, then all the changes to the sprites will be animated.
|
||||
*
|
||||
* Also, you can use different durations and easing functions on different attributes by using
|
||||
* {@link #customDuration} and {@link #customEasings}.
|
||||
*
|
||||
* By default, an animation modifier will be created during the initialization of a sprite.
|
||||
* You can get the modifier of `sprite` by `sprite.fx`.
|
||||
*
|
||||
*/
|
||||
Ext.define("Ext.draw.modifier.Animation", {
|
||||
mixins: {
|
||||
observable: 'Ext.mixin.Observable'
|
||||
},
|
||||
requires: [
|
||||
'Ext.draw.TimingFunctions',
|
||||
'Ext.draw.Animator'
|
||||
],
|
||||
extend: 'Ext.draw.modifier.Modifier',
|
||||
alias: 'modifier.animation',
|
||||
|
||||
config: {
|
||||
/**
|
||||
* @cfg {Function} easing
|
||||
* Default easing function.
|
||||
*/
|
||||
easing: function (x) {
|
||||
return x;
|
||||
},
|
||||
|
||||
/**
|
||||
* @cfg {Number} duration
|
||||
* Default duration time (ms).
|
||||
*/
|
||||
duration: 0,
|
||||
|
||||
/**
|
||||
* @cfg {Object} customEasings Overrides the default easing function for defined attributes.
|
||||
*/
|
||||
customEasings: {},
|
||||
|
||||
/**
|
||||
* @cfg {Object} customDuration Overrides the default duration for defined attributes.
|
||||
*/
|
||||
customDuration: {}
|
||||
},
|
||||
|
||||
constructor: function () {
|
||||
this.anyAnimation = false;
|
||||
this.anySpecialAnimations = false;
|
||||
this.animating = 0;
|
||||
this.animatingPool = [];
|
||||
this.callSuper(arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
prepareAttributes: function (attr) {
|
||||
if (!attr.hasOwnProperty('timers')) {
|
||||
attr.animating = false;
|
||||
attr.timers = {};
|
||||
attr.animationOriginal = Ext.Object.chain(attr);
|
||||
attr.animationOriginal.upperLevel = attr;
|
||||
}
|
||||
if (this._previous) {
|
||||
this._previous.prepareAttributes(attr.animationOriginal);
|
||||
}
|
||||
},
|
||||
|
||||
updateSprite: function (sprite) {
|
||||
// Apply the config that was configured in the sprite.
|
||||
this.setConfig(sprite.config.fx);
|
||||
},
|
||||
|
||||
updateDuration: function (duration) {
|
||||
this.anyAnimation = duration > 0;
|
||||
},
|
||||
|
||||
applyEasing: function (easing) {
|
||||
if (typeof easing === 'string') {
|
||||
return Ext.draw.TimingFunctions.easingMap[easing];
|
||||
} else {
|
||||
return easing;
|
||||
}
|
||||
},
|
||||
|
||||
applyCustomEasings: function (newCustomEasing, oldCustomEasing) {
|
||||
oldCustomEasing = oldCustomEasing || {};
|
||||
var attr, attrs, easing, i, ln;
|
||||
|
||||
for (attr in newCustomEasing) {
|
||||
easing = newCustomEasing[attr];
|
||||
attrs = attr.split(',');
|
||||
if (typeof easing === 'string') {
|
||||
easing = Ext.draw.TimingFunctions.easingMap[easing];
|
||||
}
|
||||
for (i = 0, ln = attrs.length; i < ln; i++) {
|
||||
oldCustomEasing[attrs[i]] = easing;
|
||||
}
|
||||
}
|
||||
return oldCustomEasing;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set special easings on the given attributes. E.g.:
|
||||
*
|
||||
* circleSprite.fx.setEasingOn('r', 'elasticIn');
|
||||
*
|
||||
* @param {String/Array} attrs The source attribute(s).
|
||||
* @param {String} easing The special easings.
|
||||
*/
|
||||
setEasingOn: function (attrs, easing) {
|
||||
attrs = Ext.Array.from(attrs).slice();
|
||||
var customEasings = {},
|
||||
i = 0,
|
||||
ln = attrs.length;
|
||||
|
||||
for (; i < ln; i++) {
|
||||
customEasings[attrs[i]] = easing;
|
||||
}
|
||||
this.setCustomEasings(customEasings);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove special easings on the given attributes.
|
||||
* @param {String/Array} attrs The source attribute(s).
|
||||
*/
|
||||
clearEasingOn: function (attrs) {
|
||||
attrs = Ext.Array.from(attrs, true);
|
||||
var i = 0, ln = attrs.length;
|
||||
for (; i < ln; i++) {
|
||||
delete this._customEasings[attrs[i]];
|
||||
}
|
||||
},
|
||||
|
||||
applyCustomDuration: function (newCustomDuration, oldCustomDuration) {
|
||||
oldCustomDuration = oldCustomDuration || {};
|
||||
var attr, duration, attrs, i, ln, anySpecialAnimations = this.anySpecialAnimations;
|
||||
|
||||
for (attr in newCustomDuration) {
|
||||
duration = newCustomDuration[attr];
|
||||
attrs = attr.split(',');
|
||||
anySpecialAnimations = true;
|
||||
|
||||
for (i = 0, ln = attrs.length; i < ln; i++) {
|
||||
oldCustomDuration[attrs[i]] = duration;
|
||||
}
|
||||
}
|
||||
this.anySpecialAnimations = anySpecialAnimations;
|
||||
return oldCustomDuration;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set special duration on the given attributes. E.g.:
|
||||
*
|
||||
* rectSprite.fx.setDurationOn('height', 2000);
|
||||
*
|
||||
* @param {String/Array} attrs The source attributes.
|
||||
* @param {Number} duration The special duration.
|
||||
*/
|
||||
setDurationOn: function (attrs, duration) {
|
||||
attrs = Ext.Array.from(attrs).slice();
|
||||
var customDurations = {},
|
||||
i = 0,
|
||||
ln = attrs.length;
|
||||
|
||||
for (; i < ln; i++) {
|
||||
customDurations[attrs[i]] = duration;
|
||||
}
|
||||
this.setCustomDuration(customDurations);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove special easings on the given attributes.
|
||||
* @param {Object} attrs The source attributes.
|
||||
*/
|
||||
clearDurationOn: function (attrs) {
|
||||
attrs = Ext.Array.from(attrs, true);
|
||||
var i = 0, ln = attrs.length;
|
||||
|
||||
for (; i < ln; i++) {
|
||||
delete this._customDuration[attrs[i]];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Initializes Animator for the animation.
|
||||
* @param {Object} attributes The source attributes.
|
||||
* @param {String} animating The animating flag.
|
||||
*/
|
||||
setAnimating: function (attributes, animating) {
|
||||
var me = this,
|
||||
i, j;
|
||||
|
||||
if (attributes.animating !== animating) {
|
||||
attributes.animating = animating;
|
||||
if (animating) {
|
||||
me.animatingPool.push(attributes);
|
||||
if (me.animating === 0) {
|
||||
Ext.draw.Animator.add(me);
|
||||
}
|
||||
me.animating++;
|
||||
} else {
|
||||
for (i = 0, j = 0; i < me.animatingPool.length; i++) {
|
||||
if (me.animatingPool[i] !== attributes) {
|
||||
me.animatingPool[j++] = me.animatingPool[i];
|
||||
}
|
||||
}
|
||||
me.animating = me.animatingPool.length = j;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Set the attr with given easing and duration.
|
||||
* @param {Object} attr The attributes collection.
|
||||
* @param {Object} changes The changes that popped up from lower modifier.
|
||||
* @return {Object} The changes to pop up.
|
||||
*/
|
||||
setAttrs: function (attr, changes) {
|
||||
var timers = attr.timers,
|
||||
parsers = this._sprite.self.def._animationProcessors,
|
||||
defaultEasing = this._easing,
|
||||
defaultDuration = this._duration,
|
||||
customDuration = this._customDuration,
|
||||
customEasings = this._customEasings,
|
||||
anySpecial = this.anySpecialAnimations,
|
||||
any = this.anyAnimation || anySpecial,
|
||||
original = attr.animationOriginal,
|
||||
ignite = false,
|
||||
timer, name, newValue, startValue, parser, easing, duration;
|
||||
|
||||
if (!any) {
|
||||
// If there is no animation enabled
|
||||
// When applying changes to attributes, simply stop current animation
|
||||
// and set the value.
|
||||
for (name in changes) {
|
||||
if (attr[name] === changes[name]) {
|
||||
delete changes[name];
|
||||
} else {
|
||||
attr[name] = changes[name];
|
||||
}
|
||||
delete original[name];
|
||||
delete timers[name];
|
||||
}
|
||||
return changes;
|
||||
} else {
|
||||
// If any animation
|
||||
for (name in changes) {
|
||||
newValue = changes[name];
|
||||
startValue = attr[name];
|
||||
if (newValue !== startValue && any && startValue !== undefined && startValue !== null && (parser = parsers[name])) {
|
||||
// If this property is animating.
|
||||
|
||||
// Figure out the desired duration and easing.
|
||||
easing = defaultEasing;
|
||||
duration = defaultDuration;
|
||||
if (anySpecial) {
|
||||
// Deducing the easing function and duration
|
||||
if (name in customEasings) {
|
||||
easing = customEasings[name];
|
||||
}
|
||||
if (name in customDuration) {
|
||||
duration = customDuration[name];
|
||||
}
|
||||
}
|
||||
|
||||
// If the property is animating
|
||||
if (duration) {
|
||||
if (!timers[name]) {
|
||||
timers[name] = {};
|
||||
}
|
||||
|
||||
timer = timers[name];
|
||||
timer.start = 0;
|
||||
timer.easing = easing;
|
||||
timer.duration = duration;
|
||||
timer.compute = parser.compute;
|
||||
timer.serve = parser.serve || Ext.draw.Draw.reflectFn;
|
||||
|
||||
if (parser.parseInitial) {
|
||||
var initial = parser.parseInitial(startValue, newValue);
|
||||
timer.source = initial[0];
|
||||
timer.target = initial[1];
|
||||
} else if (parser.parse) {
|
||||
timer.source = parser.parse(startValue);
|
||||
timer.target = parser.parse(newValue);
|
||||
} else {
|
||||
timer.source = startValue;
|
||||
timer.target = newValue;
|
||||
}
|
||||
// The animation started. Change to originalVal.
|
||||
timers[name] = timer;
|
||||
original[name] = newValue;
|
||||
delete changes[name];
|
||||
ignite = true;
|
||||
continue;
|
||||
} else {
|
||||
delete original[name];
|
||||
}
|
||||
} else {
|
||||
delete original[name];
|
||||
}
|
||||
|
||||
// If the property is not animating.
|
||||
delete timers[name];
|
||||
}
|
||||
}
|
||||
|
||||
if (ignite && !attr.animating) {
|
||||
this.setAnimating(attr, true);
|
||||
}
|
||||
|
||||
return changes;
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* Update attributes to current value according to current animation time.
|
||||
* This method will not effect the values of lower layers, but may delete a
|
||||
* value from it.
|
||||
* @param {Object} attr The source attributes.
|
||||
* @return {Object} the changes to popup.
|
||||
*/
|
||||
updateAttributes: function (attr) {
|
||||
if (!attr.animating) {
|
||||
return {};
|
||||
}
|
||||
var changes = {},
|
||||
any = false,
|
||||
original = attr.animationOriginal,
|
||||
timers = attr.timers,
|
||||
now = Ext.draw.Animator.animationTime(),
|
||||
name, timer, delta;
|
||||
|
||||
// If updated in the same frame, return.
|
||||
if (attr.lastUpdate === now) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (name in timers) {
|
||||
timer = timers[name];
|
||||
if (!timer.start) {
|
||||
timer.start = now;
|
||||
delta = 0;
|
||||
} else {
|
||||
delta = (now - timer.start) / timer.duration;
|
||||
}
|
||||
if (delta >= 1) {
|
||||
changes[name] = original[name];
|
||||
delete original[name];
|
||||
delete timers[name];
|
||||
} else {
|
||||
changes[name] = timer.serve(timer.compute(timer.source, timer.target, timer.easing(delta), attr[name]));
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
attr.lastUpdate = now;
|
||||
this.setAnimating(attr, any);
|
||||
return changes;
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
pushDown: function (attr, changes) {
|
||||
changes = Ext.draw.modifier.Modifier.prototype.pushDown.call(this, attr.animationOriginal, changes);
|
||||
return this.setAttrs(attr, changes);
|
||||
},
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
popUp: function (attr, changes) {
|
||||
attr = attr.upperLevel;
|
||||
changes = this.setAttrs(attr, changes);
|
||||
if (this._next) {
|
||||
return this._next.popUp(attr, changes);
|
||||
} else {
|
||||
return Ext.apply(attr, changes);
|
||||
}
|
||||
},
|
||||
|
||||
// This is called as an animated object in `Ext.draw.Animator`.
|
||||
step: function () {
|
||||
var me = this,
|
||||
pool = me.animatingPool.slice(),
|
||||
attributes,
|
||||
i, ln;
|
||||
|
||||
for (i = 0, ln = pool.length; i < ln; i++) {
|
||||
attributes = pool[i];
|
||||
var changes = this.updateAttributes(attributes),
|
||||
name;
|
||||
|
||||
// Looking for anything in changes
|
||||
//noinspection LoopStatementThatDoesntLoopJS
|
||||
for (name in changes) {
|
||||
if (this._next) {
|
||||
this._next.popUp(attributes, changes);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop all animations effected by this modifier
|
||||
*/
|
||||
stop: function () {
|
||||
this.step();
|
||||
|
||||
var me = this,
|
||||
pool = me.animatingPool,
|
||||
i, ln;
|
||||
|
||||
for (i = 0, ln = pool.length; i < ln; i++) {
|
||||
pool[i].animating = false;
|
||||
}
|
||||
me.animatingPool.length = 0;
|
||||
me.animating = 0;
|
||||
Ext.draw.Animator.remove(me);
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
var me = this;
|
||||
me.animatingPool.length = 0;
|
||||
me.animating = 0;
|
||||
}
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue