[IMP] replacing by flotr2, initial commit
bzr revid: fp@tinyerp.com-20120507081908-j0sfm5f84f0t32yg
This commit is contained in:
parent
16a2d354fc
commit
dd6de9b200
|
@ -1,14 +1,24 @@
|
|||
{
|
||||
"name": "web Graph",
|
||||
"name": "Graph Views",
|
||||
"category" : "Hidden",
|
||||
"description":'Openerp web graph view',
|
||||
"version": "2.0",
|
||||
"description":"""Graph Views for Web Client
|
||||
|
||||
* Parse a <graph> view but allows changing dynamically the presentation
|
||||
* Graph Types: pie, lines, areas, bars, radar
|
||||
* Stacked / Not Stacked for areas and bars
|
||||
* Legends: top, inside (top/left), hidden
|
||||
* Features: download as PNG or CSV, browse data grid, switch orientation
|
||||
* Unlimited "Group By" levels, multi level analysis
|
||||
""",
|
||||
"version": "3.0",
|
||||
"depends": ['web'],
|
||||
"js": [
|
||||
"static/lib/dhtmlxGraph/codebase/thirdparty/excanvas/excanvas.js",
|
||||
"static/lib/dhtmlxGraph/codebase/dhtmlxchart.js",
|
||||
"static/src/js/graph.js"],
|
||||
"css": ["static/lib/dhtmlxGraph/codebase/dhtmlxchart.css"],
|
||||
"static/lib/flotr2/flotr2.js",
|
||||
"static/src/js/graph.js"
|
||||
],
|
||||
"css": [
|
||||
"static/src/css/*.css",
|
||||
],
|
||||
'qweb' : [
|
||||
"static/src/xml/*.xml",
|
||||
],
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
* Keep the minimum files for flotr2.js
|
||||
* Support clicking on a graph area to filter (or switch view?)
|
|
@ -1,73 +0,0 @@
|
|||
<h1>GNU GENERAL PUBLIC LICENSE</h1>
|
||||
Version 2, June 1991 </p>
|
||||
</p>
|
||||
<p>Copyright (C) 1989, 1991 Free Software Foundation, Inc. </p>
|
||||
<p>59 Temple Place - Suite 330, Boston, MA 02111-1307, USA</p>
|
||||
</p>
|
||||
<p>Everyone is permitted to copy and distribute verbatim copies</p>
|
||||
<p>of this license document, but changing it is not allowed.</p>
|
||||
</p>
|
||||
</p>
|
||||
<p>TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION</p>
|
||||
<p>0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". </p>
|
||||
</p>
|
||||
<p>Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. </p>
|
||||
</p>
|
||||
<p>1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. </p>
|
||||
</p>
|
||||
<p>You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. </p>
|
||||
</p>
|
||||
<p>2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: </p>
|
||||
</p>
|
||||
</p>
|
||||
<p>a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. </p>
|
||||
</p>
|
||||
<p>b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. </p>
|
||||
</p>
|
||||
<p>c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) </p>
|
||||
<p>These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. </p>
|
||||
</p>
|
||||
<p>Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. </p>
|
||||
</p>
|
||||
<p>In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. </p>
|
||||
</p>
|
||||
<p>3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: </p>
|
||||
</p>
|
||||
<p>a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, </p>
|
||||
</p>
|
||||
<p>b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, </p>
|
||||
</p>
|
||||
<p>c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) </p>
|
||||
<p>The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. </p>
|
||||
</p>
|
||||
<p>If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. </p>
|
||||
</p>
|
||||
<p>4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. </p>
|
||||
</p>
|
||||
<p>5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. </p>
|
||||
</p>
|
||||
<p>6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. </p>
|
||||
</p>
|
||||
<p>7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. </p>
|
||||
</p>
|
||||
<p>If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. </p>
|
||||
</p>
|
||||
<p>It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. </p>
|
||||
</p>
|
||||
<p>This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. </p>
|
||||
</p>
|
||||
<p>8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. </p>
|
||||
</p>
|
||||
<p>9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. </p>
|
||||
</p>
|
||||
<p>Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. </p>
|
||||
</p>
|
||||
<p>10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. </p>
|
||||
</p>
|
||||
<p>NO WARRANTY</p>
|
||||
</p>
|
||||
<p>11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. </p>
|
||||
</p>
|
||||
<p>12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. </p>
|
||||
</p>
|
||||
<p>
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
Copyright DHTMLX LTD. http://www.dhtmlx.com
|
||||
You allowed to use this component or parts of it under GPL terms
|
||||
To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com
|
||||
*/
|
||||
.dhx_tooltip{display:none;position:absolute;font-family:Tahoma;font-size:8pt;z-index:10000;background-color:white;padding:2px 2px 2px 2px;border:1px solid #A4BED4;}
|
||||
.dhx_chart{position:relative;font-family:Verdana;font-size:13px;color:#000;overflow:hidden;}
|
||||
.dhx_canvas_text{position:absolute;text-align:center;}
|
||||
.dhx_map_img{width:100%;height:100%;position:absolute;top:0;left:0;border:0;filter:alpha(opacity=0);}
|
||||
.dhx_axis_item_y{position:absolute;height:10px;line-height:10px;text-align:right;}
|
||||
.dhx_axis_title_y{text-align:center;font-family:Verdana;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-o-transform:rotate(-90deg);padding-left:3px;}
|
||||
.dhx_axis_item_x{text-align:left;margin-top:20px;margin-left:-14px;font-size:8pt;-webkit-transform:rotate(-60deg);-moz-transform:rotate(-60deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-o-transform:rotate(-60deg);padding-left:3px;}
|
||||
.dhx_axis_title_x{text-align:center;margin-top:50px;}
|
||||
.dhx_chart_legend{position:absolute;}
|
||||
.dhx_chart_legend_item{height:18px;line-height:18px;padding:2px;}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +0,0 @@
|
|||
/*
|
||||
Copyright DHTMLX LTD. http://www.dhtmlx.com
|
||||
You allowed to use this component or parts of it under GPL terms
|
||||
To use it on other terms or get Professional edition of the component please contact us at sales@dhtmlx.com
|
||||
*/
|
||||
/*pure colors*/
/*fonts*/
/*2010 September 28*//* DHX DEPEND FROM FILE 'tooltip.css'*//*style used by tooltip's container*/
.dhx_tooltip{
display:none;
position:absolute;
font-family:Tahoma;
font-size:8pt;
z-index:10000;
background-color:white;
padding:2px 2px 2px 2px;
border:1px solid #A4BED4;
}/* DHX DEPEND FROM FILE 'chart.css'*//*chart container*/
.dhx_chart{
position:relative;
font-family:Verdana;
font-size:13px;
color:#000000;
overflow:hidden;
}
/*labels*/
.dhx_canvas_text{
position:absolute;
text-align:center;
overflow:hidden;
white-space:nowrap;
}
/*map*/
.dhx_map_img{
width : 100%;
height : 100%;
position : absolute;
top : 0px;
left : 0px;
border:0px;
filter:alpha(opacity=0);
}
/*scales*/
.dhx_axis_item_y{
position:absolute;
height:10px;
line-height:10px;
text-align:right;
}
.dhx_axis_title_x{
text-align:center;
}
.dhx_axis_title_y{
text-align:center;
font-family:Verdana;
/*safari*/
-webkit-transform: rotate(-90deg);
/*firefox*/
-moz-transform: rotate(-90deg);
/*IE*/
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
/*opera*/
-o-transform:rotate(-90deg);
padding-left:3px;
}
/*legend block*/
.dhx_chart_legend{
position:absolute;
}
.dhx_chart_legend_item{
height:18px;
line-height:18px;
padding: 2px;
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +0,0 @@
|
|||
ExplorerCanvas
|
||||
|
||||
Google Open Source:
|
||||
<http://code.google.com>
|
||||
<opensource@google.com>
|
||||
|
||||
Developers:
|
||||
Emil A Eklund <emil@eae.net>
|
||||
Erik Arvidsson <erik@eae.net>
|
||||
Glen Murphy <glen@glenmurphy.com>
|
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,22 +0,0 @@
|
|||
ExplorerCanvas
|
||||
Copyright 2006 Google Inc.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
DESCRIPTION
|
||||
|
||||
Firefox, Safari and Opera 9 support the canvas tag to allow 2D command-based
|
||||
drawing operations. ExplorerCanvas brings the same functionality to Internet
|
||||
Explorer; web developers only need to include a single script tag in their
|
||||
existing canvas webpages to enable this support.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
INSTALLATION
|
||||
|
||||
Include the ExplorerCanvas tag in the same directory as your HTML files, and
|
||||
add the following code to your page, preferably in the <head> tag.
|
||||
|
||||
<!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->
|
||||
|
||||
If you run into trouble, please look at the included example code to see how
|
||||
to best implement this
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
dhtmlxChart v.2.6 Standard edition build 100928
|
||||
|
||||
This component is allowed to use under GPL or you need to obtain Commercial or Enterise License
|
||||
to use it in not GPL project. PLease contact sales@dhtmlx.com for details
|
||||
|
||||
|
||||
(c) DHTMLX Ltd.
|
|
@ -1,70 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Data Loading: XML</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
var barChart;
|
||||
window.onload=function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:document.getElementById("chart_container"),
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart.load("../common/sales.xml");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#sales#",
|
||||
details:"#year#"
|
||||
});
|
||||
barChart2.load("../common/sales.xml");
|
||||
}
|
||||
function reloada(){
|
||||
|
||||
barChart.destructor();
|
||||
barChart = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
details:"#year#"
|
||||
})
|
||||
barChart.load("../common/sales.xml");
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Chart data can be loaded from different sources: xml,json,JS array,csv<br/> This samples demonstrates loading from xml file.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
<input type="button" name="some_name" value="reload" onclick="reloada()">
|
||||
</body>
|
||||
</html>
|
|
@ -1,63 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Data Loading: JSON</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ value:"2.9", label:"2000" },
|
||||
{ value:"3.5", label:"2001" },
|
||||
{ value:"3.1", label:"2002" },
|
||||
{ value:"4.2", label:"2003" },
|
||||
{ value:"4.5", label:"2004" },
|
||||
{ value:"9.6", label:"2005" },
|
||||
{ value:"7.4", label:"2006" },
|
||||
{ value:"9.0", label:"2007" },
|
||||
{ value:"7.3", label:"2008" },
|
||||
{ value:"2.8", label:"2009" }
|
||||
];
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
label:"#label#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
details:"#label#"
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,65 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Data Loading: CSV</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = "\
|
||||
2.9, 2000\n\
|
||||
3.5, 2001\n\
|
||||
3.1, 2002\n\
|
||||
4.2, 2003\n\
|
||||
4.5, 2004\n\
|
||||
9.6, 2005\n\
|
||||
7.4, 2006\n\
|
||||
9.0, 2007\n\
|
||||
7.3, 2008\n\
|
||||
2.8, 2009";
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#data0#",
|
||||
label:"#data1#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart.parse(data,"csv");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#data0#",
|
||||
details:"#data1#"
|
||||
});
|
||||
barChart2.parse(data,"csv");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>When data are loaded from CSV string, you should use "data" and field index to define field in the template, for example, "#data0#","#data1#",etc.<br/>Charts in this sample represents values in the first column. Therefore, we have set value:"#data0#" in a chart contructor.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,68 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Data Loading: JS array</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
[ "2.9", "2000" ],
|
||||
[ "3.5", "2001" ],
|
||||
[ "3.1", "2002" ],
|
||||
[ "4.2", "2003" ],
|
||||
[ "4.5", "2004" ],
|
||||
[ "9.6", "2005" ],
|
||||
[ "7.4", "2006" ],
|
||||
[ "9.0", "2007" ],
|
||||
[ "7.3", "2008" ],
|
||||
[ "2.8", "2009" ]
|
||||
];
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#data0#",
|
||||
label:"#data1#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart.parse(data,"jsarray");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#data0#",
|
||||
details:"#data1#"
|
||||
});
|
||||
barChart2.parse(data,"jsarray");
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>When data are loaded from JS array, you should use "data" and field index to define field in the template, for example, "#data0#","#data1#",etc.<br/>Charts in this sample represents values in the first column. Therefore, we have set value:"#data0#" in a chart contructor.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,119 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>PieChart: Initiazation</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h1>BarChart: Scales</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Automatic vertical scale</td>
|
||||
<td>Custom vertical scale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:350px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0",sales1:"3.4",sales2:"1.2",sales3:"6.9", year:"2000" },
|
||||
{ sales:"3.8",sales1:"4.5",sales2:"2.5",sales3:"7.5", year:"2001" },
|
||||
{ sales:"3.4",sales1:"4.7",sales2:"1.9",sales3:"5.5", year:"2002" }
|
||||
];
|
||||
|
||||
|
||||
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:30,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
title:"Sales,million"
|
||||
},
|
||||
gradient:true,
|
||||
border:false,
|
||||
color: "#66cc33"
|
||||
})
|
||||
barChart1.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#"
|
||||
});
|
||||
barChart1.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff99",
|
||||
label:"#sales2#"
|
||||
});
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
|
||||
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:30,
|
||||
color: "#66cc33",
|
||||
label:"#sales#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start: 0,
|
||||
end: 10,
|
||||
step: 1,
|
||||
title:"Sales, mil"
|
||||
}
|
||||
|
||||
});
|
||||
barChart2.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#"
|
||||
});
|
||||
barChart2.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff99",
|
||||
label:"#sales2#"
|
||||
});
|
||||
barChart2.addSeries({
|
||||
value:"#sales3#",
|
||||
color:"#ccff99",
|
||||
label:"#sales3#"
|
||||
});
|
||||
|
||||
barChart2.parse(data,"json");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,66 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Colors: default</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"2.9", year:"2000" },
|
||||
{ sales:"3.5", year:"2001" },
|
||||
{ sales:"3.1", year:"2002" },
|
||||
{ sales:"4.2", year:"2003" },
|
||||
{ sales:"4.5", year:"2004" },
|
||||
{ sales:"9.6", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30
|
||||
})
|
||||
barChart.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#sales#",
|
||||
details:"#year#"
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Chart colors are defined by color property. If it is not set, chart is colored using "rainbow" algorithm.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,67 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Colors: custom colors</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"2.9", year:"2000", color:"#0000FF" },
|
||||
{ sales:"3.5", year:"2001", color:"#00FF00"},
|
||||
{ sales:"3.1", year:"2002", color:"#00FFFF"},
|
||||
{ sales:"4.2", year:"2003", color:"#FF0000"},
|
||||
{ sales:"4.5", year:"2004", color:"#FF00FF"},
|
||||
{ sales:"9.6", year:"2005", color:"#FFFF00"},
|
||||
{ sales:"7.4", year:"2006", color:"#880088"},
|
||||
{ sales:"9.0", year:"2007", color:"#008800"},
|
||||
{ sales:"7.3", year:"2008", color:"#880000"},
|
||||
{ sales:"4.8", year:"2009", color:"#000088"}
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
color:"#color#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30
|
||||
})
|
||||
barChart.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#sales#",
|
||||
details:"#year#",
|
||||
color:"#color#"
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Colors can be defined with template. In this sample colors and defined in data by "color" field.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px"></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,77 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Colors: custom logic</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"2.9", year:"2000" },
|
||||
{ sales:"3.5", year:"2001" },
|
||||
{ sales:"3.1", year:"2002" },
|
||||
{ sales:"4.2", year:"2003" },
|
||||
{ sales:"4.5", year:"2004" },
|
||||
{ sales:"9.6", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
width:30,
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
gradient:true,
|
||||
border:false,
|
||||
color:function(obj){
|
||||
if (obj.sales > 5) return "#FF0000";
|
||||
else return "#00FF00";
|
||||
}
|
||||
})
|
||||
barChart.parse(data,"json");
|
||||
|
||||
var index = 1;
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart_container2",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
gradient:true,
|
||||
color:function(obj,ratio){
|
||||
index++;
|
||||
if (index % 2) return "#FF0000";
|
||||
return "#00FF00";
|
||||
}
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Colors can be defined using function. In this sample color of bars depends on sales value, and colors of pie sectors are alternative.</p>
|
||||
<div id="chart_container" style="width:450px;height:300px;border:1px solid #A4BED4;float:left;margin-right:20px""></div>
|
||||
<div id="chart_container2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,64 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Colors: color gradient</title>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"2.9", year:"2000" },
|
||||
{ sales:"3.5", year:"2001" },
|
||||
{ sales:"3.1", year:"2002" },
|
||||
{ sales:"4.2", year:"2003" },
|
||||
{ sales:"4.5", year:"2004" },
|
||||
{ sales:"9.6", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:0,
|
||||
left:0
|
||||
},
|
||||
width:30,
|
||||
gradient:function(gradient){
|
||||
gradient.addColorStop(1.0,"#FF0000");
|
||||
gradient.addColorStop(0.2,"#FFFF00");
|
||||
gradient.addColorStop(0.0,"#00FF22");
|
||||
}
|
||||
})
|
||||
barChart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>The color of Bar Chart can be defined by gradient property. <br/>
|
||||
In this case you should set a function that takes a gradient object as an argument. And using addColorStop you may define colors for chart.</p>
|
||||
<div id="chart_container" style="width:500px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,96 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Grouping and Sorting</title>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var barChart;
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:70,
|
||||
label:"#id#",
|
||||
sort:{
|
||||
by:"#id#",
|
||||
as:"string",
|
||||
dir:"asc"
|
||||
},
|
||||
group:{
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
},
|
||||
padding:{
|
||||
bottom:0
|
||||
}
|
||||
})
|
||||
barChart.load("../common/stat.xml");
|
||||
}
|
||||
|
||||
function groupA(){
|
||||
barChart.group({
|
||||
by:"#company#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
});
|
||||
barChart.refresh();
|
||||
}
|
||||
|
||||
function groupB(){
|
||||
barChart.group({
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function groupC(){
|
||||
barChart.group({
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","max"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>It's possible to group chart data by a certain property. You may use group method or group property in a chart constructor.</p>
|
||||
<div id="chart_container" style="width:600px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
|
||||
<br/>
|
||||
<input type="button" name="some_name" value="Group by" onclick="window['group'+document.getElementById('groupby').value]()">
|
||||
<select name="groupby" id="groupby">
|
||||
<option value="A">company</option>
|
||||
<option value="B" SELECTED>year (total sales)</option>
|
||||
<option value="C">year (max sales)</option>
|
||||
</select>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,126 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales and grouping</title>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
<style>
|
||||
.dhx_chart_title{
|
||||
padding-left:3px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var barChart;
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:70,
|
||||
yAxis:{},
|
||||
xAxis:{
|
||||
template:"#id#",
|
||||
title:"Sales per year (all companies)"
|
||||
},
|
||||
label:"#sales#",
|
||||
sort:{
|
||||
by:"#id#",
|
||||
as:"string",
|
||||
dir:"asc"
|
||||
},
|
||||
group:{
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
}
|
||||
})
|
||||
barChart.load("../common/stat.xml");
|
||||
}
|
||||
|
||||
function groupA(){
|
||||
barChart.define("xAxis",{
|
||||
template:"#id#",
|
||||
title:"Total sales per companies"
|
||||
});
|
||||
barChart.define("yAxis",{});
|
||||
barChart.group({
|
||||
by:"#company#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
});
|
||||
barChart.refresh();
|
||||
}
|
||||
|
||||
function groupB(){
|
||||
barChart.define("xAxis",{
|
||||
template:"#id#",
|
||||
title:"Sales per year (all companies)"
|
||||
});
|
||||
barChart.define("yAxis",{});
|
||||
barChart.group({
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","sum"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function groupC(){
|
||||
barChart.define("xAxis",{
|
||||
template:"#id#",
|
||||
title:"Maximum sales per year"
|
||||
});
|
||||
barChart.define("yAxis",{});
|
||||
barChart.group({
|
||||
by:"#year#",
|
||||
map:{
|
||||
sales:["#sales#","max"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sort(direction){
|
||||
|
||||
barChart.define("sort",{
|
||||
by:"#sales#",
|
||||
dir:direction,
|
||||
as:"int"
|
||||
});
|
||||
barChart.render();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>To update configuration properties after grouping you need to cal define method with new settings.</p>
|
||||
<div id="chart_container" style="width:600px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
<br/>
|
||||
<input type="button" name="some_name" value="Group by" onclick="window['group'+document.getElementById('groupby').value]()">
|
||||
<select name="groupby" id="groupby">
|
||||
<option value="A">company</option>
|
||||
<option value="B" SELECTED>year (total sales)</option>
|
||||
<option value="C">year (max sales)</option>
|
||||
</select>
|
||||
<br/><br/>
|
||||
<input type="button" name="some_name" value="Sort by sales" onclick="sort(document.getElementById('dir').value)">
|
||||
<select name="dir" id="dir">
|
||||
<option value="asc" selected>asc</option>
|
||||
<option value="desc" >desc</option>
|
||||
</select>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,67 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Initialization</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"6300", company:"Company 1",color:"#d2ed7e" },
|
||||
{ sales:"8700", company:"Company 2",color:"#b3e025" },
|
||||
{ sales:"10500", company:"Company 3",color:"#9ac86d" },
|
||||
{ sales:"7800", company:"Company 4",color:"#e0e56c" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
color:"#color#",
|
||||
label:"#company#",
|
||||
pieInnerText:"<b>#sales#</b>",
|
||||
gradient:true
|
||||
});
|
||||
chart.parse(data,"json");
|
||||
|
||||
|
||||
var chart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
color:"#color#",
|
||||
label:"#company#",
|
||||
pieInnerText:"<b>#sales#</b>",
|
||||
gradient:true,
|
||||
radius:90,
|
||||
x:280,
|
||||
y:150
|
||||
});
|
||||
chart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td>Pie chart with Automatic radius and center position</td>
|
||||
<td>Pie chart with Custom radius and center position</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,52 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Labels</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"10.7", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var pieChart = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
pieInnerText:"#sales#",
|
||||
label:"#year#"
|
||||
});
|
||||
pieChart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<p>There are two properties to define sectors labels:
|
||||
<li>label - defines text outside sectors</li>
|
||||
<li>pieInnerText - set text inside sectors</li>
|
||||
</p>
|
||||
|
||||
<div id="chart" style="width:450px;height:300px;"></div></td>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,48 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>3D view</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"10.5", component:"dhxGrid",color:"#c7e1f3" },
|
||||
{ sales:"8.7", component:"dhxScheduler",color:"#45abf5" },
|
||||
{ sales:"6.3", component:"dhxTree",color:"#3590d0" },
|
||||
{ sales:"5.3", component:"dhxTabbar",color:"#BDDBF9" },
|
||||
{ sales:"4.5", component:"dhxLayout",color:"#d9e5ed" },
|
||||
{ sales:"4.2", component:"dhxMenu",color:"#aed7f4" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart = new dhtmlXChart({
|
||||
view:"pie3D",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
label:function(obj){
|
||||
var sum = chart.sum("#sales#");
|
||||
return obj.component+" ("+Math.round(parseFloat(obj.sales)/sum*100)+"%)";
|
||||
},
|
||||
color:"#color#",
|
||||
radius:110
|
||||
});
|
||||
chart.parse(data,"json");
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart" style="width:500px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Details Block</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
window.onload = function(){
|
||||
var pieChart = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
pieInnerText:"#sales#",
|
||||
gradient:true,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
legend:"#year#"
|
||||
});
|
||||
pieChart.parse(data,"json");
|
||||
|
||||
|
||||
var pieChart2 = new dhtmlXChart({
|
||||
view:"pie",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
pieInnerText:"#sales#",
|
||||
gradient:true,
|
||||
legend:{
|
||||
width: 75,
|
||||
align:"right",
|
||||
valign:"middle",
|
||||
marker:{
|
||||
type:"round",
|
||||
width:15
|
||||
},
|
||||
template:"#year#"
|
||||
}
|
||||
});
|
||||
pieChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<p>Pie Chart provides property "details" that displayes informationa about all sectors in a separate block.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>PieChart with default details block</td>
|
||||
<td>PieChart with custom details block</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,55 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Initialization</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#ffffff",
|
||||
color: "#000000"
|
||||
},
|
||||
line:{
|
||||
color:"#ff9900",
|
||||
width:3
|
||||
}
|
||||
})
|
||||
chart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Style definition</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
window.onload = function(){
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#ffffff",
|
||||
color: "#000000"
|
||||
},
|
||||
line:{
|
||||
color:"#ff9900",
|
||||
width:3
|
||||
}
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
|
||||
var chart2 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#3399ff",
|
||||
color: "#ffffff"
|
||||
},
|
||||
line:{
|
||||
color:"#3399ff",
|
||||
width:3
|
||||
}
|
||||
})
|
||||
chart2.parse(data,"json");
|
||||
|
||||
var data2 = [
|
||||
{ sales:"1.2", year:"2000" },
|
||||
{ sales:"2.4", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"5.9", year:"2005" },
|
||||
{ sales:"6.5", year:"2006" },
|
||||
{ sales:"6.1", year:"2007" },
|
||||
{ sales:"6.0", year:"2008" },
|
||||
{ sales:"6.2", year:"2009" }
|
||||
];
|
||||
var chart3 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart3",
|
||||
value:"#sales#",
|
||||
item:{
|
||||
radius:0
|
||||
},
|
||||
line:{
|
||||
color:"#b25151",
|
||||
width:3
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#",
|
||||
lines:true
|
||||
},
|
||||
yAxis:{
|
||||
start: 0,
|
||||
end: 10,
|
||||
step: 1,
|
||||
title:"Sales, mil"
|
||||
},
|
||||
tooltip:{
|
||||
template:"#year#"
|
||||
}
|
||||
})
|
||||
chart3.parse(data2,"json");
|
||||
|
||||
var chart4 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart4",
|
||||
value:"#sales#",
|
||||
item:{
|
||||
borderColor: "#000000",
|
||||
color: "#ffffff",
|
||||
borderWidth:1,
|
||||
radius:3
|
||||
},
|
||||
line:{
|
||||
color:"#3399ff",
|
||||
width:3
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start: 0,
|
||||
end: 10,
|
||||
step: 1,
|
||||
title:"Sales, mil",
|
||||
lines:false
|
||||
},
|
||||
tooltip:{
|
||||
template:"#year#"
|
||||
}
|
||||
})
|
||||
chart4.parse(data2,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart3" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart4" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,101 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#ffffff",
|
||||
color: "#000000"
|
||||
},
|
||||
line:{
|
||||
color:"#ff9900",
|
||||
width:3
|
||||
},
|
||||
yAxis:{},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
}
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
|
||||
var chart2 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#3399ff",
|
||||
color: "#ffffff"
|
||||
},
|
||||
line:{
|
||||
color:"#3399ff",
|
||||
width:3
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales, million"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#3399ff",
|
||||
color: "#ffffff"
|
||||
}
|
||||
})
|
||||
chart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td>Automatic vertical scale</td>
|
||||
<td>Custom vertical scale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Initialization</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart = new dhtmlXChart({
|
||||
view:"spline",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
item:{
|
||||
borderColor: "#ffffff",
|
||||
color: "#000000"
|
||||
},
|
||||
line:{
|
||||
color:"#ff9900",
|
||||
width:3
|
||||
},
|
||||
padding:{
|
||||
left:15
|
||||
}
|
||||
})
|
||||
chart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</body>
|
||||
</html>
|
|
@ -1,94 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ "companyA":"-1.9", "companyB":1, year:"2000" },
|
||||
{ "companyA":"-0.8", "companyB":0.8, year:"2001" },
|
||||
{ "companyA":"3.4", "companyB":1.9, year:"2002" },
|
||||
{ "companyA":"4.1", "companyB":2.5, year:"2003" },
|
||||
{ "companyA":"4.3", "companyB":3.1, year:"2004" },
|
||||
{ "companyA":"5.9", "companyB":4.5, year:"2005" },
|
||||
{ "companyA":"6.1", "companyB":5.7, year:"2006" },
|
||||
{ "companyA":"6.5", "companyB":7.2, year:"2007" },
|
||||
{ "companyA":"5.2", "companyB":6.5, year:"2008" },
|
||||
{ "companyA":"4.8", "companyB":6.8, year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"line",
|
||||
container:"chart1",
|
||||
value:"#companyA#",
|
||||
item:{
|
||||
borderColor: "#3399ff",
|
||||
color: "#ffffff"
|
||||
},
|
||||
line:{
|
||||
color:"#3399ff",
|
||||
width:3
|
||||
},
|
||||
xAxis:{
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:-2,
|
||||
step:1,
|
||||
end:8
|
||||
},
|
||||
padding:{
|
||||
left:35,
|
||||
bottom:20
|
||||
},
|
||||
origin:0,
|
||||
legend:{
|
||||
layout:"x",
|
||||
width: 75,
|
||||
align:"center",
|
||||
valign:"bottom",
|
||||
marker:{
|
||||
type:"round",
|
||||
width:15
|
||||
},
|
||||
values:[
|
||||
{text:"company A",color:"#3399ff"},
|
||||
{text:"company B",color:"#66cc00"}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
chart1.addSeries({
|
||||
value:"#companyB#",
|
||||
item:{
|
||||
borderColor: "#66cc00",
|
||||
color: "#ffffff"
|
||||
},
|
||||
line:{
|
||||
color:"#66cc00",
|
||||
width:3
|
||||
}
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:500px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,74 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>PieChart: Initiazation</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
width:30,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#66ccff",
|
||||
gradient:"3d",
|
||||
//border:false,
|
||||
width:25,
|
||||
padding:{
|
||||
bottom:0,
|
||||
right:50,
|
||||
left:50
|
||||
}
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<h1>BarChart: Initiazation</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -1,80 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>PieChart: Initiazation</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h1>BarChart: Initiazation</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#66cc33",
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
}
|
||||
})
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
}
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,99 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>PieChart: Initiazation</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h1>BarChart: Scales</h1>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Custom vertical scale and origin</td>
|
||||
<td>Automatic vertical scale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"-2.0", year:"2000" },
|
||||
{ sales:"-0.4", year:"2001" },
|
||||
{ sales:"1.1", year:"2002" },
|
||||
{ sales:"3.3", year:"2003" },
|
||||
{ sales:"1.2", year:"2004" },
|
||||
{ sales:"1.8", year:"2005" },
|
||||
{ sales:"4.1", year:"2006" },
|
||||
{ sales:"5.5", year:"2007" },
|
||||
{ sales:"3.5", year:"2008" },
|
||||
{ sales:"1.8", year:"2009" }
|
||||
];
|
||||
|
||||
|
||||
|
||||
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
width:30,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
template:"#year#",
|
||||
title:"Year"
|
||||
},
|
||||
|
||||
yAxis:{
|
||||
start:-3,
|
||||
end:7,
|
||||
step:1,
|
||||
title:"Profit"
|
||||
},
|
||||
origin:0,
|
||||
gradient:true,
|
||||
border:false
|
||||
})
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
|
||||
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:30,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
template:"#year#",
|
||||
title:"Year"
|
||||
},
|
||||
yAxis:{
|
||||
title:"Profit"
|
||||
}
|
||||
|
||||
});
|
||||
barChart2.parse(data,"json");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,207 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Chart</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h1>Chart</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart3" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart4" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart5" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart6" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
alpha:function(data){
|
||||
return data.sales/10;
|
||||
},
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#66cc33",
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales, million"
|
||||
}
|
||||
})
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:2,
|
||||
border:false,
|
||||
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Years",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales,<br/>million"
|
||||
}
|
||||
|
||||
})
|
||||
barChart2.parse(data,"json");
|
||||
|
||||
|
||||
var barChart3 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart3",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:35,
|
||||
border:false,
|
||||
gradient:true,
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales,<br/>million"
|
||||
}
|
||||
});
|
||||
barChart3.parse(data,"json");
|
||||
|
||||
var barChart4 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
alpha:function(data){
|
||||
return data.sales/10;
|
||||
},
|
||||
container:"chart4",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:50,
|
||||
gradient:function(gradient){
|
||||
gradient.addColorStop(1.0,"#FF0000");
|
||||
gradient.addColorStop(0.3,"#FFFF00");
|
||||
gradient.addColorStop(0.0,"#00FF22");
|
||||
},
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales,<br/>million"
|
||||
}
|
||||
})
|
||||
barChart4.parse(data,"json");
|
||||
|
||||
|
||||
var barChart5 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart5",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:35,
|
||||
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales,<br/>million"
|
||||
}
|
||||
})
|
||||
barChart5.parse(data,"json");
|
||||
|
||||
var barChart6 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
alpha:function(data){
|
||||
return data.sales/10;
|
||||
},
|
||||
container:"chart6",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#66cc33",
|
||||
border:false,
|
||||
width:30,
|
||||
radius:0,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
end:10,
|
||||
step:1,
|
||||
title:"Sales,<br/>million"
|
||||
}
|
||||
})
|
||||
barChart6.parse(data,"json");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,87 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>StackedBar Chart</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body onload="init()">
|
||||
<h1>StackedBar Chart</h1>
|
||||
<div id="chart1" style="width:450px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0",sales1:"3.4",sales2:"1.2",sales3:"6.9", year:"2000" },
|
||||
{ sales:"5.0",sales1:"5.0",sales2:"5.0",sales3:"5.0", year:"2001" },
|
||||
{ sales:"3.4",sales1:"4.7",sales2:"1.9",sales3:"5.5", year:"2002" }
|
||||
];
|
||||
function init(){
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"stackedBar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:60,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
title:"Sales,million"
|
||||
},
|
||||
gradient:"3d",
|
||||
//border:false,
|
||||
color: "#66cc33",
|
||||
legend:{
|
||||
values:[{text:"department A",color:"#66cc33"},{text:"department B",color:"#ff9933"},{text:"department C",color:"#ffff99"},{text:"department D",color:"#66ffcc"}],
|
||||
valign:"top",
|
||||
align:"right",
|
||||
width:120,
|
||||
layout:"y",
|
||||
marker:{
|
||||
width:15,
|
||||
type:"round"
|
||||
}
|
||||
}
|
||||
})
|
||||
barChart1.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#",
|
||||
tooltip:{
|
||||
template:"#sales1#"
|
||||
}
|
||||
});
|
||||
barChart1.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff99",
|
||||
label:"#sales2#",
|
||||
tooltip:{
|
||||
template:"#sales2#"
|
||||
}
|
||||
});
|
||||
barChart1.addSeries({
|
||||
value:"#sales3#",
|
||||
color:"#66ffcc",
|
||||
label:"#sales3#",
|
||||
tooltip:{
|
||||
template:"#sales3#"
|
||||
}
|
||||
});
|
||||
barChart1.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Series</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h1>Series</h1>
|
||||
|
||||
<div id="chart" style="width:500px;height:300px;border:1px solid #A4BED4;"></div>
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"4.2",sales1:"3.4",sales2:"1.2", year:"2000" },
|
||||
{ sales:"3.8",sales1:"4.5",sales2:"2.5", year:"2001" },
|
||||
{ sales:"3.4",sales1:"4.7",sales2:"1.9", year:"2002" }
|
||||
];
|
||||
|
||||
|
||||
|
||||
var barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:30,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
step:1,
|
||||
end:5,
|
||||
title:"Sales,million"
|
||||
},
|
||||
gradient:true,
|
||||
border:false,
|
||||
color: "#66cc33",
|
||||
legend:{
|
||||
values:[{text:"department A",color:"#66cc33"},{text:"department B",color:"#ff9933"},{text:"department C",color:"#ffff99"}],
|
||||
valign:"top",
|
||||
align:"center",
|
||||
width:120,
|
||||
layout:"x",
|
||||
marker:{
|
||||
width:15,
|
||||
type:"round"
|
||||
}
|
||||
}
|
||||
})
|
||||
barChart.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#"
|
||||
});
|
||||
barChart.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff99",
|
||||
label:"#sales2#"
|
||||
});
|
||||
barChart.parse(data,"json");
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,120 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Horizontal bar Chart</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
</head>
|
||||
<body >
|
||||
<h3>Horizontal bar Chart</h3>
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:450px;height:350px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:450px;height:350px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
|
||||
var data2 = [
|
||||
{ sales:"3.0",sales1:"3.4",sales2:"1.2",sales3:"6.9", year:"2007" },
|
||||
{ sales:"3.8",sales1:"4.5",sales2:"2.5",sales3:"7.5", year:"2008" },
|
||||
{ sales:"3.4",sales1:"4.7",sales2:"1.9",sales3:"5.5", year:"2009" }
|
||||
];
|
||||
var barChart1 = new dhtmlXChart({
|
||||
view:"barH",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
width:50,
|
||||
label: "#sales#",
|
||||
gradient:"3d",
|
||||
color:"#ffcc00",
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
lines: true
|
||||
},
|
||||
yAxis:{
|
||||
title:"Year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{
|
||||
left:75,
|
||||
right:30
|
||||
}
|
||||
})
|
||||
barChart1.parse(data,"json");
|
||||
|
||||
|
||||
|
||||
var barChart2 = new dhtmlXChart({
|
||||
view:"barH",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
width:30,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year"
|
||||
},
|
||||
yAxis:{
|
||||
title:"Year",
|
||||
template:"#year#"
|
||||
},
|
||||
gradient:true,
|
||||
border:false,
|
||||
color: "#66cc33",
|
||||
legend:{
|
||||
values:[{text:"department A",color:"#66cc33"},{text:"department B",color:"#ff9933"},{text:"department C",color:"#ffff99"}],
|
||||
|
||||
width:120,
|
||||
align:"right",
|
||||
valign:"middle",
|
||||
marker:{
|
||||
width:15,
|
||||
type:"round"
|
||||
}
|
||||
},
|
||||
padding:{
|
||||
left:65
|
||||
}
|
||||
});
|
||||
barChart2.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#"
|
||||
});
|
||||
barChart2.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff99",
|
||||
label:"#sales2#"
|
||||
});
|
||||
barChart2.parse(data2,"json");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,75 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Horizontal Stacked Bars</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
|
||||
var data = [
|
||||
{ sales:"3.0",sales1:"3.4",sales2:"1.2",sales3:"6.9", year:"2007" },
|
||||
{ sales:"5.0",sales1:"5.0",sales2:"5.0",sales3:"5.0", year:"2008" },
|
||||
{ sales:"3.4",sales1:"4.7",sales2:"1.9",sales3:"5.5", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"stackedBarH",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
width:50,
|
||||
label: "#sales#",
|
||||
gradient:"3d",
|
||||
color: "#66cc33",
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
lines: false
|
||||
},
|
||||
yAxis:{
|
||||
title:"Year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{left:75},
|
||||
legend:{
|
||||
values:[{text:"department A",color:"#66cc33"},{text:"department B",color:"#ff9933"},{text:"department C",color:"#ffff66"},{text:"department D",color:"#66ffcc"}],
|
||||
valign:"top",
|
||||
align:"left",
|
||||
width:120,
|
||||
layout:"x",
|
||||
marker:{
|
||||
width:15,
|
||||
type:"round"
|
||||
}
|
||||
}
|
||||
})
|
||||
barChart.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#ff9933",
|
||||
label:"#sales1#"
|
||||
});
|
||||
barChart.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#ffff66",
|
||||
label:"#sales2#"
|
||||
});
|
||||
barChart.addSeries({
|
||||
value:"#sales3#",
|
||||
color:"#66ffcc",
|
||||
label:"#sales3#"
|
||||
});
|
||||
barChart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart" style="width:480px;height:350px;border:1px solid #A4BED4;"></div></td>
|
||||
</body>
|
||||
</html>
|
|
@ -1,55 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Initialization</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"7.6", year:"2005" },
|
||||
{ sales:"7.8", year:"2006" },
|
||||
{ sales:"7.2", year:"2007" },
|
||||
{ sales:"5.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart = new dhtmlXChart({
|
||||
view:"area",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
label:"#year#",
|
||||
color:"#00ccff",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
|
||||
alpha:0.8,
|
||||
padding:{
|
||||
left:0,
|
||||
right:0,
|
||||
bottom:0
|
||||
}
|
||||
})
|
||||
chart.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="chart" style="width:450px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<style>
|
||||
.dhx_axis_item_x{
|
||||
font-size: 10px
|
||||
}
|
||||
.dhx_axis_item_y{
|
||||
font-size: 10px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"7.6", year:"2005" },
|
||||
{ sales:"7.8", year:"2006" },
|
||||
{ sales:"7.2", year:"2007" },
|
||||
{ sales:"5.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"area",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
color:"#00ccff",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
padding:{
|
||||
left:30
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
step:1,
|
||||
end:10
|
||||
},
|
||||
xAxis:{
|
||||
lines:true,
|
||||
title:"sales per year",
|
||||
template:"#year#"
|
||||
}
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
|
||||
var chart2 = new dhtmlXChart({
|
||||
view:"area",
|
||||
container:"chart2",
|
||||
value:"#sales#",
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
color:"#00ccff",
|
||||
xAxis:{
|
||||
title:"sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
yAxis:{
|
||||
},
|
||||
padding:{
|
||||
left:30
|
||||
},
|
||||
item:{
|
||||
borderColor: "#3399ff",
|
||||
color: "#ffffff"
|
||||
}
|
||||
})
|
||||
chart2.parse(data,"json");
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td>Custom vertical scale</td>
|
||||
<td>Automatic vertical scale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:470px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
<td><div id="chart2" style="width:470px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<style>
|
||||
.dhx_axis_item_x{
|
||||
font-size: 10px
|
||||
}
|
||||
.dhx_axis_item_y{
|
||||
font-size: 10px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:3.0, sales1:2.8, sales2:3.4, year:"2000" },
|
||||
{ sales:3.8, sales1:2.5, sales2:2.0, year:"2001" },
|
||||
{ sales:3.4, sales1:1.7, sales2:3.1, year:"2002" },
|
||||
{ sales:4.1, sales1:2.7, sales2:3.5, year:"2003" },
|
||||
{ sales:4.3, sales1:3.6, sales2:4.0, year:"2004" },
|
||||
{ sales:7.6, sales1:3.1, sales2:4.9, year:"2005" },
|
||||
{ sales:7.8, sales1:3.9, sales2:5.2, year:"2006" },
|
||||
{ sales:7.2, sales1:3.6, sales2:5.6, year:"2007" },
|
||||
{ sales:5.3, sales1:2.9, sales2:4.1, year:"2008" },
|
||||
{ sales:4.8, sales1:2.1, sales2:3.9, year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"area",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
color:"#00ccff",
|
||||
alpha:0.6,
|
||||
padding:{
|
||||
left:30
|
||||
},
|
||||
yAxis:{
|
||||
start:0,
|
||||
step:1,
|
||||
end:8
|
||||
},
|
||||
xAxis:{
|
||||
lines:true,
|
||||
title:"sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
legend:{
|
||||
layout:"x",
|
||||
width: 75,
|
||||
align:"center",
|
||||
valign:"top",
|
||||
marker:{
|
||||
type:"round",
|
||||
width:15
|
||||
},
|
||||
values:[
|
||||
{text:"company A",color:"#00ccff"},
|
||||
{text:"company B",color:"#0000ff"},
|
||||
{text:"company C",color:"#cc33ff"}
|
||||
]
|
||||
}
|
||||
})
|
||||
chart1.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#0000ff"
|
||||
})
|
||||
chart1.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#cc33ff"
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<div id="chart" style="width:470px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,88 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Scales</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<style>
|
||||
.dhx_axis_item_x{
|
||||
font-size: 10px
|
||||
}
|
||||
.dhx_axis_item_y{
|
||||
font-size: 10px
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:3.0, sales1:2.8, sales2:3.9, year:"2000" },
|
||||
{ sales:3.8, sales1:2.5, sales2:2.0, year:"2001" },
|
||||
{ sales:3.4, sales1:1.7, sales2:3.1, year:"2002" },
|
||||
{ sales:4.1, sales1:2.7, sales2:3.5, year:"2003" },
|
||||
{ sales:4.3, sales1:3.6, sales2:4.0, year:"2004" },
|
||||
{ sales:7.6, sales1:3.1, sales2:4.9, year:"2005" },
|
||||
{ sales:7.8, sales1:3.9, sales2:5.2, year:"2006" },
|
||||
{ sales:7.2, sales1:3.6, sales2:5.6, year:"2007" },
|
||||
{ sales:5.3, sales1:2.9, sales2:4.1, year:"2008" },
|
||||
{ sales:4.8, sales1:2.1, sales2:3.9, year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
var chart1 = new dhtmlXChart({
|
||||
view:"stackedArea",
|
||||
container:"chart",
|
||||
value:"#sales#",
|
||||
color:"#00ccff",
|
||||
alpha:0.6,
|
||||
padding:{
|
||||
left:30
|
||||
},
|
||||
yAxis:{
|
||||
|
||||
},
|
||||
xAxis:{
|
||||
lines:true,
|
||||
title:"sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
legend:{
|
||||
layout:"x",
|
||||
width: 75,
|
||||
align:"center",
|
||||
valign:"top",
|
||||
marker:{
|
||||
type:"round",
|
||||
width:15
|
||||
},
|
||||
values:[
|
||||
{text:"company A",color:"#00ccff"},
|
||||
{text:"company B",color:"#0000ff"},
|
||||
{text:"company C",color:"#cc33ff"}
|
||||
]
|
||||
}
|
||||
})
|
||||
chart1.addSeries({
|
||||
value:"#sales1#",
|
||||
color:"#0000ff"
|
||||
})
|
||||
chart1.addSeries({
|
||||
value:"#sales2#",
|
||||
color:"#cc33ff"
|
||||
})
|
||||
chart1.parse(data,"json");
|
||||
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<div id="chart" style="width:470px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,83 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Adding</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
var barChart1;
|
||||
|
||||
window.onload = function(){
|
||||
barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{
|
||||
top:20,
|
||||
left:0,
|
||||
right:0
|
||||
}
|
||||
});
|
||||
barChart1.parse(data,"json");
|
||||
}
|
||||
|
||||
var counter = 2010;
|
||||
function add_new () {
|
||||
barChart1.add({
|
||||
year:counter,
|
||||
sales:(Math.random()*5).toFixed(2)
|
||||
});
|
||||
counter++;
|
||||
}
|
||||
function delete_first(){
|
||||
barChart1.remove(barChart1.first());
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:750px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
<input type="button" value="add" onclick="add_new()">
|
||||
<input type="button" value="delete" onclick="delete_first()">
|
||||
</body>
|
||||
</html>
|
|
@ -1,96 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Events</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
var barChart1;
|
||||
window.onload = function(){
|
||||
barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{
|
||||
top:20,
|
||||
left:0,
|
||||
right:0
|
||||
}
|
||||
});
|
||||
barChart1.parse(data,"json");
|
||||
barChart1.attachEvent("onMouseMove", function(id){
|
||||
id = barChart1.get(id).year;
|
||||
log("onMouseMove", id);
|
||||
return true;
|
||||
});
|
||||
barChart1.attachEvent("onMouseOut", function(id){
|
||||
log("onMouseOut", id);
|
||||
return true;
|
||||
});
|
||||
|
||||
barChart1.attachEvent("onItemClick", function(id){
|
||||
id = barChart1.get(id).year;
|
||||
log("onItemClick", id);
|
||||
return true;
|
||||
});
|
||||
barChart1.attachEvent("onItemDblClick", function(id){
|
||||
id = barChart1.get(id).year;
|
||||
log("onItemDblClick", id);
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function log(name, id){
|
||||
var t = document.createElement("DIV");
|
||||
t.innerHTML = name+" for element "+id;
|
||||
|
||||
var p = document.getElementById("log_div");
|
||||
p.insertBefore(t, p.firstChild);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:750px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="log_div" style="widht:950px; height:300px; font-family:Tahoma;overflow:auto"></div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,73 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Sorting</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
var barChart1;
|
||||
|
||||
window.onload = function(){
|
||||
barChart1 = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{
|
||||
top:20,
|
||||
left:0,
|
||||
right:0
|
||||
}
|
||||
});
|
||||
barChart1.parse(data,"json");
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body >
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:750px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<input type="button" value="sort by sales, ASC" onclick="barChart1.sort('#sales#','asc');">
|
||||
<input type="button" value="sort by sales, DESC" onclick="barChart1.sort('#sales#','desc');">
|
||||
<input type="button" value="sort by year, ASC" onclick="barChart1.sort('#year#','asc');">
|
||||
<input type="button" value="sort by year, DESC" onclick="barChart1.sort('#year#','desc');">
|
||||
</body>
|
||||
</html>
|
|
@ -1,81 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Filtering</title>
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart1",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
color:"#b3e025",
|
||||
gradient:true,
|
||||
border:false,
|
||||
width:50,
|
||||
tooltip:{
|
||||
template:"#sales#"
|
||||
},
|
||||
xAxis:{
|
||||
title:"Sales per year",
|
||||
template:"#year#"
|
||||
},
|
||||
padding:{
|
||||
top:30,
|
||||
left:0,
|
||||
right:0
|
||||
}
|
||||
});
|
||||
barChart.parse(data,"json");
|
||||
}
|
||||
|
||||
function filter_year(){
|
||||
barChart.filter(function(obj){
|
||||
return obj.year >2005;
|
||||
})
|
||||
}
|
||||
function filter_sales(){
|
||||
barChart.filter(function(obj){
|
||||
return obj.sales < 8;
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td><div id="chart1" style="width:750px;height:300px;border:1px solid #A4BED4;"></div></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<input type="button" value="show all" onclick="barChart.filter();">
|
||||
<input type="button" value="show year > 2005" onclick="filter_year()">
|
||||
<input type="button" value="show sales < 8" onclick="filter_sales()">
|
||||
</body>
|
||||
</html>
|
|
@ -1,70 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Integration with grid</title>
|
||||
<link rel='STYLESHEET' type='text/css' href='../../../dhtmlxGrid/codebase/dhtmlxgrid.css'>
|
||||
<link rel='STYLESHEET' type='text/css' href='../../../dhtmlxGrid/codebase/skins/dhtmlxgrid_dhx_skyblue.css'>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxcommon.js'></script>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxgrid.js'></script>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxgridcell.js'></script>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var barChart;
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
color:"#66ccff",
|
||||
gradient:"3d",
|
||||
container:"chart_container",
|
||||
value:"#data0#",
|
||||
label:"#data0#",
|
||||
radius:3,
|
||||
tooltip:{
|
||||
template:"#data0#"
|
||||
},
|
||||
width:30,
|
||||
origin:0,
|
||||
yAxis:{},
|
||||
xAxis:{
|
||||
template:function(){return ""}
|
||||
},
|
||||
border:false
|
||||
});
|
||||
|
||||
function refresh_chart(){
|
||||
barChart.clearAll();
|
||||
barChart.parse(mygrid,"dhtmlxgrid");
|
||||
};
|
||||
|
||||
mygrid = new dhtmlXGridObject('gridbox');
|
||||
mygrid.setImagePath('../../../dhtmlxGrid/codebase/imgs/');
|
||||
mygrid.setSkin("dhx_skyblue")
|
||||
mygrid.loadXML("../../../dhtmlxGrid/samples/common/gridH.xml",refresh_chart);
|
||||
mygrid.attachEvent("onEditCell",function(stage){
|
||||
if (stage == 2)
|
||||
refresh_chart();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="gridbox" style="width:600px; height:270px; background-color:white;"></div>
|
||||
<hr>
|
||||
<div id="chart_container" style="width:500px;height:300px;"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,79 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Integration with Grid and Grouping</title>
|
||||
<link rel='STYLESHEET' type='text/css' href='../../../dhtmlxGrid/codebase/dhtmlxgrid.css'>
|
||||
<link rel='STYLESHEET' type='text/css' href='../../../dhtmlxGrid/codebase/skins/dhtmlxgrid_dhx_skyblue.css'>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxcommon.js'></script>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxgrid.js'></script>
|
||||
<script src='../../../dhtmlxGrid/codebase/dhtmlxgridcell.js'></script>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
<script>
|
||||
var barChart;
|
||||
window.onload = function(){
|
||||
barChart = new dhtmlXChart({
|
||||
view:"bar",
|
||||
container:"chart_container",
|
||||
value:"#sales#",
|
||||
label:"#sales#",
|
||||
sort:{
|
||||
by:"#sales#",
|
||||
as:"int"
|
||||
},
|
||||
group:{
|
||||
by:"#data2#",
|
||||
map:{
|
||||
author:["#data2#"],
|
||||
sales:["#data0#","sum"]
|
||||
}
|
||||
},
|
||||
xAxis:{
|
||||
template:"#author#"
|
||||
},
|
||||
padding:{
|
||||
left:0,
|
||||
right:0
|
||||
},
|
||||
color:"#45abf5",
|
||||
gradient:true,
|
||||
width:50,
|
||||
border:false
|
||||
});
|
||||
|
||||
function refresh_chart(){
|
||||
barChart.clearAll();
|
||||
barChart.parse(mygrid,"dhtmlxgrid");
|
||||
};
|
||||
|
||||
mygrid = new dhtmlXGridObject('gridbox');
|
||||
mygrid.setImagePath('../../../dhtmlxGrid/codebase/imgs/');
|
||||
mygrid.setSkin("dhx_skyblue")
|
||||
mygrid.loadXML("../../../dhtmlxGrid/samples/common/gridH.xml",refresh_chart);
|
||||
mygrid.attachEvent("onEditCell",function(stage){
|
||||
if (stage == 2)
|
||||
refresh_chart();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="gridbox" style="width:600px; height:270px; background-color:white;"></div>
|
||||
<hr>
|
||||
<div id="chart_container" style="width:600px;height:300px;"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,77 +0,0 @@
|
|||
<!--conf
|
||||
<sample>
|
||||
<product version="2.6" edition="std"/>
|
||||
<modifications>
|
||||
<modified date="100609"/>
|
||||
</modifications>
|
||||
</sample>
|
||||
-->
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Integration with dhtmlxWindows</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../../../dhtmlxWindows/codebase/dhtmlxwindows.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../../dhtmlxWindows/codebase/skins/dhtmlxwindows_dhx_skyblue.css">
|
||||
|
||||
<script src="../../../dhtmlxWindows/codebase/dhtmlxcommon.js"></script>
|
||||
<script src="../../../dhtmlxWindows/codebase/dhtmlxwindows.js"></script>
|
||||
<script src="../../../dhtmlxWindows/codebase/dhtmlxcontainer.js"></script>
|
||||
|
||||
<script src="../../codebase/dhtmlxchart.js" type="text/javascript"></script>
|
||||
<link rel="STYLESHEET" type="text/css" href="../../codebase/dhtmlxchart.css">
|
||||
|
||||
</head>
|
||||
<style type="text/css" media="screen">
|
||||
html, body{
|
||||
height:100%;
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
}
|
||||
</style>
|
||||
<body onload="doOnLoad();">
|
||||
|
||||
<script>
|
||||
var dhxWins
|
||||
var data = [
|
||||
{ sales:"3.0", year:"2000" },
|
||||
{ sales:"3.8", year:"2001" },
|
||||
{ sales:"3.4", year:"2002" },
|
||||
{ sales:"4.1", year:"2003" },
|
||||
{ sales:"4.3", year:"2004" },
|
||||
{ sales:"9.9", year:"2005" },
|
||||
{ sales:"7.4", year:"2006" },
|
||||
{ sales:"9.0", year:"2007" },
|
||||
{ sales:"7.3", year:"2008" },
|
||||
{ sales:"4.8", year:"2009" }
|
||||
];
|
||||
|
||||
|
||||
function doOnLoad() {
|
||||
dhxWinsParams = {
|
||||
image_path: "../../../dhtmlxWindows/codebase/imgs/",
|
||||
wins: [ {id: "w1", left: 230, top: 230, width: 420, height: 340}]
|
||||
};
|
||||
dhxWins = new dhtmlXWindows(dhxWinsParams);
|
||||
pieChart2 = dhxWins.window("w1").attachChart({
|
||||
view:"pie",
|
||||
value:"#sales#",
|
||||
pieInnerText:"#sales#",
|
||||
gradient:true,
|
||||
details:{
|
||||
width: 75,
|
||||
align:"right",
|
||||
valign:top,
|
||||
marker:{
|
||||
type:"round",
|
||||
width:15
|
||||
},
|
||||
template:"#year#"
|
||||
}
|
||||
});
|
||||
|
||||
pieChart2.parse(data,"json");
|
||||
};
|
||||
</script>
|
||||
</body>
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
Copyright DHTMLX LTD. http://www.dhtmlx.com
|
||||
This version of Software is free for using in non-commercial applications.
|
||||
For commercial use please contact sales@dhtmlx.com to obtain license
|
||||
*/
|
||||
|
||||
$mysql_host = "localhost";
|
||||
$mysql_user = "root";
|
||||
$mysql_pasw = "";
|
||||
$mysql_db = "sampledb";
|
||||
|
||||
?>
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
$str = "<data>";
|
||||
|
||||
for ($i=1; $i <= 100; $i++) {
|
||||
$sales = rand(100,1000);
|
||||
$year = rand(1996,2009);
|
||||
$company = "Company ".rand(1,3);
|
||||
$str.="<item id='{$i}' sales='{$sales}' year='{$year}' company='{$company}'></item>";
|
||||
}
|
||||
|
||||
$str .= "</data>";
|
||||
|
||||
file_put_contents("stat.xml",$str);
|
||||
|
||||
?>
|
|
@ -1,46 +0,0 @@
|
|||
<data>
|
||||
<item id="1">
|
||||
<sales>2.9</sales>
|
||||
<year>2000</year>
|
||||
</item>
|
||||
<item id="2">
|
||||
<sales>3.5</sales>
|
||||
<year>2001</year>
|
||||
</item>
|
||||
<item id="3">
|
||||
<sales>3.1</sales>
|
||||
<year>2002</year>
|
||||
</item>
|
||||
<item id="4">
|
||||
<sales>4.2</sales>
|
||||
<year>2003</year>
|
||||
</item>
|
||||
<item id="5">
|
||||
<sales>4.5</sales>
|
||||
<year>2004</year>
|
||||
</item>
|
||||
<item id="6">
|
||||
<sales>7.4</sales>
|
||||
<year>2005</year>
|
||||
</item>
|
||||
<item id="7">
|
||||
<sales>9.6</sales>
|
||||
<year>2006</year>
|
||||
</item>
|
||||
<item id="8">
|
||||
<sales>9</sales>
|
||||
<year>2007</year>
|
||||
</item>
|
||||
<item id="9">
|
||||
<sales>7.3</sales>
|
||||
<year>2008</year>
|
||||
</item>
|
||||
<item id="10">
|
||||
<sales>5.8</sales>
|
||||
<year>2009</year>
|
||||
</item>
|
||||
<item id="11">
|
||||
<sales>7.7</sales>
|
||||
<year>2010</year>
|
||||
</item>
|
||||
</data>
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +0,0 @@
|
|||
Next samples
|
||||
08_integration\01_dhtmlxgrid.html
|
||||
08_integration\02_dhtmlxgrid_group.html
|
||||
08_integration\03_layout.html
|
||||
requires external components ( dhtmlxgrid and dhtmlxwindow )
|
||||
|
||||
Its recommended to run samples through any kind of webserver.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2012 Carl Sutherland
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,35 @@
|
|||
all: test flotr2
|
||||
|
||||
test:
|
||||
cd spec; jasmine-headless-webkit -j jasmine.yml -c
|
||||
|
||||
libraries:
|
||||
smoosh make/lib.json
|
||||
cat ./build/bean.js > build/lib.js
|
||||
cat ./build/underscore.js >> build/lib.js
|
||||
cat ./build/bean.min.js > build/lib.min.js
|
||||
echo ";" >> build/lib.min.js
|
||||
cat ./build/underscore.min.js >> build/lib.min.js
|
||||
echo ";" >> build/lib.min.js
|
||||
|
||||
ie:
|
||||
smoosh make/ie.json
|
||||
|
||||
flotr2: libraries ie
|
||||
smoosh make/flotr2.json
|
||||
cat build/lib.js build/flotr2.js > flotr2.js
|
||||
cat build/lib.min.js > flotr2.min.js
|
||||
cat build/flotr2.min.js >> flotr2.min.js
|
||||
echo ';' >> flotr2.min.js
|
||||
cp build/ie.min.js flotr2.ie.min.js
|
||||
|
||||
flotr2-basic: libraries ie
|
||||
smoosh make/basic.json
|
||||
cat build/lib.min.js > flotr2-basic.min.js
|
||||
cat build/flotr2-basic.min.js >> flotr2-basic.min.js
|
||||
|
||||
flotr-examples:
|
||||
smoosh make/examples.json
|
||||
cp build/examples.min.js flotr2.examples.min.js
|
||||
cp build/examples-types.js flotr2.examples.types.js
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
Flotr2
|
||||
======
|
||||
|
||||
The Canvas graphing library.
|
||||
|
||||
![Google Groups](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif)
|
||||
|
||||
http://groups.google.com/group/flotr2/
|
||||
|
||||
Please fork http://jsfiddle.net/cesutherland/ZFBj5/ with your question or bug reproduction case.
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
The API consists of a primary draw method which accepts a configuration object, helper methods, and several microlibs.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
var
|
||||
// Container div:
|
||||
container = document.getElementById("flotr-example-graph"),
|
||||
// First data series:
|
||||
d1 = [[0, 3], [4, 8], [8, 5], [9, 13]],
|
||||
// Second data series:
|
||||
d2 = [],
|
||||
// A couple flotr configuration options:
|
||||
options = {
|
||||
xaxis: {
|
||||
minorTickFreq: 4
|
||||
},
|
||||
grid: {
|
||||
minorVerticalLines: true
|
||||
}
|
||||
},
|
||||
i, graph;
|
||||
|
||||
// Generated second data set:
|
||||
for (i = 0; i < 14; i += 0.5) {
|
||||
d2.push([i, Math.sin(i)]);
|
||||
}
|
||||
|
||||
// Draw the graph:
|
||||
graph = Flotr.draw(
|
||||
container, // Container element
|
||||
[ d1, d2 ], // Array of data series
|
||||
options // Configuration options
|
||||
);
|
||||
```
|
||||
|
||||
### Microlibs
|
||||
|
||||
* [underscore.js](http://documentcloud.github.com/underscore/)
|
||||
* [bean.js](https://github.com/fat/bean)
|
||||
|
||||
Extending
|
||||
---------
|
||||
|
||||
Flotr may be extended by adding new plugins and graph types.
|
||||
|
||||
### Graph Types
|
||||
|
||||
Graph types define how a particular chart is rendered. Examples include line, bar, pie.
|
||||
|
||||
Existing graph types are found in `js/types/`.
|
||||
|
||||
### Plugins
|
||||
|
||||
Plugins extend the core of flotr with new functionality. They can add interactions, new decorations, etc. Examples
|
||||
include titles, labels and selection.
|
||||
|
||||
The plugins included are found in `js/plugins/`.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
This project uses [smoosh](https://github.com/fat/smoosh) to build and [jasmine](http://pivotal.github.com/jasmine/)
|
||||
with [js-imagediff](https://github.com/HumbleSoftware/js-imagediff) to test. Tests may be executed by
|
||||
[jasmine-headless-webkit](http://johnbintz.github.com/jasmine-headless-webkit/) with
|
||||
`cd spec; jasmine-headless-webkit -j jasmine.yml -c` or by a browser by navigating to
|
||||
`flotr2/spec/SpecRunner.html`.
|
||||
|
||||
Shoutouts
|
||||
---------
|
||||
|
||||
Thanks to Bas Wenneker, Fabien Ménager and others for all the work on the original Flotr.
|
||||
Thanks to Jochen Berger and Jordan Santell for their contributions to Flotr2.
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
Flotr 2 Architecture Notes
|
||||
|
||||
|
||||
Global:
|
||||
======
|
||||
|
||||
Flotr.js -
|
||||
versioning information
|
||||
browser detection
|
||||
extension (plugins, graph types)
|
||||
draw
|
||||
clone / merge
|
||||
tick size
|
||||
tick formatter
|
||||
engineering notation
|
||||
magnitude
|
||||
rad, pixel, floor
|
||||
drawText
|
||||
measureText
|
||||
getBestTextAlign
|
||||
align map
|
||||
compatibility
|
||||
|
||||
|
||||
Graph Architecture:
|
||||
===================
|
||||
|
||||
Axis -
|
||||
all series
|
||||
orientation
|
||||
ticks (major, minor)
|
||||
scale (d2p, p2d, logarithmic)
|
||||
notion of stacks
|
||||
|
||||
Series -
|
||||
per 'data'
|
||||
notion of range (x, y, min, max)
|
||||
|
||||
Graph -
|
||||
DOM constructon
|
||||
event attachment
|
||||
options initialization
|
||||
data range calculations
|
||||
canvas spacing calculations
|
||||
event normalization
|
||||
draw methods
|
||||
DOM cleanup
|
||||
event cleanup
|
||||
|
||||
|
||||
Utilities:
|
||||
==========
|
||||
|
||||
Color
|
||||
build colors
|
||||
parse textual color data
|
||||
convert colors
|
||||
clone colors
|
||||
|
||||
Text
|
||||
calculate text size
|
||||
canvas size
|
||||
html size
|
||||
|
||||
Date
|
||||
formatting
|
||||
constants
|
||||
|
||||
|
||||
Spacing Calculation
|
||||
===================
|
||||
|
||||
Flotr
|
||||
calculate data
|
||||
calculate margins
|
||||
|
||||
Chart
|
||||
calculate Data Ranges - Explicit or auto data minimum, maximums
|
||||
calculate Data Range Extensions - By chart type, extend data range with needs of chart type (ie. stacked bars, stacked lines)
|
||||
add Chart Padding - By chart type
|
||||
|
||||
Text
|
||||
use explicit margins
|
||||
calculate label margins
|
||||
calculate title margins
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,303 @@
|
|||
/**
|
||||
* Flotr Axis Library
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
var
|
||||
_ = Flotr._,
|
||||
LOGARITHMIC = 'logarithmic';
|
||||
|
||||
function Axis (o) {
|
||||
|
||||
this.orientation = 1;
|
||||
this.offset = 0;
|
||||
this.datamin = Number.MAX_VALUE;
|
||||
this.datamax = -Number.MAX_VALUE;
|
||||
|
||||
_.extend(this, o);
|
||||
|
||||
this._setTranslations();
|
||||
}
|
||||
|
||||
|
||||
// Prototype
|
||||
Axis.prototype = {
|
||||
|
||||
setScale : function () {
|
||||
var length = this.length;
|
||||
if (this.options.scaling == LOGARITHMIC) {
|
||||
this.scale = length / (log(this.max, this.options.base) - log(this.min, this.options.base));
|
||||
} else {
|
||||
this.scale = length / (this.max - this.min);
|
||||
}
|
||||
},
|
||||
|
||||
calculateTicks : function () {
|
||||
var options = this.options;
|
||||
|
||||
this.ticks = [];
|
||||
this.minorTicks = [];
|
||||
|
||||
// User Ticks
|
||||
if(options.ticks){
|
||||
this._cleanUserTicks(options.ticks, this.ticks);
|
||||
this._cleanUserTicks(options.minorTicks || [], this.minorTicks);
|
||||
}
|
||||
else {
|
||||
if (options.mode == 'time') {
|
||||
this._calculateTimeTicks();
|
||||
} else if (options.scaling === 'logarithmic') {
|
||||
this._calculateLogTicks();
|
||||
} else {
|
||||
this._calculateTicks();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the range of an axis to apply autoscaling.
|
||||
*/
|
||||
calculateRange: function () {
|
||||
|
||||
if (!this.used) return;
|
||||
|
||||
var axis = this,
|
||||
o = axis.options,
|
||||
min = o.min !== null ? o.min : axis.datamin,
|
||||
max = o.max !== null ? o.max : axis.datamax,
|
||||
margin = o.autoscaleMargin;
|
||||
|
||||
if (o.scaling == 'logarithmic') {
|
||||
if (min <= 0) min = axis.datamin;
|
||||
|
||||
// Let it widen later on
|
||||
if (max <= 0) max = min;
|
||||
}
|
||||
|
||||
if (max == min) {
|
||||
var widen = max ? 0.01 : 1.00;
|
||||
if (o.min === null) min -= widen;
|
||||
if (o.max === null) max += widen;
|
||||
}
|
||||
|
||||
if (o.scaling === 'logarithmic') {
|
||||
if (min < 0) min = max / o.base; // Could be the result of widening
|
||||
|
||||
var maxexp = Math.log(max);
|
||||
if (o.base != Math.E) maxexp /= Math.log(o.base);
|
||||
maxexp = Math.ceil(maxexp);
|
||||
|
||||
var minexp = Math.log(min);
|
||||
if (o.base != Math.E) minexp /= Math.log(o.base);
|
||||
minexp = Math.ceil(minexp);
|
||||
|
||||
axis.tickSize = Flotr.getTickSize(o.noTicks, minexp, maxexp, o.tickDecimals === null ? 0 : o.tickDecimals);
|
||||
|
||||
// Try to determine a suitable amount of miniticks based on the length of a decade
|
||||
if (o.minorTickFreq === null) {
|
||||
if (maxexp - minexp > 10)
|
||||
o.minorTickFreq = 0;
|
||||
else if (maxexp - minexp > 5)
|
||||
o.minorTickFreq = 2;
|
||||
else
|
||||
o.minorTickFreq = 5;
|
||||
}
|
||||
} else {
|
||||
axis.tickSize = Flotr.getTickSize(o.noTicks, min, max, o.tickDecimals);
|
||||
}
|
||||
|
||||
axis.min = min;
|
||||
axis.max = max; //extendRange may use axis.min or axis.max, so it should be set before it is caled
|
||||
|
||||
// Autoscaling. @todo This probably fails with log scale. Find a testcase and fix it
|
||||
if(o.min === null && o.autoscale){
|
||||
axis.min -= axis.tickSize * margin;
|
||||
// Make sure we don't go below zero if all values are positive.
|
||||
if(axis.min < 0 && axis.datamin >= 0) axis.min = 0;
|
||||
axis.min = axis.tickSize * Math.floor(axis.min / axis.tickSize);
|
||||
}
|
||||
|
||||
if(o.max === null && o.autoscale){
|
||||
axis.max += axis.tickSize * margin;
|
||||
if(axis.max > 0 && axis.datamax <= 0 && axis.datamax != axis.datamin) axis.max = 0;
|
||||
axis.max = axis.tickSize * Math.ceil(axis.max / axis.tickSize);
|
||||
}
|
||||
|
||||
if (axis.min == axis.max) axis.max = axis.min + 1;
|
||||
},
|
||||
|
||||
calculateTextDimensions : function (T, options) {
|
||||
|
||||
var maxLabel = '',
|
||||
length,
|
||||
i;
|
||||
|
||||
if (this.options.showLabels) {
|
||||
for (i = 0; i < this.ticks.length; ++i) {
|
||||
length = this.ticks[i].label.length;
|
||||
if (length > maxLabel.length){
|
||||
maxLabel = this.ticks[i].label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.maxLabel = T.dimensions(
|
||||
maxLabel,
|
||||
{size:options.fontSize, angle: Flotr.toRad(this.options.labelsAngle)},
|
||||
'font-size:smaller;',
|
||||
'flotr-grid-label'
|
||||
);
|
||||
|
||||
this.titleSize = T.dimensions(
|
||||
this.options.title,
|
||||
{size:options.fontSize*1.2, angle: Flotr.toRad(this.options.titleAngle)},
|
||||
'font-weight:bold;',
|
||||
'flotr-axis-title'
|
||||
);
|
||||
},
|
||||
|
||||
_cleanUserTicks : function (ticks, axisTicks) {
|
||||
|
||||
var axis = this, options = this.options,
|
||||
v, i, label, tick;
|
||||
|
||||
if(_.isFunction(ticks)) ticks = ticks({min : axis.min, max : axis.max});
|
||||
|
||||
for(i = 0; i < ticks.length; ++i){
|
||||
tick = ticks[i];
|
||||
if(typeof(tick) === 'object'){
|
||||
v = tick[0];
|
||||
label = (tick.length > 1) ? tick[1] : options.tickFormatter(v, {min : axis.min, max : axis.max});
|
||||
} else {
|
||||
v = tick;
|
||||
label = options.tickFormatter(v, {min : this.min, max : this.max});
|
||||
}
|
||||
axisTicks[i] = { v: v, label: label };
|
||||
}
|
||||
},
|
||||
|
||||
_calculateTimeTicks : function () {
|
||||
this.ticks = Flotr.Date.generator(this);
|
||||
},
|
||||
|
||||
_calculateLogTicks : function () {
|
||||
|
||||
var axis = this,
|
||||
o = axis.options,
|
||||
v,
|
||||
decadeStart;
|
||||
|
||||
var max = Math.log(axis.max);
|
||||
if (o.base != Math.E) max /= Math.log(o.base);
|
||||
max = Math.ceil(max);
|
||||
|
||||
var min = Math.log(axis.min);
|
||||
if (o.base != Math.E) min /= Math.log(o.base);
|
||||
min = Math.ceil(min);
|
||||
|
||||
for (i = min; i < max; i += axis.tickSize) {
|
||||
decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);
|
||||
// Next decade begins here:
|
||||
var decadeEnd = decadeStart * ((o.base == Math.E) ? Math.exp(axis.tickSize) : Math.pow(o.base, axis.tickSize));
|
||||
var stepSize = (decadeEnd - decadeStart) / o.minorTickFreq;
|
||||
|
||||
axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min : axis.min, max : axis.max})});
|
||||
for (v = decadeStart + stepSize; v < decadeEnd; v += stepSize)
|
||||
axis.minorTicks.push({v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max})});
|
||||
}
|
||||
|
||||
// Always show the value at the would-be start of next decade (end of this decade)
|
||||
decadeStart = (o.base == Math.E) ? Math.exp(i) : Math.pow(o.base, i);
|
||||
axis.ticks.push({v: decadeStart, label: o.tickFormatter(decadeStart, {min : axis.min, max : axis.max})});
|
||||
},
|
||||
|
||||
_calculateTicks : function () {
|
||||
|
||||
var axis = this,
|
||||
o = axis.options,
|
||||
tickSize = axis.tickSize,
|
||||
min = axis.min,
|
||||
max = axis.max,
|
||||
start = tickSize * Math.ceil(min / tickSize), // Round to nearest multiple of tick size.
|
||||
decimals,
|
||||
minorTickSize,
|
||||
v, v2,
|
||||
i, j;
|
||||
|
||||
if (o.minorTickFreq)
|
||||
minorTickSize = tickSize / o.minorTickFreq;
|
||||
|
||||
// Then store all possible ticks.
|
||||
for (i = 0; (v = v2 = start + i * tickSize) <= max; ++i){
|
||||
|
||||
// Round (this is always needed to fix numerical instability).
|
||||
decimals = o.tickDecimals;
|
||||
if (decimals === null) decimals = 1 - Math.floor(Math.log(tickSize) / Math.LN10);
|
||||
if (decimals < 0) decimals = 0;
|
||||
|
||||
v = v.toFixed(decimals);
|
||||
axis.ticks.push({ v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max}) });
|
||||
|
||||
if (o.minorTickFreq) {
|
||||
for (j = 0; j < o.minorTickFreq && (i * tickSize + j * minorTickSize) < max; ++j) {
|
||||
v = v2 + j * minorTickSize;
|
||||
axis.minorTicks.push({ v: v, label: o.tickFormatter(v, {min : axis.min, max : axis.max}) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_setTranslations : function (logarithmic) {
|
||||
this.d2p = (logarithmic ? d2pLog : d2p);
|
||||
this.p2d = (logarithmic ? p2dLog : p2d);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Static Methods
|
||||
_.extend(Axis, {
|
||||
getAxes : function (options) {
|
||||
return {
|
||||
x: new Axis({options: options.xaxis, n: 1, length: this.plotWidth}),
|
||||
x2: new Axis({options: options.x2axis, n: 2, length: this.plotWidth}),
|
||||
y: new Axis({options: options.yaxis, n: 1, length: this.plotHeight, offset: this.plotHeight, orientation: -1}),
|
||||
y2: new Axis({options: options.y2axis, n: 2, length: this.plotHeight, offset: this.plotHeight, orientation: -1})
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Helper Methods
|
||||
|
||||
function d2p (dataValue) {
|
||||
return this.offset + this.orientation * (dataValue - this.min) * this.scale;
|
||||
}
|
||||
|
||||
function p2d (pointValue) {
|
||||
return (this.offset + this.orientation * pointValue) / this.scale + this.min;
|
||||
}
|
||||
|
||||
function d2pLog (dataValue) {
|
||||
return this.offset + this.orientation * (log(dataValue, this.options.base) - log(this.min, this.options.base)) * this.scale;
|
||||
}
|
||||
|
||||
function p2dLog (pointValue) {
|
||||
return exp((this.offset + this.orientation * pointValue) / this.scale + log(this.min, this.options.base), this.options.base);
|
||||
}
|
||||
|
||||
function log (value, base) {
|
||||
value = Math.log(Math.max(value, Number.MIN_VALUE));
|
||||
if (base !== Math.E)
|
||||
value /= Math.log(base);
|
||||
return value;
|
||||
}
|
||||
|
||||
function exp (value, base) {
|
||||
return (base === Math.E) ? Math.exp(value) : Math.pow(base, value);
|
||||
}
|
||||
|
||||
Flotr.Axis = Axis;
|
||||
|
||||
})();
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* Flotr Color
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
var
|
||||
_ = Flotr._;
|
||||
|
||||
// Constructor
|
||||
function Color (r, g, b, a) {
|
||||
this.rgba = ['r','g','b','a'];
|
||||
var x = 4;
|
||||
while(-1<--x){
|
||||
this[this.rgba[x]] = arguments[x] || ((x==3) ? 1.0 : 0);
|
||||
}
|
||||
this.normalize();
|
||||
}
|
||||
|
||||
// Constants
|
||||
var COLOR_NAMES = {
|
||||
aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],
|
||||
brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],
|
||||
darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],
|
||||
darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],
|
||||
darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],
|
||||
khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],
|
||||
lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],
|
||||
maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],
|
||||
violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]
|
||||
};
|
||||
|
||||
Color.prototype = {
|
||||
scale: function(rf, gf, bf, af){
|
||||
var x = 4;
|
||||
while (-1 < --x) {
|
||||
if (!_.isUndefined(arguments[x])) this[this.rgba[x]] *= arguments[x];
|
||||
}
|
||||
return this.normalize();
|
||||
},
|
||||
alpha: function(alpha) {
|
||||
if (!_.isUndefined(alpha) && !_.isNull(alpha)) {
|
||||
this.a = alpha;
|
||||
}
|
||||
return this.normalize();
|
||||
},
|
||||
clone: function(){
|
||||
return new Color(this.r, this.b, this.g, this.a);
|
||||
},
|
||||
limit: function(val,minVal,maxVal){
|
||||
return Math.max(Math.min(val, maxVal), minVal);
|
||||
},
|
||||
normalize: function(){
|
||||
var limit = this.limit;
|
||||
this.r = limit(parseInt(this.r, 10), 0, 255);
|
||||
this.g = limit(parseInt(this.g, 10), 0, 255);
|
||||
this.b = limit(parseInt(this.b, 10), 0, 255);
|
||||
this.a = limit(this.a, 0, 1);
|
||||
return this;
|
||||
},
|
||||
distance: function(color){
|
||||
if (!color) return;
|
||||
color = new Color.parse(color);
|
||||
var dist = 0, x = 3;
|
||||
while(-1<--x){
|
||||
dist += Math.abs(this[this.rgba[x]] - color[this.rgba[x]]);
|
||||
}
|
||||
return dist;
|
||||
},
|
||||
toString: function(){
|
||||
return (this.a >= 1.0) ? 'rgb('+[this.r,this.g,this.b].join(',')+')' : 'rgba('+[this.r,this.g,this.b,this.a].join(',')+')';
|
||||
},
|
||||
contrast: function () {
|
||||
var
|
||||
test = 1 - ( 0.299 * this.r + 0.587 * this.g + 0.114 * this.b) / 255;
|
||||
return (test < 0.5 ? '#000000' : '#ffffff');
|
||||
}
|
||||
};
|
||||
|
||||
_.extend(Color, {
|
||||
/**
|
||||
* Parses a color string and returns a corresponding Color.
|
||||
* The different tests are in order of probability to improve speed.
|
||||
* @param {String, Color} str - string thats representing a color
|
||||
* @return {Color} returns a Color object or false
|
||||
*/
|
||||
parse: function(color){
|
||||
if (color instanceof Color) return color;
|
||||
|
||||
var result;
|
||||
|
||||
// #a0b1c2
|
||||
if((result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)))
|
||||
return new Color(parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16));
|
||||
|
||||
// rgb(num,num,num)
|
||||
if((result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)))
|
||||
return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10));
|
||||
|
||||
// #fff
|
||||
if((result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)))
|
||||
return new Color(parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16));
|
||||
|
||||
// rgba(num,num,num,num)
|
||||
if((result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color)))
|
||||
return new Color(parseInt(result[1], 10), parseInt(result[2], 10), parseInt(result[3], 10), parseFloat(result[4]));
|
||||
|
||||
// rgb(num%,num%,num%)
|
||||
if((result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)))
|
||||
return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55);
|
||||
|
||||
// rgba(num%,num%,num%,num)
|
||||
if((result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(color)))
|
||||
return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4]));
|
||||
|
||||
// Otherwise, we're most likely dealing with a named color.
|
||||
var name = (color+'').replace(/^\s*([\S\s]*?)\s*$/, '$1').toLowerCase();
|
||||
if(name == 'transparent'){
|
||||
return new Color(255, 255, 255, 0);
|
||||
}
|
||||
return (result = COLOR_NAMES[name]) ? new Color(result[0], result[1], result[2]) : new Color(0, 0, 0, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Process color and options into color style.
|
||||
*/
|
||||
processColor: function(color, options) {
|
||||
|
||||
var opacity = options.opacity;
|
||||
if (!color) return 'rgba(0, 0, 0, 0)';
|
||||
if (color instanceof Color) return color.alpha(opacity).toString();
|
||||
if (_.isString(color)) return Color.parse(color).alpha(opacity).toString();
|
||||
|
||||
var grad = color.colors ? color : {colors: color};
|
||||
|
||||
if (!options.ctx) {
|
||||
if (!_.isArray(grad.colors)) return 'rgba(0, 0, 0, 0)';
|
||||
return Color.parse(_.isArray(grad.colors[0]) ? grad.colors[0][1] : grad.colors[0]).alpha(opacity).toString();
|
||||
}
|
||||
grad = _.extend({start: 'top', end: 'bottom'}, grad);
|
||||
|
||||
if (/top/i.test(grad.start)) options.x1 = 0;
|
||||
if (/left/i.test(grad.start)) options.y1 = 0;
|
||||
if (/bottom/i.test(grad.end)) options.x2 = 0;
|
||||
if (/right/i.test(grad.end)) options.y2 = 0;
|
||||
|
||||
var i, c, stop, gradient = options.ctx.createLinearGradient(options.x1, options.y1, options.x2, options.y2);
|
||||
for (i = 0; i < grad.colors.length; i++) {
|
||||
c = grad.colors[i];
|
||||
if (_.isArray(c)) {
|
||||
stop = c[0];
|
||||
c = c[1];
|
||||
}
|
||||
else stop = i / (grad.colors.length-1);
|
||||
gradient.addColorStop(stop, Color.parse(c).alpha(opacity));
|
||||
}
|
||||
return gradient;
|
||||
}
|
||||
});
|
||||
|
||||
Flotr.Color = Color;
|
||||
|
||||
})();
|
|
@ -0,0 +1,88 @@
|
|||
(function () {
|
||||
|
||||
var _ = Flotr._;
|
||||
|
||||
Flotr.DOM = {
|
||||
addClass: function(element, name){
|
||||
var classList = (element.className ? element.className : '');
|
||||
if (_.include(classList.split(/\s+/g), name)) return;
|
||||
element.className = (classList ? classList + ' ' : '') + name;
|
||||
},
|
||||
/**
|
||||
* Create an element.
|
||||
*/
|
||||
create: function(tag){
|
||||
return document.createElement(tag);
|
||||
},
|
||||
node: function(html) {
|
||||
var div = Flotr.DOM.create('div'), n;
|
||||
div.innerHTML = html;
|
||||
n = div.children[0];
|
||||
div.innerHTML = '';
|
||||
return n;
|
||||
},
|
||||
/**
|
||||
* Remove all children.
|
||||
*/
|
||||
empty: function(element){
|
||||
element.innerHTML = '';
|
||||
/*
|
||||
if (!element) return;
|
||||
_.each(element.childNodes, function (e) {
|
||||
Flotr.DOM.empty(e);
|
||||
element.removeChild(e);
|
||||
});
|
||||
*/
|
||||
},
|
||||
hide: function(element){
|
||||
Flotr.DOM.setStyles(element, {display:'none'});
|
||||
},
|
||||
/**
|
||||
* Insert a child.
|
||||
* @param {Element} element
|
||||
* @param {Element|String} Element or string to be appended.
|
||||
*/
|
||||
insert: function(element, child){
|
||||
if(_.isString(child))
|
||||
element.innerHTML += child;
|
||||
else if (_.isElement(child))
|
||||
element.appendChild(child);
|
||||
},
|
||||
// @TODO find xbrowser implementation
|
||||
opacity: function(element, opacity) {
|
||||
element.style.opacity = opacity;
|
||||
},
|
||||
position: function(element, p){
|
||||
if (!element.offsetParent)
|
||||
return {left: (element.offsetLeft || 0), top: (element.offsetTop || 0)};
|
||||
|
||||
p = this.position(element.offsetParent);
|
||||
p.left += element.offsetLeft;
|
||||
p.top += element.offsetTop;
|
||||
return p;
|
||||
},
|
||||
removeClass: function(element, name) {
|
||||
var classList = (element.className ? element.className : '');
|
||||
element.className = _.filter(classList.split(/\s+/g), function (c) {
|
||||
if (c != name) return true; }
|
||||
).join(' ');
|
||||
},
|
||||
setStyles: function(element, o) {
|
||||
_.each(o, function (value, key) {
|
||||
element.style[key] = value;
|
||||
});
|
||||
},
|
||||
show: function(element){
|
||||
Flotr.DOM.setStyles(element, {display:''});
|
||||
},
|
||||
/**
|
||||
* Return element size.
|
||||
*/
|
||||
size: function(element){
|
||||
return {
|
||||
height : element.offsetHeight,
|
||||
width : element.offsetWidth };
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
|
@ -0,0 +1,207 @@
|
|||
/**
|
||||
* Flotr Date
|
||||
*/
|
||||
Flotr.Date = {
|
||||
|
||||
set : function (date, name, mode, value) {
|
||||
mode = mode || 'UTC';
|
||||
name = 'set' + (mode === 'UTC' ? 'UTC' : '') + name;
|
||||
date[name](value);
|
||||
},
|
||||
|
||||
get : function (date, name, mode) {
|
||||
mode = mode || 'UTC';
|
||||
name = 'get' + (mode === 'UTC' ? 'UTC' : '') + name;
|
||||
return date[name]();
|
||||
},
|
||||
|
||||
format: function(d, format, mode) {
|
||||
if (!d) return;
|
||||
|
||||
// We should maybe use an "official" date format spec, like PHP date() or ColdFusion
|
||||
// http://fr.php.net/manual/en/function.date.php
|
||||
// http://livedocs.adobe.com/coldfusion/8/htmldocs/help.html?content=functions_c-d_29.html
|
||||
var
|
||||
get = this.get,
|
||||
tokens = {
|
||||
h: get(d, 'Hours', mode).toString(),
|
||||
H: leftPad(get(d, 'Hours', mode)),
|
||||
M: leftPad(get(d, 'Minutes', mode)),
|
||||
S: leftPad(get(d, 'Seconds', mode)),
|
||||
s: get(d, 'Milliseconds', mode),
|
||||
d: get(d, 'Date', mode).toString(),
|
||||
m: (get(d, 'Month') + 1).toString(),
|
||||
y: get(d, 'FullYear').toString(),
|
||||
b: Flotr.Date.monthNames[get(d, 'Month', mode)]
|
||||
};
|
||||
|
||||
function leftPad(n){
|
||||
n += '';
|
||||
return n.length == 1 ? "0" + n : n;
|
||||
}
|
||||
|
||||
var r = [], c,
|
||||
escape = false;
|
||||
|
||||
for (var i = 0; i < format.length; ++i) {
|
||||
c = format.charAt(i);
|
||||
|
||||
if (escape) {
|
||||
r.push(tokens[c] || c);
|
||||
escape = false;
|
||||
}
|
||||
else if (c == "%")
|
||||
escape = true;
|
||||
else
|
||||
r.push(c);
|
||||
}
|
||||
return r.join('');
|
||||
},
|
||||
getFormat: function(time, span) {
|
||||
var tu = Flotr.Date.timeUnits;
|
||||
if (time < tu.second) return "%h:%M:%S.%s";
|
||||
else if (time < tu.minute) return "%h:%M:%S";
|
||||
else if (time < tu.day) return (span < 2 * tu.day) ? "%h:%M" : "%b %d %h:%M";
|
||||
else if (time < tu.month) return "%b %d";
|
||||
else if (time < tu.year) return (span < tu.year) ? "%b" : "%b %y";
|
||||
else return "%y";
|
||||
},
|
||||
formatter: function (v, axis) {
|
||||
var
|
||||
options = axis.options,
|
||||
scale = Flotr.Date.timeUnits[options.timeUnit],
|
||||
d = new Date(v * scale);
|
||||
|
||||
// first check global format
|
||||
if (axis.options.timeFormat)
|
||||
return Flotr.Date.format(d, options.timeFormat, options.timeMode);
|
||||
|
||||
var span = (axis.max - axis.min) * scale,
|
||||
t = axis.tickSize * Flotr.Date.timeUnits[axis.tickUnit];
|
||||
|
||||
return Flotr.Date.format(d, Flotr.Date.getFormat(t, span), options.timeMode);
|
||||
},
|
||||
generator: function(axis) {
|
||||
|
||||
var
|
||||
set = this.set,
|
||||
get = this.get,
|
||||
timeUnits = this.timeUnits,
|
||||
spec = this.spec,
|
||||
options = axis.options,
|
||||
mode = options.timeMode,
|
||||
scale = timeUnits[options.timeUnit],
|
||||
min = axis.min * scale,
|
||||
max = axis.max * scale,
|
||||
delta = (max - min) / options.noTicks,
|
||||
ticks = [],
|
||||
tickSize = axis.tickSize,
|
||||
tickUnit,
|
||||
formatter, i;
|
||||
|
||||
// Use custom formatter or time tick formatter
|
||||
formatter = (options.tickFormatter === Flotr.defaultTickFormatter ?
|
||||
this.formatter : options.tickFormatter
|
||||
);
|
||||
|
||||
for (i = 0; i < spec.length - 1; ++i) {
|
||||
var d = spec[i][0] * timeUnits[spec[i][1]];
|
||||
if (delta < (d + spec[i+1][0] * timeUnits[spec[i+1][1]]) / 2 && d >= tickSize)
|
||||
break;
|
||||
}
|
||||
tickSize = spec[i][0];
|
||||
tickUnit = spec[i][1];
|
||||
|
||||
// special-case the possibility of several years
|
||||
if (tickUnit == "year") {
|
||||
tickSize = Flotr.getTickSize(options.noTicks*timeUnits.year, min, max, 0);
|
||||
|
||||
// Fix for 0.5 year case
|
||||
if (tickSize == 0.5) {
|
||||
tickUnit = "month";
|
||||
tickSize = 6;
|
||||
}
|
||||
}
|
||||
|
||||
axis.tickUnit = tickUnit;
|
||||
axis.tickSize = tickSize;
|
||||
|
||||
var
|
||||
d = new Date(min);
|
||||
|
||||
var step = tickSize * timeUnits[tickUnit];
|
||||
|
||||
function setTick (name) {
|
||||
set(d, name, mode, Flotr.floorInBase(
|
||||
get(d, name, mode), tickSize
|
||||
));
|
||||
}
|
||||
|
||||
switch (tickUnit) {
|
||||
case "millisecond": setTick('Milliseconds'); break;
|
||||
case "second": setTick('Seconds'); break;
|
||||
case "minute": setTick('Minutes'); break;
|
||||
case "hour": setTick('Hours'); break;
|
||||
case "month": setTick('Month'); break;
|
||||
case "year": setTick('FullYear'); break;
|
||||
}
|
||||
|
||||
// reset smaller components
|
||||
if (step >= timeUnits.second) set(d, 'Milliseconds', mode, 0);
|
||||
if (step >= timeUnits.minute) set(d, 'Seconds', mode, 0);
|
||||
if (step >= timeUnits.hour) set(d, 'Minutes', mode, 0);
|
||||
if (step >= timeUnits.day) set(d, 'Hours', mode, 0);
|
||||
if (step >= timeUnits.day * 4) set(d, 'Date', mode, 1);
|
||||
if (step >= timeUnits.year) set(d, 'Month', mode, 0);
|
||||
|
||||
var carry = 0, v = NaN, prev;
|
||||
do {
|
||||
prev = v;
|
||||
v = d.getTime();
|
||||
ticks.push({ v: v / scale, label: formatter(v / scale, axis) });
|
||||
if (tickUnit == "month") {
|
||||
if (tickSize < 1) {
|
||||
/* a bit complicated - we'll divide the month up but we need to take care of fractions
|
||||
so we don't end up in the middle of a day */
|
||||
set(d, 'Date', mode, 1);
|
||||
var start = d.getTime();
|
||||
set(d, 'Month', mode, get(d, 'Month', mode) + 1)
|
||||
var end = d.getTime();
|
||||
d.setTime(v + carry * timeUnits.hour + (end - start) * tickSize);
|
||||
carry = get(d, 'Hours', mode)
|
||||
set(d, 'Hours', mode, 0);
|
||||
}
|
||||
else
|
||||
set(d, 'Month', mode, get(d, 'Month', mode) + tickSize);
|
||||
}
|
||||
else if (tickUnit == "year") {
|
||||
set(d, 'FullYear', mode, get(d, 'FullYear', mode) + tickSize);
|
||||
}
|
||||
else
|
||||
d.setTime(v + step);
|
||||
|
||||
} while (v < max && v != prev);
|
||||
|
||||
return ticks;
|
||||
},
|
||||
timeUnits: {
|
||||
millisecond: 1,
|
||||
second: 1000,
|
||||
minute: 1000 * 60,
|
||||
hour: 1000 * 60 * 60,
|
||||
day: 1000 * 60 * 60 * 24,
|
||||
month: 1000 * 60 * 60 * 24 * 30,
|
||||
year: 1000 * 60 * 60 * 24 * 365.2425
|
||||
},
|
||||
// the allowed tick sizes, after 1 year we use an integer algorithm
|
||||
spec: [
|
||||
[1, "millisecond"], [20, "millisecond"], [50, "millisecond"], [100, "millisecond"], [200, "millisecond"], [500, "millisecond"],
|
||||
[1, "second"], [2, "second"], [5, "second"], [10, "second"], [30, "second"],
|
||||
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"], [30, "minute"],
|
||||
[1, "hour"], [2, "hour"], [4, "hour"], [8, "hour"], [12, "hour"],
|
||||
[1, "day"], [2, "day"], [3, "day"],
|
||||
[0.25, "month"], [0.5, "month"], [1, "month"], [2, "month"], [3, "month"], [6, "month"],
|
||||
[1, "year"]
|
||||
],
|
||||
monthNames: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* Flotr Defaults
|
||||
*/
|
||||
Flotr.defaultOptions = {
|
||||
colors: ['#00A8F0', '#C0D800', '#CB4B4B', '#4DA74D', '#9440ED'], //=> The default colorscheme. When there are > 5 series, additional colors are generated.
|
||||
ieBackgroundColor: '#FFFFFF', // Background color for excanvas clipping
|
||||
title: null, // => The graph's title
|
||||
subtitle: null, // => The graph's subtitle
|
||||
shadowSize: 4, // => size of the 'fake' shadow
|
||||
defaultType: null, // => default series type
|
||||
HtmlText: true, // => wether to draw the text using HTML or on the canvas
|
||||
fontColor: '#545454', // => default font color
|
||||
fontSize: 7.5, // => canvas' text font size
|
||||
resolution: 1, // => resolution of the graph, to have printer-friendly graphs !
|
||||
parseFloat: true, // => whether to preprocess data for floats (ie. if input is string)
|
||||
xaxis: {
|
||||
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3]
|
||||
minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3]
|
||||
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise
|
||||
showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide
|
||||
labelsAngle: 0, // => labels' angle, in degrees
|
||||
title: null, // => axis title
|
||||
titleAngle: 0, // => axis title's angle, in degrees
|
||||
noTicks: 5, // => number of ticks for automagically generated ticks
|
||||
minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks
|
||||
tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string
|
||||
tickDecimals: null, // => no. of decimals, null means auto
|
||||
min: null, // => min. value to show, null means set automatically
|
||||
max: null, // => max. value to show, null means set automatically
|
||||
autoscale: false, // => Turns autoscaling on with true
|
||||
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max
|
||||
color: null, // => color of the ticks
|
||||
mode: 'normal', // => can be 'time' or 'normal'
|
||||
timeFormat: null,
|
||||
timeMode:'UTC', // => For UTC time ('local' for local time).
|
||||
timeUnit:'millisecond',// => Unit for time (millisecond, second, minute, hour, day, month, year)
|
||||
scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic'
|
||||
base: Math.E,
|
||||
titleAlign: 'center',
|
||||
margin: true // => Turn off margins with false
|
||||
},
|
||||
x2axis: {},
|
||||
yaxis: {
|
||||
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3]
|
||||
minorTicks: null, // => format: either [1, 3] or [[1, 'a'], 3]
|
||||
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise
|
||||
showMinorLabels: false,// => true to show the axis minor ticks labels, false to hide
|
||||
labelsAngle: 0, // => labels' angle, in degrees
|
||||
title: null, // => axis title
|
||||
titleAngle: 90, // => axis title's angle, in degrees
|
||||
noTicks: 5, // => number of ticks for automagically generated ticks
|
||||
minorTickFreq: null, // => number of minor ticks between major ticks for autogenerated ticks
|
||||
tickFormatter: Flotr.defaultTickFormatter, // => fn: number, Object -> string
|
||||
tickDecimals: null, // => no. of decimals, null means auto
|
||||
min: null, // => min. value to show, null means set automatically
|
||||
max: null, // => max. value to show, null means set automatically
|
||||
autoscale: false, // => Turns autoscaling on with true
|
||||
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max
|
||||
color: null, // => The color of the ticks
|
||||
scaling: 'linear', // => Scaling, can be 'linear' or 'logarithmic'
|
||||
base: Math.E,
|
||||
titleAlign: 'center',
|
||||
margin: true // => Turn off margins with false
|
||||
},
|
||||
y2axis: {
|
||||
titleAngle: 270
|
||||
},
|
||||
grid: {
|
||||
color: '#545454', // => primary color used for outline and labels
|
||||
backgroundColor: null, // => null for transparent, else color
|
||||
backgroundImage: null, // => background image. String or object with src, left and top
|
||||
watermarkAlpha: 0.4, // =>
|
||||
tickColor: '#DDDDDD', // => color used for the ticks
|
||||
labelMargin: 3, // => margin in pixels
|
||||
verticalLines: true, // => whether to show gridlines in vertical direction
|
||||
minorVerticalLines: null, // => whether to show gridlines for minor ticks in vertical dir.
|
||||
horizontalLines: true, // => whether to show gridlines in horizontal direction
|
||||
minorHorizontalLines: null, // => whether to show gridlines for minor ticks in horizontal dir.
|
||||
outlineWidth: 1, // => width of the grid outline/border in pixels
|
||||
outline : 'nsew', // => walls of the outline to display
|
||||
circular: false // => if set to true, the grid will be circular, must be used when radars are drawn
|
||||
},
|
||||
mouse: {
|
||||
track: false, // => true to track the mouse, no tracking otherwise
|
||||
trackAll: false,
|
||||
position: 'se', // => position of the value box (default south-east)
|
||||
relative: false, // => next to the mouse cursor
|
||||
trackFormatter: Flotr.defaultTrackFormatter, // => formats the values in the value box
|
||||
margin: 5, // => margin in pixels of the valuebox
|
||||
lineColor: '#FF3F19', // => line color of points that are drawn when mouse comes near a value of a series
|
||||
trackDecimals: 1, // => decimals for the track values
|
||||
sensibility: 2, // => the lower this number, the more precise you have to aim to show a value
|
||||
trackY: true, // => whether or not to track the mouse in the y axis
|
||||
radius: 3, // => radius of the track point
|
||||
fillColor: null, // => color to fill our select bar with only applies to bar and similar graphs (only bars for now)
|
||||
fillOpacity: 0.4 // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
}
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Flotr Event Adapter
|
||||
*/
|
||||
(function () {
|
||||
var
|
||||
F = Flotr,
|
||||
bean = F.bean;
|
||||
F.EventAdapter = {
|
||||
observe: function(object, name, callback) {
|
||||
bean.add(object, name, callback);
|
||||
return this;
|
||||
},
|
||||
fire: function(object, name, args) {
|
||||
bean.fire(object, name, args);
|
||||
if (typeof(Prototype) != 'undefined')
|
||||
Event.fire(object, name, args);
|
||||
// @TODO Someone who uses mootools, add mootools adapter for existing applciations.
|
||||
return this;
|
||||
},
|
||||
stopObserving: function(object, name, callback) {
|
||||
bean.remove(object, name, callback);
|
||||
return this;
|
||||
},
|
||||
eventPointer: function(e) {
|
||||
if (!F._.isUndefined(e.touches) && e.touches.length > 0) {
|
||||
return {
|
||||
x : e.touches[0].pageX,
|
||||
y : e.touches[0].pageY
|
||||
};
|
||||
} else if (!F._.isUndefined(e.changedTouches) && e.changedTouches.length > 0) {
|
||||
return {
|
||||
x : e.changedTouches[0].pageX,
|
||||
y : e.changedTouches[0].pageY
|
||||
};
|
||||
} else if (e.pageX || e.pageY) {
|
||||
return {
|
||||
x : e.pageX,
|
||||
y : e.pageY
|
||||
};
|
||||
} else if (e.clientX || e.clientY) {
|
||||
var
|
||||
d = document,
|
||||
b = d.body,
|
||||
de = d.documentElement;
|
||||
return {
|
||||
x: e.clientX + b.scrollLeft + de.scrollLeft,
|
||||
y: e.clientY + b.scrollTop + de.scrollTop
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,250 @@
|
|||
/**
|
||||
* Flotr2 (c) 2012 Carl Sutherland
|
||||
* MIT License
|
||||
* Special thanks to:
|
||||
* Flotr: http://code.google.com/p/flotr/ (fork)
|
||||
* Flot: https://github.com/flot/flot (original fork)
|
||||
*/
|
||||
(function () {
|
||||
|
||||
var
|
||||
global = this,
|
||||
previousFlotr = this.Flotr,
|
||||
Flotr;
|
||||
|
||||
Flotr = {
|
||||
_: _,
|
||||
bean: bean,
|
||||
isIphone: /iphone/i.test(navigator.userAgent),
|
||||
isIE: (navigator.appVersion.indexOf("MSIE") != -1 ? parseFloat(navigator.appVersion.split("MSIE")[1]) : false),
|
||||
|
||||
/**
|
||||
* An object of the registered graph types. Use Flotr.addType(type, object)
|
||||
* to add your own type.
|
||||
*/
|
||||
graphTypes: {},
|
||||
|
||||
/**
|
||||
* The list of the registered plugins
|
||||
*/
|
||||
plugins: {},
|
||||
|
||||
/**
|
||||
* Can be used to add your own chart type.
|
||||
* @param {String} name - Type of chart, like 'pies', 'bars' etc.
|
||||
* @param {String} graphType - The object containing the basic drawing functions (draw, etc)
|
||||
*/
|
||||
addType: function(name, graphType){
|
||||
Flotr.graphTypes[name] = graphType;
|
||||
Flotr.defaultOptions[name] = graphType.options || {};
|
||||
Flotr.defaultOptions.defaultType = Flotr.defaultOptions.defaultType || name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Can be used to add a plugin
|
||||
* @param {String} name - The name of the plugin
|
||||
* @param {String} plugin - The object containing the plugin's data (callbacks, options, function1, function2, ...)
|
||||
*/
|
||||
addPlugin: function(name, plugin){
|
||||
Flotr.plugins[name] = plugin;
|
||||
Flotr.defaultOptions[name] = plugin.options || {};
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws the graph. This function is here for backwards compatibility with Flotr version 0.1.0alpha.
|
||||
* You could also draw graphs by directly calling Flotr.Graph(element, data, options).
|
||||
* @param {Element} el - element to insert the graph into
|
||||
* @param {Object} data - an array or object of dataseries
|
||||
* @param {Object} options - an object containing options
|
||||
* @param {Class} _GraphKlass_ - (optional) Class to pass the arguments to, defaults to Flotr.Graph
|
||||
* @return {Object} returns a new graph object and of course draws the graph.
|
||||
*/
|
||||
draw: function(el, data, options, GraphKlass){
|
||||
GraphKlass = GraphKlass || Flotr.Graph;
|
||||
return new GraphKlass(el, data, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively merges two objects.
|
||||
* @param {Object} src - source object (likely the object with the least properties)
|
||||
* @param {Object} dest - destination object (optional, object with the most properties)
|
||||
* @return {Object} recursively merged Object
|
||||
* @TODO See if we can't remove this.
|
||||
*/
|
||||
merge: function(src, dest){
|
||||
var i, v, result = dest || {};
|
||||
|
||||
for (i in src) {
|
||||
v = src[i];
|
||||
if (v && typeof(v) === 'object') {
|
||||
if (v.constructor === Array) {
|
||||
result[i] = this._.clone(v);
|
||||
} else if (v.constructor !== RegExp && !this._.isElement(v)) {
|
||||
result[i] = Flotr.merge(v, (dest ? dest[i] : undefined));
|
||||
} else {
|
||||
result[i] = v;
|
||||
}
|
||||
} else {
|
||||
result[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively clones an object.
|
||||
* @param {Object} object - The object to clone
|
||||
* @return {Object} the clone
|
||||
* @TODO See if we can't remove this.
|
||||
*/
|
||||
clone: function(object){
|
||||
return Flotr.merge(object, {});
|
||||
},
|
||||
|
||||
/**
|
||||
* Function calculates the ticksize and returns it.
|
||||
* @param {Integer} noTicks - number of ticks
|
||||
* @param {Integer} min - lower bound integer value for the current axis
|
||||
* @param {Integer} max - upper bound integer value for the current axis
|
||||
* @param {Integer} decimals - number of decimals for the ticks
|
||||
* @return {Integer} returns the ticksize in pixels
|
||||
*/
|
||||
getTickSize: function(noTicks, min, max, decimals){
|
||||
var delta = (max - min) / noTicks,
|
||||
magn = Flotr.getMagnitude(delta),
|
||||
tickSize = 10,
|
||||
norm = delta / magn; // Norm is between 1.0 and 10.0.
|
||||
|
||||
if(norm < 1.5) tickSize = 1;
|
||||
else if(norm < 2.25) tickSize = 2;
|
||||
else if(norm < 3) tickSize = ((decimals === 0) ? 2 : 2.5);
|
||||
else if(norm < 7.5) tickSize = 5;
|
||||
|
||||
return tickSize * magn;
|
||||
},
|
||||
|
||||
/**
|
||||
* Default tick formatter.
|
||||
* @param {String, Integer} val - tick value integer
|
||||
* @param {Object} axisOpts - the axis' options
|
||||
* @return {String} formatted tick string
|
||||
*/
|
||||
defaultTickFormatter: function(val, axisOpts){
|
||||
return val+'';
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats the mouse tracker values.
|
||||
* @param {Object} obj - Track value Object {x:..,y:..}
|
||||
* @return {String} Formatted track string
|
||||
*/
|
||||
defaultTrackFormatter: function(obj){
|
||||
return '('+obj.x+', '+obj.y+')';
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function to convert file size values in bytes to kB, MB, ...
|
||||
* @param value {Number} - The value to convert
|
||||
* @param precision {Number} - The number of digits after the comma (default: 2)
|
||||
* @param base {Number} - The base (default: 1000)
|
||||
*/
|
||||
engineeringNotation: function(value, precision, base){
|
||||
var sizes = ['Y','Z','E','P','T','G','M','k',''],
|
||||
fractionSizes = ['y','z','a','f','p','n','µ','m',''],
|
||||
total = sizes.length;
|
||||
|
||||
base = base || 1000;
|
||||
precision = Math.pow(10, precision || 2);
|
||||
|
||||
if (value === 0) return 0;
|
||||
|
||||
if (value > 1) {
|
||||
while (total-- && (value >= base)) value /= base;
|
||||
}
|
||||
else {
|
||||
sizes = fractionSizes;
|
||||
total = sizes.length;
|
||||
while (total-- && (value < 1)) value *= base;
|
||||
}
|
||||
|
||||
return (Math.round(value * precision) / precision) + sizes[total];
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the magnitude of the input value.
|
||||
* @param {Integer, Float} x - integer or float value
|
||||
* @return {Integer, Float} returns the magnitude of the input value
|
||||
*/
|
||||
getMagnitude: function(x){
|
||||
return Math.pow(10, Math.floor(Math.log(x) / Math.LN10));
|
||||
},
|
||||
toPixel: function(val){
|
||||
return Math.floor(val)+0.5;//((val-Math.round(val) < 0.4) ? (Math.floor(val)-0.5) : val);
|
||||
},
|
||||
toRad: function(angle){
|
||||
return -angle * (Math.PI/180);
|
||||
},
|
||||
floorInBase: function(n, base) {
|
||||
return base * Math.floor(n / base);
|
||||
},
|
||||
drawText: function(ctx, text, x, y, style) {
|
||||
if (!ctx.fillText) {
|
||||
ctx.drawText(text, x, y, style);
|
||||
return;
|
||||
}
|
||||
|
||||
style = this._.extend({
|
||||
size: Flotr.defaultOptions.fontSize,
|
||||
color: '#000000',
|
||||
textAlign: 'left',
|
||||
textBaseline: 'bottom',
|
||||
weight: 1,
|
||||
angle: 0
|
||||
}, style);
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(style.angle);
|
||||
ctx.fillStyle = style.color;
|
||||
ctx.font = (style.weight > 1 ? "bold " : "") + (style.size*1.3) + "px sans-serif";
|
||||
ctx.textAlign = style.textAlign;
|
||||
ctx.textBaseline = style.textBaseline;
|
||||
ctx.fillText(text, 0, 0);
|
||||
ctx.restore();
|
||||
},
|
||||
getBestTextAlign: function(angle, style) {
|
||||
style = style || {textAlign: 'center', textBaseline: 'middle'};
|
||||
angle += Flotr.getTextAngleFromAlign(style);
|
||||
|
||||
if (Math.abs(Math.cos(angle)) > 10e-3)
|
||||
style.textAlign = (Math.cos(angle) > 0 ? 'right' : 'left');
|
||||
|
||||
if (Math.abs(Math.sin(angle)) > 10e-3)
|
||||
style.textBaseline = (Math.sin(angle) > 0 ? 'top' : 'bottom');
|
||||
|
||||
return style;
|
||||
},
|
||||
alignTable: {
|
||||
'right middle' : 0,
|
||||
'right top' : Math.PI/4,
|
||||
'center top' : Math.PI/2,
|
||||
'left top' : 3*(Math.PI/4),
|
||||
'left middle' : Math.PI,
|
||||
'left bottom' : -3*(Math.PI/4),
|
||||
'center bottom': -Math.PI/2,
|
||||
'right bottom' : -Math.PI/4,
|
||||
'center middle': 0
|
||||
},
|
||||
getTextAngleFromAlign: function(style) {
|
||||
return Flotr.alignTable[style.textAlign+' '+style.textBaseline] || 0;
|
||||
},
|
||||
noConflict : function () {
|
||||
global.Flotr = previousFlotr;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
global.Flotr = Flotr;
|
||||
|
||||
})();
|
|
@ -0,0 +1,745 @@
|
|||
/**
|
||||
* Flotr Graph class that plots a graph on creation.
|
||||
*/
|
||||
(function () {
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
E = Flotr.EventAdapter,
|
||||
_ = Flotr._,
|
||||
flotr = Flotr;
|
||||
/**
|
||||
* Flotr Graph constructor.
|
||||
* @param {Element} el - element to insert the graph into
|
||||
* @param {Object} data - an array or object of dataseries
|
||||
* @param {Object} options - an object containing options
|
||||
*/
|
||||
Graph = function(el, data, options){
|
||||
// Let's see if we can get away with out this [JS]
|
||||
// try {
|
||||
this._setEl(el);
|
||||
this._initMembers();
|
||||
this._initPlugins();
|
||||
|
||||
E.fire(this.el, 'flotr:beforeinit', [this]);
|
||||
|
||||
this.data = data;
|
||||
this.series = flotr.Series.getSeries(data);
|
||||
this._initOptions(options);
|
||||
this._initGraphTypes();
|
||||
this._initCanvas();
|
||||
this._text = new flotr.Text({
|
||||
element : this.el,
|
||||
ctx : this.ctx,
|
||||
html : this.options.HtmlText,
|
||||
textEnabled : this.textEnabled
|
||||
});
|
||||
E.fire(this.el, 'flotr:afterconstruct', [this]);
|
||||
this._initEvents();
|
||||
|
||||
this.findDataRanges();
|
||||
this.calculateSpacing();
|
||||
|
||||
this.draw(_.bind(function() {
|
||||
E.fire(this.el, 'flotr:afterinit', [this]);
|
||||
}, this));
|
||||
/*
|
||||
try {
|
||||
} catch (e) {
|
||||
try {
|
||||
console.error(e);
|
||||
} catch (e2) {}
|
||||
}*/
|
||||
};
|
||||
|
||||
function observe (object, name, callback) {
|
||||
E.observe.apply(this, arguments);
|
||||
this._handles.push(arguments);
|
||||
return this;
|
||||
}
|
||||
|
||||
Graph.prototype = {
|
||||
|
||||
destroy: function () {
|
||||
E.fire(this.el, 'flotr:destroy');
|
||||
_.each(this._handles, function (handle) {
|
||||
E.stopObserving.apply(this, handle);
|
||||
});
|
||||
this._handles = [];
|
||||
this.el.graph = null;
|
||||
},
|
||||
|
||||
observe : observe,
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
_observe : observe,
|
||||
|
||||
processColor: function(color, options){
|
||||
var o = { x1: 0, y1: 0, x2: this.plotWidth, y2: this.plotHeight, opacity: 1, ctx: this.ctx };
|
||||
_.extend(o, options);
|
||||
return flotr.Color.processColor(color, o);
|
||||
},
|
||||
/**
|
||||
* Function determines the min and max values for the xaxis and yaxis.
|
||||
*
|
||||
* TODO logarithmic range validation (consideration of 0)
|
||||
*/
|
||||
findDataRanges: function(){
|
||||
var a = this.axes,
|
||||
xaxis, yaxis, range;
|
||||
|
||||
_.each(this.series, function (series) {
|
||||
range = series.getRange();
|
||||
if (range) {
|
||||
xaxis = series.xaxis;
|
||||
yaxis = series.yaxis;
|
||||
xaxis.datamin = Math.min(range.xmin, xaxis.datamin);
|
||||
xaxis.datamax = Math.max(range.xmax, xaxis.datamax);
|
||||
yaxis.datamin = Math.min(range.ymin, yaxis.datamin);
|
||||
yaxis.datamax = Math.max(range.ymax, yaxis.datamax);
|
||||
xaxis.used = (xaxis.used || range.xused);
|
||||
yaxis.used = (yaxis.used || range.yused);
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Check for empty data, no data case (none used)
|
||||
if (!a.x.used && !a.x2.used) a.x.used = true;
|
||||
if (!a.y.used && !a.y2.used) a.y.used = true;
|
||||
|
||||
_.each(a, function (axis) {
|
||||
axis.calculateRange();
|
||||
});
|
||||
|
||||
var
|
||||
types = _.keys(flotr.graphTypes),
|
||||
drawn = false;
|
||||
|
||||
_.each(this.series, function (series) {
|
||||
if (series.hide) return;
|
||||
_.each(types, function (type) {
|
||||
if (series[type] && series[type].show) {
|
||||
this.extendRange(type, series);
|
||||
drawn = true;
|
||||
}
|
||||
}, this);
|
||||
if (!drawn) {
|
||||
this.extendRange(this.options.defaultType, series);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
extendRange : function (type, series) {
|
||||
if (this[type].extendRange) this[type].extendRange(series, series.data, series[type], this[type]);
|
||||
if (this[type].extendYRange) this[type].extendYRange(series.yaxis, series.data, series[type], this[type]);
|
||||
if (this[type].extendXRange) this[type].extendXRange(series.xaxis, series.data, series[type], this[type]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates axis label sizes.
|
||||
*/
|
||||
calculateSpacing: function(){
|
||||
|
||||
var a = this.axes,
|
||||
options = this.options,
|
||||
series = this.series,
|
||||
margin = options.grid.labelMargin,
|
||||
T = this._text,
|
||||
x = a.x,
|
||||
x2 = a.x2,
|
||||
y = a.y,
|
||||
y2 = a.y2,
|
||||
maxOutset = options.grid.outlineWidth,
|
||||
i, j, l, dim;
|
||||
|
||||
// TODO post refactor, fix this
|
||||
_.each(a, function (axis) {
|
||||
axis.calculateTicks();
|
||||
axis.calculateTextDimensions(T, options);
|
||||
});
|
||||
|
||||
// Title height
|
||||
dim = T.dimensions(
|
||||
options.title,
|
||||
{size: options.fontSize*1.5},
|
||||
'font-size:1em;font-weight:bold;',
|
||||
'flotr-title'
|
||||
);
|
||||
this.titleHeight = dim.height;
|
||||
|
||||
// Subtitle height
|
||||
dim = T.dimensions(
|
||||
options.subtitle,
|
||||
{size: options.fontSize},
|
||||
'font-size:smaller;',
|
||||
'flotr-subtitle'
|
||||
);
|
||||
this.subtitleHeight = dim.height;
|
||||
|
||||
for(j = 0; j < options.length; ++j){
|
||||
if (series[j].points.show){
|
||||
maxOutset = Math.max(maxOutset, series[j].points.radius + series[j].points.lineWidth/2);
|
||||
}
|
||||
}
|
||||
|
||||
var p = this.plotOffset;
|
||||
if (x.options.margin === false) {
|
||||
p.bottom = 0;
|
||||
p.top = 0;
|
||||
} else {
|
||||
p.bottom += (options.grid.circular ? 0 : (x.used && x.options.showLabels ? (x.maxLabel.height + margin) : 0)) +
|
||||
(x.used && x.options.title ? (x.titleSize.height + margin) : 0) + maxOutset;
|
||||
|
||||
p.top += (options.grid.circular ? 0 : (x2.used && x2.options.showLabels ? (x2.maxLabel.height + margin) : 0)) +
|
||||
(x2.used && x2.options.title ? (x2.titleSize.height + margin) : 0) + this.subtitleHeight + this.titleHeight + maxOutset;
|
||||
}
|
||||
if (y.options.margin === false) {
|
||||
p.left = 0;
|
||||
p.right = 0;
|
||||
} else {
|
||||
p.left += (options.grid.circular ? 0 : (y.used && y.options.showLabels ? (y.maxLabel.width + margin) : 0)) +
|
||||
(y.used && y.options.title ? (y.titleSize.width + margin) : 0) + maxOutset;
|
||||
|
||||
p.right += (options.grid.circular ? 0 : (y2.used && y2.options.showLabels ? (y2.maxLabel.width + margin) : 0)) +
|
||||
(y2.used && y2.options.title ? (y2.titleSize.width + margin) : 0) + maxOutset;
|
||||
}
|
||||
|
||||
p.top = Math.floor(p.top); // In order the outline not to be blured
|
||||
|
||||
this.plotWidth = this.canvasWidth - p.left - p.right;
|
||||
this.plotHeight = this.canvasHeight - p.bottom - p.top;
|
||||
|
||||
// TODO post refactor, fix this
|
||||
x.length = x2.length = this.plotWidth;
|
||||
y.length = y2.length = this.plotHeight;
|
||||
y.offset = y2.offset = this.plotHeight;
|
||||
x.setScale();
|
||||
x2.setScale();
|
||||
y.setScale();
|
||||
y2.setScale();
|
||||
},
|
||||
/**
|
||||
* Draws grid, labels, series and outline.
|
||||
*/
|
||||
draw: function(after) {
|
||||
|
||||
var
|
||||
context = this.ctx,
|
||||
i;
|
||||
|
||||
E.fire(this.el, 'flotr:beforedraw', [this.series, this]);
|
||||
|
||||
if (this.series.length) {
|
||||
|
||||
context.save();
|
||||
context.translate(this.plotOffset.left, this.plotOffset.top);
|
||||
|
||||
for (i = 0; i < this.series.length; i++) {
|
||||
if (!this.series[i].hide) this.drawSeries(this.series[i]);
|
||||
}
|
||||
|
||||
context.restore();
|
||||
this.clip();
|
||||
}
|
||||
|
||||
E.fire(this.el, 'flotr:afterdraw', [this.series, this]);
|
||||
if (after) after();
|
||||
},
|
||||
/**
|
||||
* Actually draws the graph.
|
||||
* @param {Object} series - series to draw
|
||||
*/
|
||||
drawSeries: function(series){
|
||||
|
||||
function drawChart (series, typeKey) {
|
||||
var options = this.getOptions(series, typeKey);
|
||||
this[typeKey].draw(options);
|
||||
}
|
||||
|
||||
var drawn = false;
|
||||
series = series || this.series;
|
||||
|
||||
_.each(flotr.graphTypes, function (type, typeKey) {
|
||||
if (series[typeKey] && series[typeKey].show && this[typeKey]) {
|
||||
drawn = true;
|
||||
drawChart.call(this, series, typeKey);
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!drawn) drawChart.call(this, series, this.options.defaultType);
|
||||
},
|
||||
|
||||
getOptions : function (series, typeKey) {
|
||||
var
|
||||
type = series[typeKey],
|
||||
graphType = this[typeKey],
|
||||
options = {
|
||||
context : this.ctx,
|
||||
width : this.plotWidth,
|
||||
height : this.plotHeight,
|
||||
fontSize : this.options.fontSize,
|
||||
fontColor : this.options.fontColor,
|
||||
textEnabled : this.textEnabled,
|
||||
htmlText : this.options.HtmlText,
|
||||
text : this._text, // TODO Is this necessary?
|
||||
element : this.el,
|
||||
data : series.data,
|
||||
color : series.color,
|
||||
shadowSize : series.shadowSize,
|
||||
xScale : _.bind(series.xaxis.d2p, series.xaxis),
|
||||
yScale : _.bind(series.yaxis.d2p, series.yaxis)
|
||||
};
|
||||
|
||||
options = flotr.merge(type, options);
|
||||
|
||||
// Fill
|
||||
options.fillStyle = this.processColor(
|
||||
type.fillColor || series.color,
|
||||
{opacity: type.fillOpacity}
|
||||
);
|
||||
|
||||
return options;
|
||||
},
|
||||
/**
|
||||
* Calculates the coordinates from a mouse event object.
|
||||
* @param {Event} event - Mouse Event object.
|
||||
* @return {Object} Object with coordinates of the mouse.
|
||||
*/
|
||||
getEventPosition: function (e){
|
||||
|
||||
var
|
||||
d = document,
|
||||
b = d.body,
|
||||
de = d.documentElement,
|
||||
axes = this.axes,
|
||||
plotOffset = this.plotOffset,
|
||||
lastMousePos = this.lastMousePos,
|
||||
pointer = E.eventPointer(e),
|
||||
dx = pointer.x - lastMousePos.pageX,
|
||||
dy = pointer.y - lastMousePos.pageY,
|
||||
r, rx, ry;
|
||||
|
||||
if ('ontouchstart' in this.el) {
|
||||
r = D.position(this.overlay);
|
||||
rx = pointer.x - r.left - plotOffset.left;
|
||||
ry = pointer.y - r.top - plotOffset.top;
|
||||
} else {
|
||||
r = this.overlay.getBoundingClientRect();
|
||||
rx = e.clientX - r.left - plotOffset.left - b.scrollLeft - de.scrollLeft;
|
||||
ry = e.clientY - r.top - plotOffset.top - b.scrollTop - de.scrollTop;
|
||||
}
|
||||
|
||||
return {
|
||||
x: axes.x.p2d(rx),
|
||||
x2: axes.x2.p2d(rx),
|
||||
y: axes.y.p2d(ry),
|
||||
y2: axes.y2.p2d(ry),
|
||||
relX: rx,
|
||||
relY: ry,
|
||||
dX: dx,
|
||||
dY: dy,
|
||||
absX: pointer.x,
|
||||
absY: pointer.y,
|
||||
pageX: pointer.x,
|
||||
pageY: pointer.y
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Observes the 'click' event and fires the 'flotr:click' event.
|
||||
* @param {Event} event - 'click' Event object.
|
||||
*/
|
||||
clickHandler: function(event){
|
||||
if(this.ignoreClick){
|
||||
this.ignoreClick = false;
|
||||
return this.ignoreClick;
|
||||
}
|
||||
E.fire(this.el, 'flotr:click', [this.getEventPosition(event), this]);
|
||||
},
|
||||
/**
|
||||
* Observes mouse movement over the graph area. Fires the 'flotr:mousemove' event.
|
||||
* @param {Event} event - 'mousemove' Event object.
|
||||
*/
|
||||
mouseMoveHandler: function(event){
|
||||
if (this.mouseDownMoveHandler) return;
|
||||
var pos = this.getEventPosition(event);
|
||||
E.fire(this.el, 'flotr:mousemove', [event, pos, this]);
|
||||
this.lastMousePos = pos;
|
||||
},
|
||||
/**
|
||||
* Observes the 'mousedown' event.
|
||||
* @param {Event} event - 'mousedown' Event object.
|
||||
*/
|
||||
mouseDownHandler: function (event){
|
||||
|
||||
/*
|
||||
// @TODO Context menu?
|
||||
if(event.isRightClick()) {
|
||||
event.stop();
|
||||
|
||||
var overlay = this.overlay;
|
||||
overlay.hide();
|
||||
|
||||
function cancelContextMenu () {
|
||||
overlay.show();
|
||||
E.stopObserving(document, 'mousemove', cancelContextMenu);
|
||||
}
|
||||
E.observe(document, 'mousemove', cancelContextMenu);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
if (this.mouseUpHandler) return;
|
||||
this.mouseUpHandler = _.bind(function (e) {
|
||||
E.stopObserving(document, 'mouseup', this.mouseUpHandler);
|
||||
E.stopObserving(document, 'mousemove', this.mouseDownMoveHandler);
|
||||
this.mouseDownMoveHandler = null;
|
||||
this.mouseUpHandler = null;
|
||||
// @TODO why?
|
||||
//e.stop();
|
||||
E.fire(this.el, 'flotr:mouseup', [e, this]);
|
||||
}, this);
|
||||
this.mouseDownMoveHandler = _.bind(function (e) {
|
||||
var pos = this.getEventPosition(e);
|
||||
E.fire(this.el, 'flotr:mousemove', [event, pos, this]);
|
||||
this.lastMousePos = pos;
|
||||
}, this);
|
||||
E.observe(document, 'mouseup', this.mouseUpHandler);
|
||||
E.observe(document, 'mousemove', this.mouseDownMoveHandler);
|
||||
E.fire(this.el, 'flotr:mousedown', [event, this]);
|
||||
this.ignoreClick = false;
|
||||
},
|
||||
drawTooltip: function(content, x, y, options) {
|
||||
var mt = this.getMouseTrack(),
|
||||
style = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;',
|
||||
p = options.position,
|
||||
m = options.margin,
|
||||
plotOffset = this.plotOffset;
|
||||
|
||||
if(x !== null && y !== null){
|
||||
if (!options.relative) { // absolute to the canvas
|
||||
if(p.charAt(0) == 'n') style += 'top:' + (m + plotOffset.top) + 'px;bottom:auto;';
|
||||
else if(p.charAt(0) == 's') style += 'bottom:' + (m + plotOffset.bottom) + 'px;top:auto;';
|
||||
if(p.charAt(1) == 'e') style += 'right:' + (m + plotOffset.right) + 'px;left:auto;';
|
||||
else if(p.charAt(1) == 'w') style += 'left:' + (m + plotOffset.left) + 'px;right:auto;';
|
||||
}
|
||||
else { // relative to the mouse
|
||||
if(p.charAt(0) == 'n') style += 'bottom:' + (m - plotOffset.top - y + this.canvasHeight) + 'px;top:auto;';
|
||||
else if(p.charAt(0) == 's') style += 'top:' + (m + plotOffset.top + y) + 'px;bottom:auto;';
|
||||
if(p.charAt(1) == 'e') style += 'left:' + (m + plotOffset.left + x) + 'px;right:auto;';
|
||||
else if(p.charAt(1) == 'w') style += 'right:' + (m - plotOffset.left - x + this.canvasWidth) + 'px;left:auto;';
|
||||
}
|
||||
|
||||
mt.style.cssText = style;
|
||||
D.empty(mt);
|
||||
D.insert(mt, content);
|
||||
D.show(mt);
|
||||
}
|
||||
else {
|
||||
D.hide(mt);
|
||||
}
|
||||
},
|
||||
|
||||
clip: function () {
|
||||
|
||||
var
|
||||
ctx = this.ctx,
|
||||
o = this.plotOffset,
|
||||
w = this.canvasWidth,
|
||||
h = this.canvasHeight;
|
||||
|
||||
if (flotr.isIE && flotr.isIE < 9) {
|
||||
// Clipping for excanvas :-(
|
||||
ctx.save();
|
||||
ctx.fillStyle = this.processColor(this.options.ieBackgroundColor);
|
||||
ctx.fillRect(0, 0, w, o.top);
|
||||
ctx.fillRect(0, 0, o.left, h);
|
||||
ctx.fillRect(0, h - o.bottom, w, o.bottom);
|
||||
ctx.fillRect(w - o.right, 0, o.right,h);
|
||||
ctx.restore();
|
||||
} else {
|
||||
ctx.clearRect(0, 0, w, o.top);
|
||||
ctx.clearRect(0, 0, o.left, h);
|
||||
ctx.clearRect(0, h - o.bottom, w, o.bottom);
|
||||
ctx.clearRect(w - o.right, 0, o.right,h);
|
||||
}
|
||||
},
|
||||
|
||||
_initMembers: function() {
|
||||
this._handles = [];
|
||||
this.lastMousePos = {pageX: null, pageY: null };
|
||||
this.plotOffset = {left: 0, right: 0, top: 0, bottom: 0};
|
||||
this.ignoreClick = true;
|
||||
this.prevHit = null;
|
||||
},
|
||||
|
||||
_initGraphTypes: function() {
|
||||
_.each(flotr.graphTypes, function(handler, graphType){
|
||||
this[graphType] = flotr.clone(handler);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_initEvents: function () {
|
||||
|
||||
var
|
||||
el = this.el,
|
||||
touchendHandler, movement, touchend;
|
||||
|
||||
if ('ontouchstart' in el) {
|
||||
|
||||
touchendHandler = _.bind(function (e) {
|
||||
touchend = true;
|
||||
E.stopObserving(document, 'touchend', touchendHandler);
|
||||
E.fire(el, 'flotr:mouseup', [event, this]);
|
||||
this.multitouches = null;
|
||||
|
||||
if (!movement) {
|
||||
this.clickHandler(e);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.observe(this.overlay, 'touchstart', _.bind(function (e) {
|
||||
movement = false;
|
||||
touchend = false;
|
||||
this.ignoreClick = false;
|
||||
|
||||
if (e.touches && e.touches.length > 1) {
|
||||
this.multitouches = e.touches;
|
||||
}
|
||||
|
||||
E.fire(el, 'flotr:mousedown', [event, this]);
|
||||
this.observe(document, 'touchend', touchendHandler);
|
||||
}, this));
|
||||
|
||||
this.observe(this.overlay, 'touchmove', _.bind(function (e) {
|
||||
|
||||
var pos = this.getEventPosition(e);
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
movement = true;
|
||||
|
||||
if (this.multitouches || (e.touches && e.touches.length > 1)) {
|
||||
this.multitouches = e.touches;
|
||||
} else {
|
||||
if (!touchend) {
|
||||
E.fire(el, 'flotr:mousemove', [event, pos, this]);
|
||||
}
|
||||
}
|
||||
this.lastMousePos = pos;
|
||||
}, this));
|
||||
|
||||
} else {
|
||||
this.
|
||||
observe(this.overlay, 'mousedown', _.bind(this.mouseDownHandler, this)).
|
||||
observe(el, 'mousemove', _.bind(this.mouseMoveHandler, this)).
|
||||
observe(this.overlay, 'click', _.bind(this.clickHandler, this)).
|
||||
observe(el, 'mouseout', function () {
|
||||
E.fire(el, 'flotr:mouseout');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes the canvas and it's overlay canvas element. When the browser is IE, this makes use
|
||||
* of excanvas. The overlay canvas is inserted for displaying interactions. After the canvas elements
|
||||
* are created, the elements are inserted into the container element.
|
||||
*/
|
||||
_initCanvas: function(){
|
||||
var el = this.el,
|
||||
o = this.options,
|
||||
children = el.children,
|
||||
removedChildren = [],
|
||||
child, i,
|
||||
size, style;
|
||||
|
||||
// Empty the el
|
||||
for (i = children.length; i--;) {
|
||||
child = children[i];
|
||||
if (!this.canvas && child.className === 'flotr-canvas') {
|
||||
this.canvas = child;
|
||||
} else if (!this.overlay && child.className === 'flotr-overlay') {
|
||||
this.overlay = child;
|
||||
} else {
|
||||
removedChildren.push(child);
|
||||
}
|
||||
}
|
||||
for (i = removedChildren.length; i--;) {
|
||||
el.removeChild(removedChildren[i]);
|
||||
}
|
||||
|
||||
D.setStyles(el, {position: 'relative'}); // For positioning labels and overlay.
|
||||
size = {};
|
||||
size.width = el.clientWidth;
|
||||
size.height = el.clientHeight;
|
||||
|
||||
if(size.width <= 0 || size.height <= 0 || o.resolution <= 0){
|
||||
throw 'Invalid dimensions for plot, width = ' + size.width + ', height = ' + size.height + ', resolution = ' + o.resolution;
|
||||
}
|
||||
|
||||
// Main canvas for drawing graph types
|
||||
this.canvas = getCanvas(this.canvas, 'canvas');
|
||||
// Overlay canvas for interactive features
|
||||
this.overlay = getCanvas(this.overlay, 'overlay');
|
||||
this.ctx = getContext(this.canvas);
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.octx = getContext(this.overlay);
|
||||
this.octx.clearRect(0, 0, this.overlay.width, this.overlay.height);
|
||||
this.canvasHeight = size.height;
|
||||
this.canvasWidth = size.width;
|
||||
this.textEnabled = !!this.ctx.drawText || !!this.ctx.fillText; // Enable text functions
|
||||
|
||||
function getCanvas(canvas, name){
|
||||
if(!canvas){
|
||||
canvas = D.create('canvas');
|
||||
if (typeof FlashCanvas != "undefined" && typeof canvas.getContext === 'function') {
|
||||
FlashCanvas.initElement(canvas);
|
||||
}
|
||||
canvas.className = 'flotr-'+name;
|
||||
canvas.style.cssText = 'position:absolute;left:0px;top:0px;';
|
||||
D.insert(el, canvas);
|
||||
}
|
||||
_.each(size, function(size, attribute){
|
||||
D.show(canvas);
|
||||
if (name == 'canvas' && canvas.getAttribute(attribute) === size) {
|
||||
return;
|
||||
}
|
||||
canvas.setAttribute(attribute, size * o.resolution);
|
||||
canvas.style[attribute] = size + 'px';
|
||||
});
|
||||
canvas.context_ = null; // Reset the ExCanvas context
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getContext(canvas){
|
||||
if(window.G_vmlCanvasManager) window.G_vmlCanvasManager.initElement(canvas); // For ExCanvas
|
||||
var context = canvas.getContext('2d');
|
||||
if(!window.G_vmlCanvasManager) context.scale(o.resolution, o.resolution);
|
||||
return context;
|
||||
}
|
||||
},
|
||||
|
||||
_initPlugins: function(){
|
||||
// TODO Should be moved to flotr and mixed in.
|
||||
_.each(flotr.plugins, function(plugin, name){
|
||||
_.each(plugin.callbacks, function(fn, c){
|
||||
this.observe(this.el, c, _.bind(fn, this));
|
||||
}, this);
|
||||
this[name] = flotr.clone(plugin);
|
||||
_.each(this[name], function(fn, p){
|
||||
if (_.isFunction(fn))
|
||||
this[name][p] = _.bind(fn, this);
|
||||
}, this);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets options and initializes some variables and color specific values, used by the constructor.
|
||||
* @param {Object} opts - options object
|
||||
*/
|
||||
_initOptions: function(opts){
|
||||
var options = flotr.clone(flotr.defaultOptions);
|
||||
options.x2axis = _.extend(_.clone(options.xaxis), options.x2axis);
|
||||
options.y2axis = _.extend(_.clone(options.yaxis), options.y2axis);
|
||||
this.options = flotr.merge(opts || {}, options);
|
||||
|
||||
if (this.options.grid.minorVerticalLines === null &&
|
||||
this.options.xaxis.scaling === 'logarithmic') {
|
||||
this.options.grid.minorVerticalLines = true;
|
||||
}
|
||||
if (this.options.grid.minorHorizontalLines === null &&
|
||||
this.options.yaxis.scaling === 'logarithmic') {
|
||||
this.options.grid.minorHorizontalLines = true;
|
||||
}
|
||||
|
||||
E.fire(this.el, 'flotr:afterinitoptions', [this]);
|
||||
|
||||
this.axes = flotr.Axis.getAxes(this.options);
|
||||
|
||||
// Initialize some variables used throughout this function.
|
||||
var assignedColors = [],
|
||||
colors = [],
|
||||
ln = this.series.length,
|
||||
neededColors = this.series.length,
|
||||
oc = this.options.colors,
|
||||
usedColors = [],
|
||||
variation = 0,
|
||||
c, i, j, s;
|
||||
|
||||
// Collect user-defined colors from series.
|
||||
for(i = neededColors - 1; i > -1; --i){
|
||||
c = this.series[i].color;
|
||||
if(c){
|
||||
--neededColors;
|
||||
if(_.isNumber(c)) assignedColors.push(c);
|
||||
else usedColors.push(flotr.Color.parse(c));
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the number of colors that need to be generated.
|
||||
for(i = assignedColors.length - 1; i > -1; --i)
|
||||
neededColors = Math.max(neededColors, assignedColors[i] + 1);
|
||||
|
||||
// Generate needed number of colors.
|
||||
for(i = 0; colors.length < neededColors;){
|
||||
c = (oc.length == i) ? new flotr.Color(100, 100, 100) : flotr.Color.parse(oc[i]);
|
||||
|
||||
// Make sure each serie gets a different color.
|
||||
var sign = variation % 2 == 1 ? -1 : 1,
|
||||
factor = 1 + sign * Math.ceil(variation / 2) * 0.2;
|
||||
c.scale(factor, factor, factor);
|
||||
|
||||
/**
|
||||
* @todo if we're getting too close to something else, we should probably skip this one
|
||||
*/
|
||||
colors.push(c);
|
||||
|
||||
if(++i >= oc.length){
|
||||
i = 0;
|
||||
++variation;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill the options with the generated colors.
|
||||
for(i = 0, j = 0; i < ln; ++i){
|
||||
s = this.series[i];
|
||||
|
||||
// Assign the color.
|
||||
if (!s.color){
|
||||
s.color = colors[j++].toString();
|
||||
}else if(_.isNumber(s.color)){
|
||||
s.color = colors[s.color].toString();
|
||||
}
|
||||
|
||||
// Every series needs an axis
|
||||
if (!s.xaxis) s.xaxis = this.axes.x;
|
||||
if (s.xaxis == 1) s.xaxis = this.axes.x;
|
||||
else if (s.xaxis == 2) s.xaxis = this.axes.x2;
|
||||
|
||||
if (!s.yaxis) s.yaxis = this.axes.y;
|
||||
if (s.yaxis == 1) s.yaxis = this.axes.y;
|
||||
else if (s.yaxis == 2) s.yaxis = this.axes.y2;
|
||||
|
||||
// Apply missing options to the series.
|
||||
for (var t in flotr.graphTypes){
|
||||
s[t] = _.extend(_.clone(this.options[t]), s[t]);
|
||||
}
|
||||
s.mouse = _.extend(_.clone(this.options.mouse), s.mouse);
|
||||
|
||||
if (_.isUndefined(s.shadowSize)) s.shadowSize = this.options.shadowSize;
|
||||
}
|
||||
},
|
||||
|
||||
_setEl: function(el) {
|
||||
if (!el) throw 'The target container doesn\'t exist';
|
||||
else if (el.graph instanceof Graph) el.graph.destroy();
|
||||
else if (!el.clientWidth) throw 'The target container must be visible';
|
||||
|
||||
el.graph = this;
|
||||
this.el = el;
|
||||
}
|
||||
};
|
||||
|
||||
Flotr.Graph = Graph;
|
||||
|
||||
})();
|
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Flotr Series Library
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
var
|
||||
_ = Flotr._;
|
||||
|
||||
function Series (o) {
|
||||
_.extend(this, o);
|
||||
}
|
||||
|
||||
Series.prototype = {
|
||||
|
||||
getRange: function () {
|
||||
|
||||
var
|
||||
data = this.data,
|
||||
length = data.length,
|
||||
xmin = Number.MAX_VALUE,
|
||||
ymin = Number.MAX_VALUE,
|
||||
xmax = -Number.MAX_VALUE,
|
||||
ymax = -Number.MAX_VALUE,
|
||||
xused = false,
|
||||
yused = false,
|
||||
x, y, i;
|
||||
|
||||
if (length < 0 || this.hide) return false;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
x = data[i][0];
|
||||
y = data[i][1];
|
||||
if (x < xmin) { xmin = x; xused = true; }
|
||||
if (x > xmax) { xmax = x; xused = true; }
|
||||
if (y < ymin) { ymin = y; yused = true; }
|
||||
if (y > ymax) { ymax = y; yused = true; }
|
||||
}
|
||||
|
||||
return {
|
||||
xmin : xmin,
|
||||
xmax : xmax,
|
||||
ymin : ymin,
|
||||
ymax : ymax,
|
||||
xused : xused,
|
||||
yused : yused
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
_.extend(Series, {
|
||||
/**
|
||||
* Collects dataseries from input and parses the series into the right format. It returns an Array
|
||||
* of Objects each having at least the 'data' key set.
|
||||
* @param {Array, Object} data - Object or array of dataseries
|
||||
* @return {Array} Array of Objects parsed into the right format ({(...,) data: [[x1,y1], [x2,y2], ...] (, ...)})
|
||||
*/
|
||||
getSeries: function(data){
|
||||
return _.map(data, function(s){
|
||||
var series;
|
||||
if (s.data) {
|
||||
series = new Series();
|
||||
_.extend(series, s);
|
||||
} else {
|
||||
series = new Series({data:s});
|
||||
}
|
||||
return series;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Flotr.Series = Series;
|
||||
|
||||
})();
|
|
@ -0,0 +1,88 @@
|
|||
/**
|
||||
* Text Utilities
|
||||
*/
|
||||
(function () {
|
||||
|
||||
var
|
||||
F = Flotr,
|
||||
D = F.DOM,
|
||||
_ = F._,
|
||||
|
||||
Text = function (o) {
|
||||
this.o = o;
|
||||
};
|
||||
|
||||
Text.prototype = {
|
||||
|
||||
dimensions : function (text, canvasStyle, htmlStyle, className) {
|
||||
|
||||
if (!text) return { width : 0, height : 0 };
|
||||
|
||||
return (this.o.html) ?
|
||||
this.html(text, this.o.element, htmlStyle, className) :
|
||||
this.canvas(text, canvasStyle);
|
||||
},
|
||||
|
||||
canvas : function (text, style) {
|
||||
|
||||
if (!this.o.textEnabled) return;
|
||||
style = style || {};
|
||||
|
||||
var
|
||||
metrics = this.measureText(text, style),
|
||||
width = metrics.width,
|
||||
height = style.size || F.defaultOptions.fontSize,
|
||||
angle = style.angle || 0,
|
||||
cosAngle = Math.cos(angle),
|
||||
sinAngle = Math.sin(angle),
|
||||
widthPadding = 2,
|
||||
heightPadding = 6,
|
||||
bounds;
|
||||
|
||||
bounds = {
|
||||
width: Math.abs(cosAngle * width) + Math.abs(sinAngle * height) + widthPadding,
|
||||
height: Math.abs(sinAngle * width) + Math.abs(cosAngle * height) + heightPadding
|
||||
};
|
||||
|
||||
return bounds;
|
||||
},
|
||||
|
||||
html : function (text, element, style, className) {
|
||||
|
||||
var div = D.create('div');
|
||||
|
||||
D.setStyles(div, { 'position' : 'absolute', 'top' : '-10000px' });
|
||||
D.insert(div, '<div style="'+style+'" class="'+className+' flotr-dummy-div">' + text + '</div>');
|
||||
D.insert(this.o.element, div);
|
||||
|
||||
return D.size(div);
|
||||
},
|
||||
|
||||
measureText : function (text, style) {
|
||||
|
||||
var
|
||||
context = this.o.ctx,
|
||||
metrics;
|
||||
|
||||
if (!context.fillText || (F.isIphone && context.measure)) {
|
||||
return { width : context.measure(text, style)};
|
||||
}
|
||||
|
||||
style = _.extend({
|
||||
size: F.defaultOptions.fontSize,
|
||||
weight: 1,
|
||||
angle: 0
|
||||
}, style);
|
||||
|
||||
context.save();
|
||||
context.font = (style.weight > 1 ? "bold " : "") + (style.size*1.3) + "px sans-serif";
|
||||
metrics = context.measureText(text);
|
||||
context.restore();
|
||||
|
||||
return metrics;
|
||||
}
|
||||
};
|
||||
|
||||
Flotr.Text = Text;
|
||||
|
||||
})();
|
|
@ -0,0 +1,84 @@
|
|||
(function () {
|
||||
|
||||
var D = Flotr.DOM;
|
||||
|
||||
Flotr.addPlugin('crosshair', {
|
||||
options: {
|
||||
mode: null, // => one of null, 'x', 'y' or 'xy'
|
||||
color: '#FF0000', // => crosshair color
|
||||
hideCursor: true // => hide the cursor when the crosshair is shown
|
||||
},
|
||||
callbacks: {
|
||||
'flotr:mousemove': function(e, pos) {
|
||||
if (this.options.crosshair.mode) {
|
||||
this.crosshair.clearCrosshair();
|
||||
this.crosshair.drawCrosshair(pos);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws the selection box.
|
||||
*/
|
||||
drawCrosshair: function(pos) {
|
||||
var octx = this.octx,
|
||||
options = this.options.crosshair,
|
||||
plotOffset = this.plotOffset,
|
||||
x = plotOffset.left + pos.relX + 0.5,
|
||||
y = plotOffset.top + pos.relY + 0.5;
|
||||
|
||||
if (pos.relX < 0 || pos.relY < 0 || pos.relX > this.plotWidth || pos.relY > this.plotHeight) {
|
||||
this.el.style.cursor = null;
|
||||
D.removeClass(this.el, 'flotr-crosshair');
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.hideCursor) {
|
||||
this.el.style.cursor = 'none';
|
||||
D.addClass(this.el, 'flotr-crosshair');
|
||||
}
|
||||
|
||||
octx.save();
|
||||
octx.strokeStyle = options.color;
|
||||
octx.lineWidth = 1;
|
||||
octx.beginPath();
|
||||
|
||||
if (options.mode.indexOf('x') != -1) {
|
||||
octx.moveTo(x, plotOffset.top);
|
||||
octx.lineTo(x, plotOffset.top + this.plotHeight);
|
||||
}
|
||||
|
||||
if (options.mode.indexOf('y') != -1) {
|
||||
octx.moveTo(plotOffset.left, y);
|
||||
octx.lineTo(plotOffset.left + this.plotWidth, y);
|
||||
}
|
||||
|
||||
octx.stroke();
|
||||
octx.restore();
|
||||
},
|
||||
/**
|
||||
* Removes the selection box from the overlay canvas.
|
||||
*/
|
||||
clearCrosshair: function() {
|
||||
|
||||
var
|
||||
plotOffset = this.plotOffset,
|
||||
position = this.lastMousePos,
|
||||
context = this.octx;
|
||||
|
||||
if (position) {
|
||||
context.clearRect(
|
||||
position.relX + plotOffset.left,
|
||||
plotOffset.top,
|
||||
1,
|
||||
this.plotHeight + 1
|
||||
);
|
||||
context.clearRect(
|
||||
plotOffset.left,
|
||||
position.relY + plotOffset.top,
|
||||
this.plotWidth + 1,
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,51 @@
|
|||
(function() {
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
_ = Flotr._;
|
||||
|
||||
function getImage (type, canvas, width, height) {
|
||||
|
||||
// TODO add scaling for w / h
|
||||
var
|
||||
mime = 'image/'+type,
|
||||
data = canvas.toDataURL(mime),
|
||||
image = new Image();
|
||||
image.src = data;
|
||||
return image;
|
||||
}
|
||||
|
||||
Flotr.addPlugin('download', {
|
||||
|
||||
saveImage: function (type, width, height, replaceCanvas) {
|
||||
var image = null;
|
||||
if (Flotr.isIE && Flotr.isIE < 9) {
|
||||
image = '<html><body>'+this.canvas.firstChild.innerHTML+'</body></html>';
|
||||
return window.open().document.write(image);
|
||||
}
|
||||
|
||||
if (type !== 'jpeg' && type !== 'png') return;
|
||||
|
||||
image = getImage(type, this.canvas, width, height);
|
||||
|
||||
if (_.isElement(image) && replaceCanvas) {
|
||||
this.download.restoreCanvas();
|
||||
D.hide(this.canvas);
|
||||
D.hide(this.overlay);
|
||||
D.setStyles({position: 'absolute'});
|
||||
D.insert(this.el, image);
|
||||
this.saveImageElement = image;
|
||||
} else {
|
||||
return window.open(image.src);
|
||||
}
|
||||
},
|
||||
|
||||
restoreCanvas: function() {
|
||||
D.show(this.canvas);
|
||||
D.show(this.overlay);
|
||||
if (this.saveImageElement) this.el.removeChild(this.saveImageElement);
|
||||
this.saveImageElement = null;
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,208 @@
|
|||
(function () {
|
||||
|
||||
var E = Flotr.EventAdapter,
|
||||
_ = Flotr._;
|
||||
|
||||
Flotr.addPlugin('graphGrid', {
|
||||
|
||||
callbacks: {
|
||||
'flotr:beforedraw' : function () {
|
||||
this.graphGrid.drawGrid();
|
||||
},
|
||||
'flotr:afterdraw' : function () {
|
||||
this.graphGrid.drawOutline();
|
||||
}
|
||||
},
|
||||
|
||||
drawGrid: function(){
|
||||
|
||||
var
|
||||
ctx = this.ctx,
|
||||
options = this.options,
|
||||
grid = options.grid,
|
||||
verticalLines = grid.verticalLines,
|
||||
horizontalLines = grid.horizontalLines,
|
||||
minorVerticalLines = grid.minorVerticalLines,
|
||||
minorHorizontalLines = grid.minorHorizontalLines,
|
||||
plotHeight = this.plotHeight,
|
||||
plotWidth = this.plotWidth,
|
||||
a, v, i, j;
|
||||
|
||||
if(verticalLines || minorVerticalLines ||
|
||||
horizontalLines || minorHorizontalLines){
|
||||
E.fire(this.el, 'flotr:beforegrid', [this.axes.x, this.axes.y, options, this]);
|
||||
}
|
||||
ctx.save();
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = grid.tickColor;
|
||||
|
||||
function circularHorizontalTicks (ticks) {
|
||||
for(i = 0; i < ticks.length; ++i){
|
||||
var ratio = ticks[i].v / a.max;
|
||||
for(j = 0; j <= sides; ++j){
|
||||
ctx[j === 0 ? 'moveTo' : 'lineTo'](
|
||||
Math.cos(j*coeff+angle)*radius*ratio,
|
||||
Math.sin(j*coeff+angle)*radius*ratio
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawGridLines (ticks, callback) {
|
||||
_.each(_.pluck(ticks, 'v'), function(v){
|
||||
// Don't show lines on upper and lower bounds.
|
||||
if ((v <= a.min || v >= a.max) ||
|
||||
(v == a.min || v == a.max) && grid.outlineWidth)
|
||||
return;
|
||||
callback(Math.floor(a.d2p(v)) + ctx.lineWidth/2);
|
||||
});
|
||||
}
|
||||
function drawVerticalLines (x) {
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, plotHeight);
|
||||
}
|
||||
function drawHorizontalLines (y) {
|
||||
ctx.moveTo(0, y);
|
||||
ctx.lineTo(plotWidth, y);
|
||||
}
|
||||
|
||||
if (grid.circular) {
|
||||
ctx.translate(this.plotOffset.left+plotWidth/2, this.plotOffset.top+plotHeight/2);
|
||||
var radius = Math.min(plotHeight, plotWidth)*options.radar.radiusRatio/2,
|
||||
sides = this.axes.x.ticks.length,
|
||||
coeff = 2*(Math.PI/sides),
|
||||
angle = -Math.PI/2;
|
||||
|
||||
// Draw grid lines in vertical direction.
|
||||
ctx.beginPath();
|
||||
|
||||
a = this.axes.y;
|
||||
|
||||
if(horizontalLines){
|
||||
circularHorizontalTicks(a.ticks);
|
||||
}
|
||||
if(minorHorizontalLines){
|
||||
circularHorizontalTicks(a.minorTicks);
|
||||
}
|
||||
|
||||
if(verticalLines){
|
||||
_.times(sides, function(i){
|
||||
ctx.moveTo(0, 0);
|
||||
ctx.lineTo(Math.cos(i*coeff+angle)*radius, Math.sin(i*coeff+angle)*radius);
|
||||
});
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
else {
|
||||
ctx.translate(this.plotOffset.left, this.plotOffset.top);
|
||||
|
||||
// Draw grid background, if present in options.
|
||||
if(grid.backgroundColor){
|
||||
ctx.fillStyle = this.processColor(grid.backgroundColor, {x1: 0, y1: 0, x2: plotWidth, y2: plotHeight});
|
||||
ctx.fillRect(0, 0, plotWidth, plotHeight);
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
a = this.axes.x;
|
||||
if (verticalLines) drawGridLines(a.ticks, drawVerticalLines);
|
||||
if (minorVerticalLines) drawGridLines(a.minorTicks, drawVerticalLines);
|
||||
|
||||
a = this.axes.y;
|
||||
if (horizontalLines) drawGridLines(a.ticks, drawHorizontalLines);
|
||||
if (minorHorizontalLines) drawGridLines(a.minorTicks, drawHorizontalLines);
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
if(verticalLines || minorVerticalLines ||
|
||||
horizontalLines || minorHorizontalLines){
|
||||
E.fire(this.el, 'flotr:aftergrid', [this.axes.x, this.axes.y, options, this]);
|
||||
}
|
||||
},
|
||||
|
||||
drawOutline: function(){
|
||||
var
|
||||
that = this,
|
||||
options = that.options,
|
||||
grid = options.grid,
|
||||
outline = grid.outline,
|
||||
ctx = that.ctx,
|
||||
backgroundImage = grid.backgroundImage,
|
||||
plotOffset = that.plotOffset,
|
||||
leftOffset = plotOffset.left,
|
||||
topOffset = plotOffset.top,
|
||||
plotWidth = that.plotWidth,
|
||||
plotHeight = that.plotHeight,
|
||||
v, img, src, left, top, globalAlpha;
|
||||
|
||||
if (!grid.outlineWidth) return;
|
||||
|
||||
ctx.save();
|
||||
|
||||
if (grid.circular) {
|
||||
ctx.translate(leftOffset + plotWidth / 2, topOffset + plotHeight / 2);
|
||||
var radius = Math.min(plotHeight, plotWidth) * options.radar.radiusRatio / 2,
|
||||
sides = this.axes.x.ticks.length,
|
||||
coeff = 2*(Math.PI/sides),
|
||||
angle = -Math.PI/2;
|
||||
|
||||
// Draw axis/grid border.
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = grid.outlineWidth;
|
||||
ctx.strokeStyle = grid.color;
|
||||
ctx.lineJoin = 'round';
|
||||
|
||||
for(i = 0; i <= sides; ++i){
|
||||
ctx[i === 0 ? 'moveTo' : 'lineTo'](Math.cos(i*coeff+angle)*radius, Math.sin(i*coeff+angle)*radius);
|
||||
}
|
||||
//ctx.arc(0, 0, radius, 0, Math.PI*2, true);
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
else {
|
||||
ctx.translate(leftOffset, topOffset);
|
||||
|
||||
// Draw axis/grid border.
|
||||
var lw = grid.outlineWidth,
|
||||
orig = 0.5-lw+((lw+1)%2/2),
|
||||
lineTo = 'lineTo',
|
||||
moveTo = 'moveTo';
|
||||
ctx.lineWidth = lw;
|
||||
ctx.strokeStyle = grid.color;
|
||||
ctx.lineJoin = 'miter';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(orig, orig);
|
||||
plotWidth = plotWidth - (lw / 2) % 1;
|
||||
plotHeight = plotHeight + lw / 2;
|
||||
ctx[outline.indexOf('n') !== -1 ? lineTo : moveTo](plotWidth, orig);
|
||||
ctx[outline.indexOf('e') !== -1 ? lineTo : moveTo](plotWidth, plotHeight);
|
||||
ctx[outline.indexOf('s') !== -1 ? lineTo : moveTo](orig, plotHeight);
|
||||
ctx[outline.indexOf('w') !== -1 ? lineTo : moveTo](orig, orig);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
if (backgroundImage) {
|
||||
|
||||
src = backgroundImage.src || backgroundImage;
|
||||
left = (parseInt(backgroundImage.left, 10) || 0) + plotOffset.left;
|
||||
top = (parseInt(backgroundImage.top, 10) || 0) + plotOffset.top;
|
||||
img = new Image();
|
||||
|
||||
img.onload = function() {
|
||||
ctx.save();
|
||||
if (backgroundImage.alpha) ctx.globalAlpha = backgroundImage.alpha;
|
||||
ctx.globalCompositeOperation = 'destination-over';
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height, left, top, plotWidth, plotHeight);
|
||||
ctx.restore();
|
||||
};
|
||||
|
||||
img.src = src;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* Selection Handles Plugin
|
||||
*
|
||||
* Depends upon options.selection.mode
|
||||
*
|
||||
* Options
|
||||
* show - True enables the handles plugin.
|
||||
* drag - Left and Right drag handles
|
||||
* scroll - Scrolling handle
|
||||
*/
|
||||
(function () {
|
||||
|
||||
var D = Flotr.DOM;
|
||||
|
||||
Flotr.addPlugin('handles', {
|
||||
|
||||
options: {
|
||||
show: false,
|
||||
drag: true,
|
||||
scroll: true
|
||||
},
|
||||
|
||||
callbacks: {
|
||||
'flotr:afterinit': init,
|
||||
'flotr:select': handleSelect,
|
||||
'flotr:mousedown': reset,
|
||||
'flotr:mousemove': mouseMoveHandler
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
var
|
||||
options = this.options,
|
||||
handles = this.handles,
|
||||
el = this.el,
|
||||
scroll, left, right, container;
|
||||
|
||||
if (!options.selection.mode || !options.handles.show || 'ontouchstart' in el) return;
|
||||
|
||||
handles.initialized = true;
|
||||
|
||||
container = D.node('<div class="flotr-handles"></div>');
|
||||
options = options.handles;
|
||||
|
||||
// Drag handles
|
||||
if (options.drag) {
|
||||
right = D.node('<div class="flotr-handles-handle flotr-handles-drag flotr-handles-right"></div>');
|
||||
left = D.node('<div class="flotr-handles-handle flotr-handles-drag flotr-handles-left"></div>');
|
||||
D.insert(container, right);
|
||||
D.insert(container, left);
|
||||
D.hide(left);
|
||||
D.hide(right);
|
||||
handles.left = left;
|
||||
handles.right = right;
|
||||
|
||||
this.observe(left, 'mousedown', function () {
|
||||
handles.moveHandler = leftMoveHandler;
|
||||
});
|
||||
this.observe(right, 'mousedown', function () {
|
||||
handles.moveHandler = rightMoveHandler;
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll handle
|
||||
if (options.scroll) {
|
||||
scroll = D.node('<div class="flotr-handles-handle flotr-handles-scroll"></div>');
|
||||
D.insert(container, scroll);
|
||||
D.hide(scroll);
|
||||
handles.scroll = scroll;
|
||||
this.observe(scroll, 'mousedown', function () {
|
||||
handles.moveHandler = scrollMoveHandler;
|
||||
});
|
||||
}
|
||||
|
||||
this.observe(document, 'mouseup', function() {
|
||||
handles.moveHandler = null;
|
||||
});
|
||||
|
||||
D.insert(el, container);
|
||||
}
|
||||
|
||||
|
||||
function handleSelect(selection) {
|
||||
|
||||
if (!this.handles.initialized) return;
|
||||
|
||||
var
|
||||
handles = this.handles,
|
||||
options = this.options.handles,
|
||||
left = handles.left,
|
||||
right = handles.right,
|
||||
scroll = handles.scroll;
|
||||
|
||||
if (options) {
|
||||
if (options.drag) {
|
||||
positionDrag(this, left, selection.x1);
|
||||
positionDrag(this, right, selection.x2);
|
||||
}
|
||||
|
||||
if (options.scroll) {
|
||||
positionScroll(
|
||||
this,
|
||||
scroll,
|
||||
selection.x1,
|
||||
selection.x2
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function positionDrag(graph, handle, x) {
|
||||
|
||||
D.show(handle);
|
||||
|
||||
var size = D.size(handle),
|
||||
l = Math.round(graph.axes.x.d2p(x) - size.width / 2),
|
||||
t = (graph.plotHeight - size.height) / 2;
|
||||
|
||||
D.setStyles(handle, {
|
||||
'left' : l+'px',
|
||||
'top' : t+'px'
|
||||
});
|
||||
}
|
||||
|
||||
function positionScroll(graph, handle, x1, x2) {
|
||||
|
||||
D.show(handle);
|
||||
|
||||
var size = D.size(handle),
|
||||
l = Math.round(graph.axes.x.d2p(x1)),
|
||||
t = (graph.plotHeight) - size.height / 2,
|
||||
w = (graph.axes.x.d2p(x2) - graph.axes.x.d2p(x1));
|
||||
|
||||
D.setStyles(handle, {
|
||||
'left' : l+'px',
|
||||
'top' : t+'px',
|
||||
'width': w+'px'
|
||||
});
|
||||
}
|
||||
|
||||
function reset() {
|
||||
|
||||
if (!this.handles.initialized) return;
|
||||
|
||||
var
|
||||
handles = this.handles;
|
||||
if (handles) {
|
||||
D.hide(handles.left);
|
||||
D.hide(handles.right);
|
||||
D.hide(handles.scroll);
|
||||
}
|
||||
}
|
||||
|
||||
function mouseMoveHandler(e, position) {
|
||||
|
||||
if (!this.handles.initialized) return;
|
||||
if (!this.handles.moveHandler) return;
|
||||
|
||||
var
|
||||
delta = position.dX,
|
||||
selection = this.selection.selection,
|
||||
area = this.selection.getArea(),
|
||||
handles = this.handles;
|
||||
|
||||
handles.moveHandler(area, delta);
|
||||
checkSwap(area, handles);
|
||||
|
||||
this.selection.setSelection(area);
|
||||
}
|
||||
|
||||
function checkSwap (area, handles) {
|
||||
var moveHandler = handles.moveHandler;
|
||||
if (area.x1 > area.x2) {
|
||||
if (moveHandler == leftMoveHandler) {
|
||||
moveHandler = rightMoveHandler;
|
||||
} else if (moveHandler == rightMoveHandler) {
|
||||
moveHandler = leftMoveHandler;
|
||||
}
|
||||
handles.moveHandler = moveHandler;
|
||||
}
|
||||
}
|
||||
|
||||
function leftMoveHandler(area, delta) {
|
||||
area.x1 += delta;
|
||||
}
|
||||
|
||||
function rightMoveHandler(area, delta) {
|
||||
area.x2 += delta;
|
||||
}
|
||||
|
||||
function scrollMoveHandler(area, delta) {
|
||||
area.x1 += delta;
|
||||
area.x2 += delta;
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,337 @@
|
|||
(function () {
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
_ = Flotr._,
|
||||
flotr = Flotr,
|
||||
S_MOUSETRACK = 'opacity:0.7;background-color:#000;color:#fff;display:none;position:absolute;padding:2px 8px;-moz-border-radius:4px;border-radius:4px;white-space:nowrap;';
|
||||
|
||||
Flotr.addPlugin('hit', {
|
||||
callbacks: {
|
||||
'flotr:mousemove': function(e, pos) {
|
||||
this.hit.track(pos);
|
||||
},
|
||||
'flotr:click': function(pos) {
|
||||
this.hit.track(pos);
|
||||
},
|
||||
'flotr:mouseout': function() {
|
||||
this.hit.clearHit();
|
||||
}
|
||||
},
|
||||
track : function (pos) {
|
||||
if (this.options.mouse.track || _.any(this.series, function(s){return s.mouse && s.mouse.track;})) {
|
||||
this.hit.hit(pos);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Try a method on a graph type. If the method exists, execute it.
|
||||
* @param {Object} series
|
||||
* @param {String} method Method name.
|
||||
* @param {Array} args Arguments applied to method.
|
||||
* @return executed successfully or failed.
|
||||
*/
|
||||
executeOnType: function(s, method, args){
|
||||
var
|
||||
success = false,
|
||||
options;
|
||||
|
||||
if (!_.isArray(s)) s = [s];
|
||||
|
||||
function e(s, index) {
|
||||
_.each(_.keys(flotr.graphTypes), function (type) {
|
||||
if (s[type] && s[type].show && this[type][method]) {
|
||||
options = this.getOptions(s, type);
|
||||
|
||||
options.fill = !!s.mouse.fillColor;
|
||||
options.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});
|
||||
options.color = s.mouse.lineColor;
|
||||
options.context = this.octx;
|
||||
options.index = index;
|
||||
|
||||
if (args) options.args = args;
|
||||
this[type][method].call(this[type], options);
|
||||
success = true;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
_.each(s, e, this);
|
||||
|
||||
return success;
|
||||
},
|
||||
/**
|
||||
* Updates the mouse tracking point on the overlay.
|
||||
*/
|
||||
drawHit: function(n){
|
||||
var octx = this.octx,
|
||||
s = n.series;
|
||||
|
||||
if (s.mouse.lineColor) {
|
||||
octx.save();
|
||||
octx.lineWidth = (s.points ? s.points.lineWidth : 1);
|
||||
octx.strokeStyle = s.mouse.lineColor;
|
||||
octx.fillStyle = this.processColor(s.mouse.fillColor || '#ffffff', {opacity: s.mouse.fillOpacity});
|
||||
octx.translate(this.plotOffset.left, this.plotOffset.top);
|
||||
|
||||
if (!this.hit.executeOnType(s, 'drawHit', n)) {
|
||||
var xa = n.xaxis,
|
||||
ya = n.yaxis;
|
||||
|
||||
octx.beginPath();
|
||||
// TODO fix this (points) should move to general testable graph mixin
|
||||
octx.arc(xa.d2p(n.x), ya.d2p(n.y), s.points.radius || s.mouse.radius, 0, 2 * Math.PI, true);
|
||||
octx.fill();
|
||||
octx.stroke();
|
||||
octx.closePath();
|
||||
}
|
||||
octx.restore();
|
||||
}
|
||||
this.prevHit = n;
|
||||
},
|
||||
/**
|
||||
* Removes the mouse tracking point from the overlay.
|
||||
*/
|
||||
clearHit: function(){
|
||||
var prev = this.prevHit,
|
||||
octx = this.octx,
|
||||
plotOffset = this.plotOffset;
|
||||
octx.save();
|
||||
octx.translate(plotOffset.left, plotOffset.top);
|
||||
if (prev) {
|
||||
if (!this.hit.executeOnType(prev.series, 'clearHit', this.prevHit)) {
|
||||
// TODO fix this (points) should move to general testable graph mixin
|
||||
var
|
||||
s = prev.series,
|
||||
lw = (s.points ? s.points.lineWidth : 1);
|
||||
offset = (s.points.radius || s.mouse.radius) + lw;
|
||||
octx.clearRect(
|
||||
prev.xaxis.d2p(prev.x) - offset,
|
||||
prev.yaxis.d2p(prev.y) - offset,
|
||||
offset*2,
|
||||
offset*2
|
||||
);
|
||||
}
|
||||
D.hide(this.mouseTrack);
|
||||
this.prevHit = null;
|
||||
}
|
||||
octx.restore();
|
||||
},
|
||||
/**
|
||||
* Retrieves the nearest data point from the mouse cursor. If it's within
|
||||
* a certain range, draw a point on the overlay canvas and display the x and y
|
||||
* value of the data.
|
||||
* @param {Object} mouse - Object that holds the relative x and y coordinates of the cursor.
|
||||
*/
|
||||
hit: function(mouse){
|
||||
|
||||
var
|
||||
options = this.options,
|
||||
prevHit = this.prevHit,
|
||||
closest, sensibility, dataIndex, seriesIndex, series, value, xaxis, yaxis;
|
||||
|
||||
if (this.series.length === 0) return;
|
||||
|
||||
// Nearest data element.
|
||||
// dist, x, y, relX, relY, absX, absY, sAngle, eAngle, fraction, mouse,
|
||||
// xaxis, yaxis, series, index, seriesIndex
|
||||
n = {
|
||||
relX : mouse.relX,
|
||||
relY : mouse.relY,
|
||||
absX : mouse.absX,
|
||||
absY : mouse.absY
|
||||
};
|
||||
|
||||
if (options.mouse.trackY &&
|
||||
!options.mouse.trackAll &&
|
||||
this.hit.executeOnType(this.series, 'hit', [mouse, n]))
|
||||
{
|
||||
|
||||
if (!_.isUndefined(n.seriesIndex)) {
|
||||
series = this.series[n.seriesIndex];
|
||||
n.series = series;
|
||||
n.mouse = series.mouse;
|
||||
n.xaxis = series.xaxis;
|
||||
n.yaxis = series.yaxis;
|
||||
}
|
||||
} else {
|
||||
|
||||
closest = this.hit.closest(mouse);
|
||||
|
||||
if (closest) {
|
||||
|
||||
closest = options.mouse.trackY ? closest.point : closest.x;
|
||||
seriesIndex = closest.seriesIndex;
|
||||
series = this.series[seriesIndex];
|
||||
xaxis = series.xaxis;
|
||||
yaxis = series.yaxis;
|
||||
sensibility = 2 * series.mouse.sensibility;
|
||||
|
||||
if
|
||||
(options.mouse.trackAll ||
|
||||
(closest.distanceX < sensibility / xaxis.scale &&
|
||||
(!options.mouse.trackY || closest.distanceY < sensibility / yaxis.scale)))
|
||||
{
|
||||
n.series = series;
|
||||
n.xaxis = series.xaxis;
|
||||
n.yaxis = series.yaxis;
|
||||
n.mouse = series.mouse;
|
||||
n.x = closest.x;
|
||||
n.y = closest.y;
|
||||
n.dist = closest.distance;
|
||||
n.index = closest.dataIndex;
|
||||
n.seriesIndex = seriesIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!prevHit || (prevHit.index !== n.index || prevHit.seriesIndex !== n.seriesIndex)) {
|
||||
this.hit.clearHit();
|
||||
if (n.series && n.mouse && n.mouse.track) {
|
||||
this.hit.drawMouseTrack(n);
|
||||
this.hit.drawHit(n);
|
||||
Flotr.EventAdapter.fire(this.el, 'flotr:hit', [n, this]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
closest : function (mouse) {
|
||||
|
||||
var
|
||||
series = this.series,
|
||||
options = this.options,
|
||||
mouseX = mouse.x,
|
||||
mouseY = mouse.y,
|
||||
compare = Number.MAX_VALUE,
|
||||
compareX = Number.MAX_VALUE,
|
||||
closest = {},
|
||||
closestX = {},
|
||||
check = false,
|
||||
serie, data,
|
||||
distance, distanceX, distanceY,
|
||||
x, y, i, j;
|
||||
|
||||
function setClosest (o) {
|
||||
o.distance = distance;
|
||||
o.distanceX = distanceX;
|
||||
o.distanceY = distanceY;
|
||||
o.seriesIndex = i;
|
||||
o.dataIndex = j;
|
||||
o.x = x;
|
||||
o.y = y;
|
||||
}
|
||||
|
||||
for (i = 0; i < series.length; i++) {
|
||||
|
||||
serie = series[i];
|
||||
data = serie.data;
|
||||
|
||||
if (data.length) check = true;
|
||||
|
||||
for (j = data.length; j--;) {
|
||||
|
||||
x = data[j][0];
|
||||
y = data[j][1];
|
||||
|
||||
if (x === null || y === null) continue;
|
||||
|
||||
// don't check if the point isn't visible in the current range
|
||||
if (x < serie.xaxis.min || x > serie.xaxis.max) continue;
|
||||
|
||||
distanceX = Math.abs(x - mouseX);
|
||||
distanceY = Math.abs(y - mouseY);
|
||||
|
||||
// Skip square root for speed
|
||||
distance = distanceX * distanceX + distanceY * distanceY;
|
||||
|
||||
if (distance < compare) {
|
||||
compare = distance;
|
||||
setClosest(closest);
|
||||
}
|
||||
|
||||
if (distanceX < compareX) {
|
||||
compareX = distanceX;
|
||||
setClosest(closestX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return check ? {
|
||||
point : closest,
|
||||
x : closestX
|
||||
} : false;
|
||||
},
|
||||
|
||||
drawMouseTrack : function (n) {
|
||||
|
||||
var
|
||||
pos = '',
|
||||
s = n.series,
|
||||
p = n.mouse.position,
|
||||
m = n.mouse.margin,
|
||||
elStyle = S_MOUSETRACK,
|
||||
mouseTrack = this.mouseTrack,
|
||||
plotOffset = this.plotOffset,
|
||||
left = plotOffset.left,
|
||||
right = plotOffset.right,
|
||||
bottom = plotOffset.bottom,
|
||||
top = plotOffset.top,
|
||||
decimals = n.mouse.trackDecimals,
|
||||
options = this.options;
|
||||
|
||||
// Create
|
||||
if (!mouseTrack) {
|
||||
mouseTrack = D.node('<div class="flotr-mouse-value"></div>');
|
||||
this.mouseTrack = mouseTrack;
|
||||
D.insert(this.el, mouseTrack);
|
||||
}
|
||||
|
||||
if (!n.mouse.relative) { // absolute to the canvas
|
||||
|
||||
if (p.charAt(0) == 'n') pos += 'top:' + (m + top) + 'px;bottom:auto;';
|
||||
else if (p.charAt(0) == 's') pos += 'bottom:' + (m + bottom) + 'px;top:auto;';
|
||||
if (p.charAt(1) == 'e') pos += 'right:' + (m + right) + 'px;left:auto;';
|
||||
else if (p.charAt(1) == 'w') pos += 'left:' + (m + left) + 'px;right:auto;';
|
||||
|
||||
// Bars
|
||||
} else if (s.bars.show) {
|
||||
pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y/2) + this.canvasHeight) + 'px;top:auto;';
|
||||
pos += 'left:' + (m + left + n.xaxis.d2p(n.x - options.bars.barWidth/2)) + 'px;right:auto;';
|
||||
|
||||
// Pie
|
||||
} else if (s.pie.show) {
|
||||
var center = {
|
||||
x: (this.plotWidth)/2,
|
||||
y: (this.plotHeight)/2
|
||||
},
|
||||
radius = (Math.min(this.canvasWidth, this.canvasHeight) * s.pie.sizeRatio) / 2,
|
||||
bisection = n.sAngle<n.eAngle ? (n.sAngle + n.eAngle) / 2: (n.sAngle + n.eAngle + 2* Math.PI) / 2;
|
||||
|
||||
pos += 'bottom:' + (m - top - center.y - Math.sin(bisection) * radius/2 + this.canvasHeight) + 'px;top:auto;';
|
||||
pos += 'left:' + (m + left + center.x + Math.cos(bisection) * radius/2) + 'px;right:auto;';
|
||||
|
||||
// Default
|
||||
} else {
|
||||
if (p.charAt(0) == 'n') pos += 'bottom:' + (m - top - n.yaxis.d2p(n.y) + this.canvasHeight) + 'px;top:auto;';
|
||||
else if (p.charAt(0) == 's') pos += 'top:' + (m + top + n.yaxis.d2p(n.y)) + 'px;bottom:auto;';
|
||||
if (p.charAt(1) == 'e') pos += 'left:' + (m + left + n.xaxis.d2p(n.x)) + 'px;right:auto;';
|
||||
else if (p.charAt(1) == 'w') pos += 'right:' + (m - left - n.xaxis.d2p(n.x) + this.canvasWidth) + 'px;left:auto;';
|
||||
}
|
||||
|
||||
elStyle += pos;
|
||||
mouseTrack.style.cssText = elStyle;
|
||||
|
||||
if (!decimals || decimals < 0) decimals = 0;
|
||||
|
||||
mouseTrack.innerHTML = n.mouse.trackFormatter({
|
||||
x: n.x.toFixed(decimals),
|
||||
y: n.y.toFixed(decimals),
|
||||
series: n.series,
|
||||
index: n.index,
|
||||
nearest: n,
|
||||
fraction: n.fraction
|
||||
});
|
||||
|
||||
D.show(mouseTrack);
|
||||
}
|
||||
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,227 @@
|
|||
(function () {
|
||||
|
||||
var D = Flotr.DOM;
|
||||
|
||||
Flotr.addPlugin('labels', {
|
||||
|
||||
callbacks : {
|
||||
'flotr:afterdraw' : function () {
|
||||
this.labels.draw();
|
||||
}
|
||||
},
|
||||
|
||||
draw: function(){
|
||||
// Construct fixed width label boxes, which can be styled easily.
|
||||
var
|
||||
axis, tick, left, top, xBoxWidth,
|
||||
radius, sides, coeff, angle,
|
||||
div, i, html = '',
|
||||
noLabels = 0,
|
||||
options = this.options,
|
||||
ctx = this.ctx,
|
||||
a = this.axes,
|
||||
style = { size: options.fontSize };
|
||||
|
||||
for (i = 0; i < a.x.ticks.length; ++i){
|
||||
if (a.x.ticks[i].label) { ++noLabels; }
|
||||
}
|
||||
xBoxWidth = this.plotWidth / noLabels;
|
||||
|
||||
if (options.grid.circular) {
|
||||
ctx.save();
|
||||
ctx.translate(this.plotOffset.left + this.plotWidth / 2,
|
||||
this.plotOffset.top + this.plotHeight / 2);
|
||||
|
||||
radius = this.plotHeight * options.radar.radiusRatio / 2 + options.fontSize;
|
||||
sides = this.axes.x.ticks.length;
|
||||
coeff = 2 * (Math.PI / sides);
|
||||
angle = -Math.PI / 2;
|
||||
|
||||
drawLabelCircular(this, a.x, false);
|
||||
drawLabelCircular(this, a.x, true);
|
||||
drawLabelCircular(this, a.y, false);
|
||||
drawLabelCircular(this, a.y, true);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
if (!options.HtmlText && this.textEnabled) {
|
||||
drawLabelNoHtmlText(this, a.x, 'center', 'top');
|
||||
drawLabelNoHtmlText(this, a.x2, 'center', 'bottom');
|
||||
drawLabelNoHtmlText(this, a.y, 'right', 'middle');
|
||||
drawLabelNoHtmlText(this, a.y2, 'left', 'middle');
|
||||
|
||||
} else if ((
|
||||
a.x.options.showLabels ||
|
||||
a.x2.options.showLabels ||
|
||||
a.y.options.showLabels ||
|
||||
a.y2.options.showLabels) &&
|
||||
!options.grid.circular
|
||||
) {
|
||||
|
||||
html = '';
|
||||
|
||||
drawLabelHtml(this, a.x);
|
||||
drawLabelHtml(this, a.x2);
|
||||
drawLabelHtml(this, a.y);
|
||||
drawLabelHtml(this, a.y2);
|
||||
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
div = D.create('div');
|
||||
D.setStyles(div, {
|
||||
fontSize: 'smaller',
|
||||
color: options.grid.color
|
||||
});
|
||||
div.className = 'flotr-labels';
|
||||
D.insert(this.el, div);
|
||||
D.insert(div, html);
|
||||
}
|
||||
|
||||
function drawLabelCircular (graph, axis, minorTicks) {
|
||||
var
|
||||
ticks = minorTicks ? axis.minorTicks : axis.ticks,
|
||||
isX = axis.orientation === 1,
|
||||
isFirst = axis.n === 1,
|
||||
style, offset;
|
||||
|
||||
style = {
|
||||
color : axis.options.color || options.grid.color,
|
||||
angle : Flotr.toRad(axis.options.labelsAngle),
|
||||
textBaseline : 'middle'
|
||||
};
|
||||
|
||||
for (i = 0; i < ticks.length &&
|
||||
(minorTicks ? axis.options.showMinorLabels : axis.options.showLabels); ++i){
|
||||
tick = ticks[i];
|
||||
tick.label += '';
|
||||
if (!tick.label || !tick.label.length) { continue; }
|
||||
|
||||
x = Math.cos(i * coeff + angle) * radius;
|
||||
y = Math.sin(i * coeff + angle) * radius;
|
||||
|
||||
style.textAlign = isX ? (Math.abs(x) < 0.1 ? 'center' : (x < 0 ? 'right' : 'left')) : 'left';
|
||||
|
||||
Flotr.drawText(
|
||||
ctx, tick.label,
|
||||
isX ? x : 3,
|
||||
isX ? y : -(axis.ticks[i].v / axis.max) * (radius - options.fontSize),
|
||||
style
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function drawLabelNoHtmlText (graph, axis, textAlign, textBaseline) {
|
||||
var
|
||||
isX = axis.orientation === 1,
|
||||
isFirst = axis.n === 1,
|
||||
style, offset;
|
||||
|
||||
style = {
|
||||
color : axis.options.color || options.grid.color,
|
||||
textAlign : textAlign,
|
||||
textBaseline : textBaseline,
|
||||
angle : Flotr.toRad(axis.options.labelsAngle)
|
||||
};
|
||||
style = Flotr.getBestTextAlign(style.angle, style);
|
||||
|
||||
for (i = 0; i < axis.ticks.length && continueShowingLabels(axis); ++i) {
|
||||
|
||||
tick = axis.ticks[i];
|
||||
if (!tick.label || !tick.label.length) { continue; }
|
||||
|
||||
offset = axis.d2p(tick.v);
|
||||
if (offset < 0 ||
|
||||
offset > (isX ? graph.plotWidth : graph.plotHeight)) { continue; }
|
||||
|
||||
Flotr.drawText(
|
||||
ctx, tick.label,
|
||||
leftOffset(graph, isX, isFirst, offset),
|
||||
topOffset(graph, isX, isFirst, offset),
|
||||
style
|
||||
);
|
||||
|
||||
// Only draw on axis y2
|
||||
if (!isX && !isFirst) {
|
||||
ctx.save();
|
||||
ctx.strokeStyle = style.color;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(graph.plotOffset.left + graph.plotWidth - 8, graph.plotOffset.top + axis.d2p(tick.v));
|
||||
ctx.lineTo(graph.plotOffset.left + graph.plotWidth, graph.plotOffset.top + axis.d2p(tick.v));
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function continueShowingLabels (axis) {
|
||||
return axis.options.showLabels && axis.used;
|
||||
}
|
||||
function leftOffset (graph, isX, isFirst, offset) {
|
||||
return graph.plotOffset.left +
|
||||
(isX ? offset :
|
||||
(isFirst ?
|
||||
-options.grid.labelMargin :
|
||||
options.grid.labelMargin + graph.plotWidth));
|
||||
}
|
||||
function topOffset (graph, isX, isFirst, offset) {
|
||||
return graph.plotOffset.top +
|
||||
(isX ? options.grid.labelMargin : offset) +
|
||||
((isX && isFirst) ? graph.plotHeight : 0);
|
||||
}
|
||||
}
|
||||
|
||||
function drawLabelHtml (graph, axis) {
|
||||
var
|
||||
isX = axis.orientation === 1,
|
||||
isFirst = axis.n === 1,
|
||||
name = '',
|
||||
left, style, top,
|
||||
offset = graph.plotOffset;
|
||||
|
||||
if (!isX && !isFirst) {
|
||||
ctx.save();
|
||||
ctx.strokeStyle = axis.options.color || options.grid.color;
|
||||
ctx.beginPath();
|
||||
}
|
||||
|
||||
if (axis.options.showLabels && (isFirst ? true : axis.used)) {
|
||||
for (i = 0; i < axis.ticks.length; ++i) {
|
||||
tick = axis.ticks[i];
|
||||
if (!tick.label || !tick.label.length ||
|
||||
((isX ? offset.left : offset.top) + axis.d2p(tick.v) < 0) ||
|
||||
((isX ? offset.left : offset.top) + axis.d2p(tick.v) > (isX ? graph.canvasWidth : graph.canvasHeight))) {
|
||||
continue;
|
||||
}
|
||||
top = offset.top +
|
||||
(isX ?
|
||||
((isFirst ? 1 : -1 ) * (graph.plotHeight + options.grid.labelMargin)) :
|
||||
axis.d2p(tick.v) - axis.maxLabel.height / 2);
|
||||
left = isX ? (offset.left + axis.d2p(tick.v) - xBoxWidth / 2) : 0;
|
||||
|
||||
name = '';
|
||||
if (i === 0) {
|
||||
name = ' first';
|
||||
} else if (i === axis.ticks.length - 1) {
|
||||
name = ' last';
|
||||
}
|
||||
name += isX ? ' flotr-grid-label-x' : ' flotr-grid-label-y';
|
||||
|
||||
html += [
|
||||
'<div style="position:absolute; text-align:' + (isX ? 'center' : 'right') + '; ',
|
||||
'top:' + top + 'px; ',
|
||||
((!isX && !isFirst) ? 'right:' : 'left:') + left + 'px; ',
|
||||
'width:' + (isX ? xBoxWidth : ((isFirst ? offset.left : offset.right) - options.grid.labelMargin)) + 'px; ',
|
||||
axis.options.color ? ('color:' + axis.options.color + '; ') : ' ',
|
||||
'" class="flotr-grid-label' + name + '">' + tick.label + '</div>'
|
||||
].join(' ');
|
||||
|
||||
if (!isX && !isFirst) {
|
||||
ctx.moveTo(offset.left + graph.plotWidth - 8, offset.top + axis.d2p(tick.v));
|
||||
ctx.lineTo(offset.left + graph.plotWidth, offset.top + axis.d2p(tick.v));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,179 @@
|
|||
(function () {
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
_ = Flotr._;
|
||||
|
||||
Flotr.addPlugin('legend', {
|
||||
options: {
|
||||
show: true, // => setting to true will show the legend, hide otherwise
|
||||
noColumns: 1, // => number of colums in legend table // @todo: doesn't work for HtmlText = false
|
||||
labelFormatter: function(v){return v;}, // => fn: string -> string
|
||||
labelBoxBorderColor: '#CCCCCC', // => border color for the little label boxes
|
||||
labelBoxWidth: 14,
|
||||
labelBoxHeight: 10,
|
||||
labelBoxMargin: 5,
|
||||
labelBoxOpacity: 0.4,
|
||||
container: null, // => container (as jQuery object) to put legend in, null means default on top of graph
|
||||
position: 'nw', // => position of default legend container within plot
|
||||
margin: 5, // => distance from grid edge to default legend container within plot
|
||||
backgroundColor: null, // => null means auto-detect
|
||||
backgroundOpacity: 0.85// => set to 0 to avoid background, set to 1 for a solid background
|
||||
},
|
||||
callbacks: {
|
||||
'flotr:afterinit': function() {
|
||||
this.legend.insertLegend();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Adds a legend div to the canvas container or draws it on the canvas.
|
||||
*/
|
||||
insertLegend: function(){
|
||||
|
||||
if(!this.options.legend.show)
|
||||
return;
|
||||
|
||||
var series = this.series,
|
||||
plotOffset = this.plotOffset,
|
||||
options = this.options,
|
||||
legend = options.legend,
|
||||
fragments = [],
|
||||
rowStarted = false,
|
||||
ctx = this.ctx,
|
||||
itemCount = _.filter(series, function(s) {return (s.label && !s.hide);}).length,
|
||||
p = legend.position,
|
||||
m = legend.margin,
|
||||
i, label, color;
|
||||
|
||||
if (itemCount) {
|
||||
if (!options.HtmlText && this.textEnabled && !legend.container) {
|
||||
var style = {
|
||||
size: options.fontSize*1.1,
|
||||
color: options.grid.color
|
||||
};
|
||||
|
||||
var lbw = legend.labelBoxWidth,
|
||||
lbh = legend.labelBoxHeight,
|
||||
lbm = legend.labelBoxMargin,
|
||||
offsetX = plotOffset.left + m,
|
||||
offsetY = plotOffset.top + m;
|
||||
|
||||
// We calculate the labels' max width
|
||||
var labelMaxWidth = 0;
|
||||
for(i = series.length - 1; i > -1; --i){
|
||||
if(!series[i].label || series[i].hide) continue;
|
||||
label = legend.labelFormatter(series[i].label);
|
||||
labelMaxWidth = Math.max(labelMaxWidth, this._text.measureText(label, style).width);
|
||||
}
|
||||
|
||||
var legendWidth = Math.round(lbw + lbm*3 + labelMaxWidth),
|
||||
legendHeight = Math.round(itemCount*(lbm+lbh) + lbm);
|
||||
|
||||
if(p.charAt(0) == 's') offsetY = plotOffset.top + this.plotHeight - (m + legendHeight);
|
||||
if(p.charAt(1) == 'e') offsetX = plotOffset.left + this.plotWidth - (m + legendWidth);
|
||||
|
||||
// Legend box
|
||||
color = this.processColor(legend.backgroundColor || 'rgb(240,240,240)', {opacity: legend.backgroundOpacity || 0.1});
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(offsetX, offsetY, legendWidth, legendHeight);
|
||||
ctx.strokeStyle = legend.labelBoxBorderColor;
|
||||
ctx.strokeRect(Flotr.toPixel(offsetX), Flotr.toPixel(offsetY), legendWidth, legendHeight);
|
||||
|
||||
// Legend labels
|
||||
var x = offsetX + lbm;
|
||||
var y = offsetY + lbm;
|
||||
for(i = 0; i < series.length; i++){
|
||||
if(!series[i].label || series[i].hide) continue;
|
||||
label = legend.labelFormatter(series[i].label);
|
||||
|
||||
ctx.fillStyle = series[i].color;
|
||||
ctx.fillRect(x, y, lbw-1, lbh-1);
|
||||
|
||||
ctx.strokeStyle = legend.labelBoxBorderColor;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeRect(Math.ceil(x)-1.5, Math.ceil(y)-1.5, lbw+2, lbh+2);
|
||||
|
||||
// Legend text
|
||||
Flotr.drawText(ctx, label, x + lbw + lbm, y + lbh, style);
|
||||
|
||||
y += lbh + lbm;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(i = 0; i < series.length; ++i){
|
||||
if(!series[i].label || series[i].hide) continue;
|
||||
|
||||
if(i % legend.noColumns === 0){
|
||||
fragments.push(rowStarted ? '</tr><tr>' : '<tr>');
|
||||
rowStarted = true;
|
||||
}
|
||||
|
||||
// @TODO remove requirement on bars
|
||||
var s = series[i],
|
||||
boxWidth = legend.labelBoxWidth,
|
||||
boxHeight = legend.labelBoxHeight,
|
||||
opacityValue = (s.bars ? s.bars.fillOpacity : legend.labelBoxOpacity),
|
||||
opacity = 'opacity:' + opacityValue + ';filter:alpha(opacity=' + opacityValue*100 + ');';
|
||||
|
||||
label = legend.labelFormatter(s.label);
|
||||
color = 'background-color:' + ((s.bars && s.bars.show && s.bars.fillColor && s.bars.fill) ? s.bars.fillColor : s.color) + ';';
|
||||
|
||||
fragments.push(
|
||||
'<td class="flotr-legend-color-box">',
|
||||
'<div style="border:1px solid ', legend.labelBoxBorderColor, ';padding:1px">',
|
||||
'<div style="width:', (boxWidth-1), 'px;height:', (boxHeight-1), 'px;border:1px solid ', series[i].color, '">', // Border
|
||||
'<div style="width:', boxWidth, 'px;height:', boxHeight, 'px;', 'opacity:.4;', color, '"></div>', // Background
|
||||
'</div>',
|
||||
'</div>',
|
||||
'</td>',
|
||||
'<td class="flotr-legend-label">', label, '</td>'
|
||||
);
|
||||
}
|
||||
if(rowStarted) fragments.push('</tr>');
|
||||
|
||||
if(fragments.length > 0){
|
||||
var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join('') + '</table>';
|
||||
if(legend.container){
|
||||
D.insert(legend.container, table);
|
||||
}
|
||||
else {
|
||||
var styles = {position: 'absolute', 'z-index': 2};
|
||||
|
||||
if(p.charAt(0) == 'n') { styles.top = (m + plotOffset.top) + 'px'; styles.bottom = 'auto'; }
|
||||
else if(p.charAt(0) == 's') { styles.bottom = (m + plotOffset.bottom) + 'px'; styles.top = 'auto'; }
|
||||
if(p.charAt(1) == 'e') { styles.right = (m + plotOffset.right) + 'px'; styles.left = 'auto'; }
|
||||
else if(p.charAt(1) == 'w') { styles.left = (m + plotOffset.left) + 'px'; styles.right = 'auto'; }
|
||||
|
||||
var div = D.create('div'), size;
|
||||
div.className = 'flotr-legend';
|
||||
D.setStyles(div, styles);
|
||||
D.insert(div, table);
|
||||
D.insert(this.el, div);
|
||||
|
||||
if(!legend.backgroundOpacity)
|
||||
return;
|
||||
|
||||
var c = legend.backgroundColor || options.grid.backgroundColor || '#ffffff';
|
||||
|
||||
_.extend(styles, D.size(div), {
|
||||
'backgroundColor': c,
|
||||
'z-index': 1
|
||||
});
|
||||
styles.width += 'px';
|
||||
styles.height += 'px';
|
||||
|
||||
// Put in the transparent background separately to avoid blended labels and
|
||||
div = D.create('div');
|
||||
div.className = 'flotr-legend-bg';
|
||||
D.setStyles(div, styles);
|
||||
D.opacity(div, legend.backgroundOpacity);
|
||||
D.insert(div, ' ');
|
||||
D.insert(this.el, div);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,278 @@
|
|||
/**
|
||||
* Selection Handles Plugin
|
||||
*
|
||||
*
|
||||
* Options
|
||||
* show - True enables the handles plugin.
|
||||
* drag - Left and Right drag handles
|
||||
* scroll - Scrolling handle
|
||||
*/
|
||||
(function () {
|
||||
|
||||
function isLeftClick (e, type) {
|
||||
return (e.which ? (e.which === 1) : (e.button === 0 || e.button === 1));
|
||||
}
|
||||
|
||||
function boundX(x, graph) {
|
||||
return Math.min(Math.max(0, x), graph.plotWidth - 1);
|
||||
}
|
||||
|
||||
function boundY(y, graph) {
|
||||
return Math.min(Math.max(0, y), graph.plotHeight);
|
||||
}
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
E = Flotr.EventAdapter,
|
||||
_ = Flotr._;
|
||||
|
||||
|
||||
Flotr.addPlugin('selection', {
|
||||
|
||||
options: {
|
||||
pinchOnly: null, // Only select on pinch
|
||||
mode: null, // => one of null, 'x', 'y' or 'xy'
|
||||
color: '#B6D9FF', // => selection box color
|
||||
fps: 20 // => frames-per-second
|
||||
},
|
||||
|
||||
callbacks: {
|
||||
'flotr:mouseup' : function (event) {
|
||||
|
||||
var
|
||||
options = this.options.selection,
|
||||
selection = this.selection,
|
||||
pointer = this.getEventPosition(event);
|
||||
|
||||
if (!options || !options.mode) return;
|
||||
if (selection.interval) clearInterval(selection.interval);
|
||||
|
||||
if (this.multitouches) {
|
||||
selection.updateSelection();
|
||||
} else
|
||||
if (!options.pinchOnly) {
|
||||
selection.setSelectionPos(selection.selection.second, pointer);
|
||||
}
|
||||
selection.clearSelection();
|
||||
|
||||
if(selection.selecting && selection.selectionIsSane()){
|
||||
selection.drawSelection();
|
||||
selection.fireSelectEvent();
|
||||
this.ignoreClick = true;
|
||||
}
|
||||
},
|
||||
'flotr:mousedown' : function (event) {
|
||||
|
||||
var
|
||||
options = this.options.selection,
|
||||
selection = this.selection,
|
||||
pointer = this.getEventPosition(event);
|
||||
|
||||
if (!options || !options.mode) return;
|
||||
if (!options.mode || (!isLeftClick(event) && _.isUndefined(event.touches))) return;
|
||||
if (!options.pinchOnly) selection.setSelectionPos(selection.selection.first, pointer);
|
||||
if (selection.interval) clearInterval(selection.interval);
|
||||
|
||||
this.lastMousePos.pageX = null;
|
||||
selection.selecting = false;
|
||||
selection.interval = setInterval(
|
||||
_.bind(selection.updateSelection, this),
|
||||
1000 / options.fps
|
||||
);
|
||||
},
|
||||
'flotr:destroy' : function (event) {
|
||||
clearInterval(this.selection.interval);
|
||||
}
|
||||
},
|
||||
|
||||
// TODO This isn't used. Maybe it belongs in the draw area and fire select event methods?
|
||||
getArea: function() {
|
||||
|
||||
var s = this.selection.selection,
|
||||
first = s.first,
|
||||
second = s.second;
|
||||
|
||||
return {
|
||||
x1: Math.min(first.x, second.x),
|
||||
x2: Math.max(first.x, second.x),
|
||||
y1: Math.min(first.y, second.y),
|
||||
y2: Math.max(first.y, second.y)
|
||||
};
|
||||
},
|
||||
|
||||
selection: {first: {x: -1, y: -1}, second: {x: -1, y: -1}},
|
||||
prevSelection: null,
|
||||
interval: null,
|
||||
|
||||
/**
|
||||
* Fires the 'flotr:select' event when the user made a selection.
|
||||
*/
|
||||
fireSelectEvent: function(name){
|
||||
var a = this.axes,
|
||||
s = this.selection.selection,
|
||||
x1, x2, y1, y2;
|
||||
|
||||
name = name || 'select';
|
||||
|
||||
x1 = a.x.p2d(s.first.x);
|
||||
x2 = a.x.p2d(s.second.x);
|
||||
y1 = a.y.p2d(s.first.y);
|
||||
y2 = a.y.p2d(s.second.y);
|
||||
|
||||
E.fire(this.el, 'flotr:'+name, [{
|
||||
x1:Math.min(x1, x2),
|
||||
y1:Math.min(y1, y2),
|
||||
x2:Math.max(x1, x2),
|
||||
y2:Math.max(y1, y2),
|
||||
xfirst:x1, xsecond:x2, yfirst:y1, ysecond:y2
|
||||
}, this]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows the user the manually select an area.
|
||||
* @param {Object} area - Object with coordinates to select.
|
||||
*/
|
||||
setSelection: function(area, preventEvent){
|
||||
var options = this.options,
|
||||
xa = this.axes.x,
|
||||
ya = this.axes.y,
|
||||
vertScale = ya.scale,
|
||||
hozScale = xa.scale,
|
||||
selX = options.selection.mode.indexOf('x') != -1,
|
||||
selY = options.selection.mode.indexOf('y') != -1,
|
||||
s = this.selection.selection;
|
||||
|
||||
this.selection.clearSelection();
|
||||
|
||||
s.first.y = boundY((selX && !selY) ? 0 : (ya.max - area.y1) * vertScale, this);
|
||||
s.second.y = boundY((selX && !selY) ? this.plotHeight - 1: (ya.max - area.y2) * vertScale, this);
|
||||
s.first.x = boundX((selY && !selX) ? 0 : area.x1, this);
|
||||
s.second.x = boundX((selY && !selX) ? this.plotWidth : area.x2, this);
|
||||
|
||||
this.selection.drawSelection();
|
||||
if (!preventEvent)
|
||||
this.selection.fireSelectEvent();
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the position of the selection.
|
||||
* @param {Object} pos - Position object.
|
||||
* @param {Event} event - Event object.
|
||||
*/
|
||||
setSelectionPos: function(pos, pointer) {
|
||||
var mode = this.options.selection.mode,
|
||||
selection = this.selection.selection;
|
||||
|
||||
if(mode.indexOf('x') == -1) {
|
||||
pos.x = (pos == selection.first) ? 0 : this.plotWidth;
|
||||
}else{
|
||||
pos.x = boundX(pointer.relX, this);
|
||||
}
|
||||
|
||||
if (mode.indexOf('y') == -1) {
|
||||
pos.y = (pos == selection.first) ? 0 : this.plotHeight - 1;
|
||||
}else{
|
||||
pos.y = boundY(pointer.relY, this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws the selection box.
|
||||
*/
|
||||
drawSelection: function() {
|
||||
|
||||
this.selection.fireSelectEvent('selecting');
|
||||
|
||||
var s = this.selection.selection,
|
||||
octx = this.octx,
|
||||
options = this.options,
|
||||
plotOffset = this.plotOffset,
|
||||
prevSelection = this.selection.prevSelection;
|
||||
|
||||
if (prevSelection &&
|
||||
s.first.x == prevSelection.first.x &&
|
||||
s.first.y == prevSelection.first.y &&
|
||||
s.second.x == prevSelection.second.x &&
|
||||
s.second.y == prevSelection.second.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
octx.save();
|
||||
octx.strokeStyle = this.processColor(options.selection.color, {opacity: 0.8});
|
||||
octx.lineWidth = 1;
|
||||
octx.lineJoin = 'miter';
|
||||
octx.fillStyle = this.processColor(options.selection.color, {opacity: 0.4});
|
||||
|
||||
this.selection.prevSelection = {
|
||||
first: { x: s.first.x, y: s.first.y },
|
||||
second: { x: s.second.x, y: s.second.y }
|
||||
};
|
||||
|
||||
var x = Math.min(s.first.x, s.second.x),
|
||||
y = Math.min(s.first.y, s.second.y),
|
||||
w = Math.abs(s.second.x - s.first.x),
|
||||
h = Math.abs(s.second.y - s.first.y);
|
||||
|
||||
octx.fillRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
|
||||
octx.strokeRect(x + plotOffset.left+0.5, y + plotOffset.top+0.5, w, h);
|
||||
octx.restore();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates (draws) the selection box.
|
||||
*/
|
||||
updateSelection: function(){
|
||||
if (!this.lastMousePos.pageX) return;
|
||||
|
||||
this.selection.selecting = true;
|
||||
|
||||
if (this.multitouches) {
|
||||
this.selection.setSelectionPos(this.selection.selection.first, this.getEventPosition(this.multitouches[0]));
|
||||
this.selection.setSelectionPos(this.selection.selection.second, this.getEventPosition(this.multitouches[1]));
|
||||
} else
|
||||
if (this.options.selection.pinchOnly) {
|
||||
return;
|
||||
} else {
|
||||
this.selection.setSelectionPos(this.selection.selection.second, this.lastMousePos);
|
||||
}
|
||||
|
||||
this.selection.clearSelection();
|
||||
|
||||
if(this.selection.selectionIsSane()) {
|
||||
this.selection.drawSelection();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the selection box from the overlay canvas.
|
||||
*/
|
||||
clearSelection: function() {
|
||||
if (!this.selection.prevSelection) return;
|
||||
|
||||
var prevSelection = this.selection.prevSelection,
|
||||
lw = 1,
|
||||
plotOffset = this.plotOffset,
|
||||
x = Math.min(prevSelection.first.x, prevSelection.second.x),
|
||||
y = Math.min(prevSelection.first.y, prevSelection.second.y),
|
||||
w = Math.abs(prevSelection.second.x - prevSelection.first.x),
|
||||
h = Math.abs(prevSelection.second.y - prevSelection.first.y);
|
||||
|
||||
this.octx.clearRect(x + plotOffset.left - lw + 0.5,
|
||||
y + plotOffset.top - lw,
|
||||
w + 2 * lw + 0.5,
|
||||
h + 2 * lw + 0.5);
|
||||
|
||||
this.selection.prevSelection = null;
|
||||
},
|
||||
/**
|
||||
* Determines whether or not the selection is sane and should be drawn.
|
||||
* @return {Boolean} - True when sane, false otherwise.
|
||||
*/
|
||||
selectionIsSane: function(){
|
||||
var s = this.selection.selection;
|
||||
return Math.abs(s.second.x - s.first.x) >= 5 ||
|
||||
Math.abs(s.second.y - s.first.y) >= 5;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
|
@ -0,0 +1,296 @@
|
|||
/** Spreadsheet **/
|
||||
(function() {
|
||||
|
||||
function getRowLabel(value){
|
||||
if (this.options.spreadsheet.tickFormatter){
|
||||
//TODO maybe pass the xaxis formatter to the custom tick formatter as an opt-out?
|
||||
return this.options.spreadsheet.tickFormatter(value);
|
||||
}
|
||||
else {
|
||||
var t = _.find(this.axes.x.ticks, function(t){return t.v == value;});
|
||||
if (t) {
|
||||
return t.label;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
var
|
||||
D = Flotr.DOM,
|
||||
_ = Flotr._;
|
||||
|
||||
Flotr.addPlugin('spreadsheet', {
|
||||
options: {
|
||||
show: false, // => show the data grid using two tabs
|
||||
tabGraphLabel: 'Graph',
|
||||
tabDataLabel: 'Data',
|
||||
toolbarDownload: 'Download CSV', // @todo: add better language support
|
||||
toolbarSelectAll: 'Select all',
|
||||
csvFileSeparator: ',',
|
||||
decimalSeparator: '.',
|
||||
tickFormatter: null,
|
||||
initialTab: 'graph'
|
||||
},
|
||||
/**
|
||||
* Builds the tabs in the DOM
|
||||
*/
|
||||
callbacks: {
|
||||
'flotr:afterconstruct': function(){
|
||||
// @TODO necessary?
|
||||
//this.el.select('.flotr-tabs-group,.flotr-datagrid-container').invoke('remove');
|
||||
|
||||
if (!this.options.spreadsheet.show) return;
|
||||
|
||||
var ss = this.spreadsheet,
|
||||
container = D.node('<div class="flotr-tabs-group" style="position:absolute;left:0px;width:'+this.canvasWidth+'px"></div>'),
|
||||
graph = D.node('<div style="float:left" class="flotr-tab selected">'+this.options.spreadsheet.tabGraphLabel+'</div>'),
|
||||
data = D.node('<div style="float:left" class="flotr-tab">'+this.options.spreadsheet.tabDataLabel+'</div>'),
|
||||
offset;
|
||||
|
||||
ss.tabsContainer = container;
|
||||
ss.tabs = { graph : graph, data : data };
|
||||
|
||||
D.insert(container, graph);
|
||||
D.insert(container, data);
|
||||
D.insert(this.el, container);
|
||||
|
||||
offset = D.size(data).height + 2;
|
||||
this.plotOffset.bottom += offset;
|
||||
|
||||
D.setStyles(container, {top: this.canvasHeight-offset+'px'});
|
||||
|
||||
this.
|
||||
observe(graph, 'click', function(){ss.showTab('graph');}).
|
||||
observe(data, 'click', function(){ss.showTab('data');});
|
||||
if (this.options.spreadsheet.initialTab !== 'graph'){
|
||||
ss.showTab(this.options.spreadsheet.initialTab);
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Builds a matrix of the data to make the correspondance between the x values and the y values :
|
||||
* X value => Y values from the axes
|
||||
* @return {Array} The data grid
|
||||
*/
|
||||
loadDataGrid: function(){
|
||||
if (this.seriesData) return this.seriesData;
|
||||
|
||||
var s = this.series,
|
||||
rows = {};
|
||||
|
||||
/* The data grid is a 2 dimensions array. There is a row for each X value.
|
||||
* Each row contains the x value and the corresponding y value for each serie ('undefined' if there isn't one)
|
||||
**/
|
||||
_.each(s, function(serie, i){
|
||||
_.each(serie.data, function (v) {
|
||||
var x = v[0],
|
||||
y = v[1],
|
||||
r = rows[x];
|
||||
if (r) {
|
||||
r[i+1] = y;
|
||||
} else {
|
||||
var newRow = [];
|
||||
newRow[0] = x;
|
||||
newRow[i+1] = y;
|
||||
rows[x] = newRow;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// The data grid is sorted by x value
|
||||
this.seriesData = _.sortBy(rows, function(row, x){
|
||||
return parseInt(x, 10);
|
||||
});
|
||||
return this.seriesData;
|
||||
},
|
||||
/**
|
||||
* Constructs the data table for the spreadsheet
|
||||
* @todo make a spreadsheet manager (Flotr.Spreadsheet)
|
||||
* @return {Element} The resulting table element
|
||||
*/
|
||||
constructDataGrid: function(){
|
||||
// If the data grid has already been built, nothing to do here
|
||||
if (this.spreadsheet.datagrid) return this.spreadsheet.datagrid;
|
||||
|
||||
var s = this.series,
|
||||
datagrid = this.spreadsheet.loadDataGrid(),
|
||||
colgroup = ['<colgroup><col />'],
|
||||
buttonDownload, buttonSelect, t;
|
||||
|
||||
// First row : series' labels
|
||||
var html = ['<table class="flotr-datagrid"><tr class="first-row">'];
|
||||
html.push('<th> </th>');
|
||||
_.each(s, function(serie,i){
|
||||
html.push('<th scope="col">'+(serie.label || String.fromCharCode(65+i))+'</th>');
|
||||
colgroup.push('<col />');
|
||||
});
|
||||
html.push('</tr>');
|
||||
// Data rows
|
||||
_.each(datagrid, function(row){
|
||||
html.push('<tr>');
|
||||
_.times(s.length+1, function(i){
|
||||
var tag = 'td',
|
||||
value = row[i],
|
||||
// TODO: do we really want to handle problems with floating point
|
||||
// precision here?
|
||||
content = (!_.isUndefined(value) ? Math.round(value*100000)/100000 : '');
|
||||
if (i === 0) {
|
||||
tag = 'th';
|
||||
var label = getRowLabel.call(this, content);
|
||||
if (label) content = label;
|
||||
}
|
||||
|
||||
html.push('<'+tag+(tag=='th'?' scope="row"':'')+'>'+content+'</'+tag+'>');
|
||||
}, this);
|
||||
html.push('</tr>');
|
||||
}, this);
|
||||
colgroup.push('</colgroup>');
|
||||
t = D.node(html.join(''));
|
||||
|
||||
/**
|
||||
* @TODO disabled this
|
||||
if (!Flotr.isIE || Flotr.isIE == 9) {
|
||||
function handleMouseout(){
|
||||
t.select('colgroup col.hover, th.hover').invoke('removeClassName', 'hover');
|
||||
}
|
||||
function handleMouseover(e){
|
||||
var td = e.element(),
|
||||
siblings = td.previousSiblings();
|
||||
t.select('th[scope=col]')[siblings.length-1].addClassName('hover');
|
||||
t.select('colgroup col')[siblings.length].addClassName('hover');
|
||||
}
|
||||
_.each(t.select('td'), function(td) {
|
||||
Flotr.EventAdapter.
|
||||
observe(td, 'mouseover', handleMouseover).
|
||||
observe(td, 'mouseout', handleMouseout);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
buttonDownload = D.node(
|
||||
'<button type="button" class="flotr-datagrid-toolbar-button">' +
|
||||
this.options.spreadsheet.toolbarDownload +
|
||||
'</button>');
|
||||
|
||||
buttonSelect = D.node(
|
||||
'<button type="button" class="flotr-datagrid-toolbar-button">' +
|
||||
this.options.spreadsheet.toolbarSelectAll+
|
||||
'</button>');
|
||||
|
||||
this.
|
||||
observe(buttonDownload, 'click', _.bind(this.spreadsheet.downloadCSV, this)).
|
||||
observe(buttonSelect, 'click', _.bind(this.spreadsheet.selectAllData, this));
|
||||
|
||||
var toolbar = D.node('<div class="flotr-datagrid-toolbar"></div>');
|
||||
D.insert(toolbar, buttonDownload);
|
||||
D.insert(toolbar, buttonSelect);
|
||||
|
||||
var containerHeight =this.canvasHeight - D.size(this.spreadsheet.tabsContainer).height-2,
|
||||
container = D.node('<div class="flotr-datagrid-container" style="position:absolute;left:0px;top:0px;width:'+
|
||||
this.canvasWidth+'px;height:'+containerHeight+'px;overflow:auto;z-index:10"></div>');
|
||||
|
||||
D.insert(container, toolbar);
|
||||
D.insert(container, t);
|
||||
D.insert(this.el, container);
|
||||
this.spreadsheet.datagrid = t;
|
||||
this.spreadsheet.container = container;
|
||||
|
||||
return t;
|
||||
},
|
||||
/**
|
||||
* Shows the specified tab, by its name
|
||||
* @todo make a tab manager (Flotr.Tabs)
|
||||
* @param {String} tabName - The tab name
|
||||
*/
|
||||
showTab: function(tabName){
|
||||
if (this.spreadsheet.activeTab === tabName){
|
||||
return;
|
||||
}
|
||||
switch(tabName) {
|
||||
case 'graph':
|
||||
D.hide(this.spreadsheet.container);
|
||||
D.removeClass(this.spreadsheet.tabs.data, 'selected');
|
||||
D.addClass(this.spreadsheet.tabs.graph, 'selected');
|
||||
break;
|
||||
case 'data':
|
||||
if (!this.spreadsheet.datagrid)
|
||||
this.spreadsheet.constructDataGrid();
|
||||
D.show(this.spreadsheet.container);
|
||||
D.addClass(this.spreadsheet.tabs.data, 'selected');
|
||||
D.removeClass(this.spreadsheet.tabs.graph, 'selected');
|
||||
break;
|
||||
default:
|
||||
throw 'Illegal tab name: ' + tabName;
|
||||
}
|
||||
this.spreadsheet.activeTab = tabName;
|
||||
},
|
||||
/**
|
||||
* Selects the data table in the DOM for copy/paste
|
||||
*/
|
||||
selectAllData: function(){
|
||||
if (this.spreadsheet.tabs) {
|
||||
var selection, range, doc, win, node = this.spreadsheet.constructDataGrid();
|
||||
|
||||
this.spreadsheet.showTab('data');
|
||||
|
||||
// deferred to be able to select the table
|
||||
setTimeout(function () {
|
||||
if ((doc = node.ownerDocument) && (win = doc.defaultView) &&
|
||||
win.getSelection && doc.createRange &&
|
||||
(selection = window.getSelection()) &&
|
||||
selection.removeAllRanges) {
|
||||
range = doc.createRange();
|
||||
range.selectNode(node);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}
|
||||
else if (document.body && document.body.createTextRange &&
|
||||
(range = document.body.createTextRange())) {
|
||||
range.moveToElementText(node);
|
||||
range.select();
|
||||
}
|
||||
}, 0);
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
},
|
||||
/**
|
||||
* Converts the data into CSV in order to download a file
|
||||
*/
|
||||
downloadCSV: function(){
|
||||
var csv = '',
|
||||
series = this.series,
|
||||
options = this.options,
|
||||
dg = this.spreadsheet.loadDataGrid(),
|
||||
separator = encodeURIComponent(options.spreadsheet.csvFileSeparator);
|
||||
|
||||
if (options.spreadsheet.decimalSeparator === options.spreadsheet.csvFileSeparator) {
|
||||
throw "The decimal separator is the same as the column separator ("+options.spreadsheet.decimalSeparator+")";
|
||||
}
|
||||
|
||||
// The first row
|
||||
_.each(series, function(serie, i){
|
||||
csv += separator+'"'+(serie.label || String.fromCharCode(65+i)).replace(/\"/g, '\\"')+'"';
|
||||
});
|
||||
|
||||
csv += "%0D%0A"; // \r\n
|
||||
|
||||
// For each row
|
||||
csv += _.reduce(dg, function(memo, row){
|
||||
var rowLabel = getRowLabel.call(this, row[0]) || '';
|
||||
rowLabel = '"'+(rowLabel+'').replace(/\"/g, '\\"')+'"';
|
||||
var numbers = row.slice(1).join(separator);
|
||||
if (options.spreadsheet.decimalSeparator !== '.') {
|
||||
numbers = numbers.replace(/\./g, options.spreadsheet.decimalSeparator);
|
||||
}
|
||||
return memo + rowLabel+separator+numbers+"%0D%0A"; // \t and \r\n
|
||||
}, '', this);
|
||||
|
||||
if (Flotr.isIE && Flotr.isIE < 9) {
|
||||
csv = csv.replace(new RegExp(separator, 'g'), decodeURIComponent(separator)).replace(/%0A/g, '\n').replace(/%0D/g, '\r');
|
||||
window.open().document.write(csv);
|
||||
}
|
||||
else window.open('data:text/csv,'+csv);
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,177 @@
|
|||
(function () {
|
||||
|
||||
var D = Flotr.DOM;
|
||||
|
||||
Flotr.addPlugin('titles', {
|
||||
callbacks: {
|
||||
'flotr:afterdraw': function() {
|
||||
this.titles.drawTitles();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws the title and the subtitle
|
||||
*/
|
||||
drawTitles : function () {
|
||||
var html,
|
||||
options = this.options,
|
||||
margin = options.grid.labelMargin,
|
||||
ctx = this.ctx,
|
||||
a = this.axes;
|
||||
|
||||
if (!options.HtmlText && this.textEnabled) {
|
||||
var style = {
|
||||
size: options.fontSize,
|
||||
color: options.grid.color,
|
||||
textAlign: 'center'
|
||||
};
|
||||
|
||||
// Add subtitle
|
||||
if (options.subtitle){
|
||||
Flotr.drawText(
|
||||
ctx, options.subtitle,
|
||||
this.plotOffset.left + this.plotWidth/2,
|
||||
this.titleHeight + this.subtitleHeight - 2,
|
||||
style
|
||||
);
|
||||
}
|
||||
|
||||
style.weight = 1.5;
|
||||
style.size *= 1.5;
|
||||
|
||||
// Add title
|
||||
if (options.title){
|
||||
Flotr.drawText(
|
||||
ctx, options.title,
|
||||
this.plotOffset.left + this.plotWidth/2,
|
||||
this.titleHeight - 2,
|
||||
style
|
||||
);
|
||||
}
|
||||
|
||||
style.weight = 1.8;
|
||||
style.size *= 0.8;
|
||||
|
||||
// Add x axis title
|
||||
if (a.x.options.title && a.x.used){
|
||||
style.textAlign = a.x.options.titleAlign || 'center';
|
||||
style.textBaseline = 'top';
|
||||
style.angle = Flotr.toRad(a.x.options.titleAngle);
|
||||
style = Flotr.getBestTextAlign(style.angle, style);
|
||||
Flotr.drawText(
|
||||
ctx, a.x.options.title,
|
||||
this.plotOffset.left + this.plotWidth/2,
|
||||
this.plotOffset.top + a.x.maxLabel.height + this.plotHeight + 2 * margin,
|
||||
style
|
||||
);
|
||||
}
|
||||
|
||||
// Add x2 axis title
|
||||
if (a.x2.options.title && a.x2.used){
|
||||
style.textAlign = a.x2.options.titleAlign || 'center';
|
||||
style.textBaseline = 'bottom';
|
||||
style.angle = Flotr.toRad(a.x2.options.titleAngle);
|
||||
style = Flotr.getBestTextAlign(style.angle, style);
|
||||
Flotr.drawText(
|
||||
ctx, a.x2.options.title,
|
||||
this.plotOffset.left + this.plotWidth/2,
|
||||
this.plotOffset.top - a.x2.maxLabel.height - 2 * margin,
|
||||
style
|
||||
);
|
||||
}
|
||||
|
||||
// Add y axis title
|
||||
if (a.y.options.title && a.y.used){
|
||||
style.textAlign = a.y.options.titleAlign || 'right';
|
||||
style.textBaseline = 'middle';
|
||||
style.angle = Flotr.toRad(a.y.options.titleAngle);
|
||||
style = Flotr.getBestTextAlign(style.angle, style);
|
||||
Flotr.drawText(
|
||||
ctx, a.y.options.title,
|
||||
this.plotOffset.left - a.y.maxLabel.width - 2 * margin,
|
||||
this.plotOffset.top + this.plotHeight / 2,
|
||||
style
|
||||
);
|
||||
}
|
||||
|
||||
// Add y2 axis title
|
||||
if (a.y2.options.title && a.y2.used){
|
||||
style.textAlign = a.y2.options.titleAlign || 'left';
|
||||
style.textBaseline = 'middle';
|
||||
style.angle = Flotr.toRad(a.y2.options.titleAngle);
|
||||
style = Flotr.getBestTextAlign(style.angle, style);
|
||||
Flotr.drawText(
|
||||
ctx, a.y2.options.title,
|
||||
this.plotOffset.left + this.plotWidth + a.y2.maxLabel.width + 2 * margin,
|
||||
this.plotOffset.top + this.plotHeight / 2,
|
||||
style
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
html = [];
|
||||
|
||||
// Add title
|
||||
if (options.title)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:0;left:',
|
||||
this.plotOffset.left, 'px;font-size:1em;font-weight:bold;text-align:center;width:',
|
||||
this.plotWidth,'px;" class="flotr-title">', options.title, '</div>'
|
||||
);
|
||||
|
||||
// Add subtitle
|
||||
if (options.subtitle)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:', this.titleHeight, 'px;left:',
|
||||
this.plotOffset.left, 'px;font-size:smaller;text-align:center;width:',
|
||||
this.plotWidth, 'px;" class="flotr-subtitle">', options.subtitle, '</div>'
|
||||
);
|
||||
|
||||
html.push('</div>');
|
||||
|
||||
html.push('<div class="flotr-axis-title" style="font-weight:bold;">');
|
||||
|
||||
// Add x axis title
|
||||
if (a.x.options.title && a.x.used)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:',
|
||||
(this.plotOffset.top + this.plotHeight + options.grid.labelMargin + a.x.titleSize.height),
|
||||
'px;left:', this.plotOffset.left, 'px;width:', this.plotWidth,
|
||||
'px;text-align:center;" class="flotr-axis-title">', a.x.options.title, '</div>'
|
||||
);
|
||||
|
||||
// Add x2 axis title
|
||||
if (a.x2.options.title && a.x2.used)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:0;left:', this.plotOffset.left, 'px;width:',
|
||||
this.plotWidth, 'px;text-align:center;" class="flotr-axis-title">', a.x2.options.title, '</div>'
|
||||
);
|
||||
|
||||
// Add y axis title
|
||||
if (a.y.options.title && a.y.used)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:',
|
||||
(this.plotOffset.top + this.plotHeight/2 - a.y.titleSize.height/2),
|
||||
'px;left:0;text-align:right;" class="flotr-axis-title">', a.y.options.title, '</div>'
|
||||
);
|
||||
|
||||
// Add y2 axis title
|
||||
if (a.y2.options.title && a.y2.used)
|
||||
html.push(
|
||||
'<div style="position:absolute;top:',
|
||||
(this.plotOffset.top + this.plotHeight/2 - a.y.titleSize.height/2),
|
||||
'px;right:0;text-align:right;" class="flotr-axis-title">', a.y2.options.title, '</div>'
|
||||
);
|
||||
|
||||
html = html.join('');
|
||||
|
||||
var div = D.create('div');
|
||||
D.setStyles({
|
||||
color: options.grid.color
|
||||
});
|
||||
div.className = 'flotr-titles';
|
||||
D.insert(this.el, div);
|
||||
D.insert(div, html);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,274 @@
|
|||
/** Bars **/
|
||||
Flotr.addType('bars', {
|
||||
|
||||
options: {
|
||||
show: false, // => setting to true will show bars, false will hide
|
||||
lineWidth: 2, // => in pixels
|
||||
barWidth: 1, // => in units of the x axis
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillColor: null, // => fill color
|
||||
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
horizontal: false, // => horizontal bars (x and y inverted)
|
||||
stacked: false, // => stacked bar charts
|
||||
centered: true, // => center the bars to their x axis value
|
||||
topPadding: 0.1 // => top padding in percent
|
||||
},
|
||||
|
||||
stack : {
|
||||
positive : [],
|
||||
negative : [],
|
||||
_positive : [], // Shadow
|
||||
_negative : [] // Shadow
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
var
|
||||
context = options.context;
|
||||
|
||||
context.save();
|
||||
context.lineJoin = 'miter';
|
||||
// @TODO linewidth not interpreted the right way.
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.strokeStyle = options.color;
|
||||
if (options.fill) context.fillStyle = options.fillStyle;
|
||||
|
||||
this.plot(options);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
plot : function (options) {
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
shadowSize = options.shadowSize,
|
||||
i, geometry, left, top, width, height;
|
||||
|
||||
if (data.length < 1) return;
|
||||
|
||||
this.translate(context, options.horizontal);
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
|
||||
geometry = this.getBarGeometry(data[i][0], data[i][1], options);
|
||||
if (geometry === null) continue;
|
||||
|
||||
left = geometry.left;
|
||||
top = geometry.top;
|
||||
width = geometry.width;
|
||||
height = geometry.height;
|
||||
|
||||
if (options.fill) context.fillRect(left, top, width, height);
|
||||
if (shadowSize) {
|
||||
context.save();
|
||||
context.fillStyle = 'rgba(0,0,0,0.05)';
|
||||
context.fillRect(left + shadowSize, top + shadowSize, width, height);
|
||||
context.restore();
|
||||
}
|
||||
if (options.lineWidth) {
|
||||
context.strokeRect(left, top, width, height);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
translate : function (context, horizontal) {
|
||||
if (horizontal) {
|
||||
context.rotate(-Math.PI / 2);
|
||||
context.scale(-1, 1);
|
||||
}
|
||||
},
|
||||
|
||||
getBarGeometry : function (x, y, options) {
|
||||
|
||||
var
|
||||
horizontal = options.horizontal,
|
||||
barWidth = options.barWidth,
|
||||
centered = options.centered,
|
||||
stack = options.stacked ? this.stack : false,
|
||||
lineWidth = options.lineWidth,
|
||||
bisection = centered ? barWidth / 2 : 0,
|
||||
xScale = horizontal ? options.yScale : options.xScale,
|
||||
yScale = horizontal ? options.xScale : options.yScale,
|
||||
xValue = horizontal ? y : x,
|
||||
yValue = horizontal ? x : y,
|
||||
stackOffset = 0,
|
||||
stackValue, left, right, top, bottom;
|
||||
|
||||
// Stacked bars
|
||||
if (stack) {
|
||||
stackValue = yValue > 0 ? stack.positive : stack.negative;
|
||||
stackOffset = stackValue[xValue] || stackOffset;
|
||||
stackValue[xValue] = stackOffset + yValue;
|
||||
}
|
||||
|
||||
left = xScale(xValue - bisection);
|
||||
right = xScale(xValue + barWidth - bisection);
|
||||
top = yScale(yValue + stackOffset);
|
||||
bottom = yScale(stackOffset);
|
||||
|
||||
// TODO for test passing... probably looks better without this
|
||||
if (bottom < 0) bottom = 0;
|
||||
|
||||
// TODO Skipping...
|
||||
// if (right < xa.min || left > xa.max || top < ya.min || bottom > ya.max) continue;
|
||||
|
||||
return (x === null || y === null) ? null : {
|
||||
x : xValue,
|
||||
y : yValue,
|
||||
xScale : xScale,
|
||||
yScale : yScale,
|
||||
top : top,
|
||||
left : Math.min(left, right) - lineWidth / 2,
|
||||
width : Math.abs(right - left) - lineWidth,
|
||||
height : bottom - top
|
||||
};
|
||||
},
|
||||
|
||||
hit : function (options) {
|
||||
var
|
||||
data = options.data,
|
||||
args = options.args,
|
||||
mouse = args[0],
|
||||
n = args[1],
|
||||
x = mouse.x,
|
||||
y = mouse.y,
|
||||
hitGeometry = this.getBarGeometry(x, y, options),
|
||||
width = hitGeometry.width / 2,
|
||||
left = hitGeometry.left,
|
||||
geometry, i;
|
||||
|
||||
for (i = data.length; i--;) {
|
||||
geometry = this.getBarGeometry(data[i][0], data[i][1], options);
|
||||
if (geometry.y > hitGeometry.y && Math.abs(left - geometry.left) < width) {
|
||||
n.x = data[i][0];
|
||||
n.y = data[i][1];
|
||||
n.index = i;
|
||||
n.seriesIndex = options.index;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
drawHit : function (options) {
|
||||
// TODO hits for stacked bars; implement using calculateStack option?
|
||||
var
|
||||
context = options.context,
|
||||
args = options.args,
|
||||
geometry = this.getBarGeometry(args.x, args.y, options),
|
||||
left = geometry.left,
|
||||
top = geometry.top,
|
||||
width = geometry.width,
|
||||
height = geometry.height;
|
||||
|
||||
context.save();
|
||||
context.strokeStyle = options.color;
|
||||
context.lineWidth = options.lineWidth;
|
||||
this.translate(context, options.horizontal);
|
||||
|
||||
// Draw highlight
|
||||
context.beginPath();
|
||||
context.moveTo(left, top + height);
|
||||
context.lineTo(left, top);
|
||||
context.lineTo(left + width, top);
|
||||
context.lineTo(left + width, top + height);
|
||||
if (options.fill) {
|
||||
context.fillStyle = options.fillStyle;
|
||||
context.fill();
|
||||
}
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
clearHit: function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
args = options.args,
|
||||
geometry = this.getBarGeometry(args.x, args.y, options),
|
||||
left = geometry.left,
|
||||
width = geometry.width,
|
||||
top = geometry.top,
|
||||
height = geometry.height,
|
||||
lineWidth = 2 * options.lineWidth;
|
||||
|
||||
context.save();
|
||||
this.translate(context, options.horizontal);
|
||||
context.clearRect(
|
||||
left - lineWidth,
|
||||
Math.min(top, top + height) - lineWidth,
|
||||
width + 2 * lineWidth,
|
||||
Math.abs(height) + 2 * lineWidth
|
||||
);
|
||||
context.restore();
|
||||
},
|
||||
|
||||
extendXRange : function (axis, data, options, bars) {
|
||||
this._extendRange(axis, data, options, bars);
|
||||
},
|
||||
|
||||
extendYRange : function (axis, data, options, bars) {
|
||||
this._extendRange(axis, data, options, bars);
|
||||
},
|
||||
_extendRange: function (axis, data, options, bars) {
|
||||
|
||||
var
|
||||
max = axis.options.max;
|
||||
|
||||
if (_.isNumber(max) || _.isString(max)) return;
|
||||
|
||||
var
|
||||
newmin = axis.min,
|
||||
newmax = axis.max,
|
||||
horizontal = options.horizontal,
|
||||
orientation = axis.orientation,
|
||||
positiveSums = this.positiveSums || {},
|
||||
negativeSums = this.negativeSums || {},
|
||||
value, datum, index, j;
|
||||
|
||||
// Sides of bars
|
||||
if ((orientation == 1 && !horizontal) || (orientation == -1 && horizontal)) {
|
||||
if (options.centered) {
|
||||
newmax = Math.max(axis.datamax + options.barWidth, newmax);
|
||||
newmin = Math.min(axis.datamin - options.barWidth, newmin);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.stacked &&
|
||||
((orientation == 1 && horizontal) || (orientation == -1 && !horizontal))){
|
||||
|
||||
for (j = data.length; j--;) {
|
||||
value = data[j][(orientation == 1 ? 1 : 0)]+'';
|
||||
datum = data[j][(orientation == 1 ? 0 : 1)];
|
||||
|
||||
// Positive
|
||||
if (datum > 0) {
|
||||
positiveSums[value] = (positiveSums[value] || 0) + datum;
|
||||
newmax = Math.max(newmax, positiveSums[value]);
|
||||
}
|
||||
|
||||
// Negative
|
||||
else {
|
||||
negativeSums[value] = (negativeSums[value] || 0) + datum;
|
||||
newmin = Math.min(newmin, negativeSums[value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End of bars
|
||||
if ((orientation == 1 && horizontal) || (orientation == -1 && !horizontal)) {
|
||||
if (options.topPadding && (axis.max === axis.datamax || (options.stacked && this.stackMax !== newmax))) {
|
||||
newmax += options.topPadding * (newmax - newmin);
|
||||
}
|
||||
}
|
||||
|
||||
this.stackMin = newmin;
|
||||
this.stackMax = newmax;
|
||||
this.negativeSums = negativeSums;
|
||||
this.positiveSums = positiveSums;
|
||||
|
||||
axis.max = newmax;
|
||||
axis.min = newmin;
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,119 @@
|
|||
/** Bubbles **/
|
||||
Flotr.addType('bubbles', {
|
||||
options: {
|
||||
show: false, // => setting to true will show radar chart, false will hide
|
||||
lineWidth: 2, // => line width in pixels
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
baseRadius: 2 // => ratio of the radar, against the plot size
|
||||
},
|
||||
draw : function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
shadowSize = options.shadowSize;
|
||||
|
||||
context.save();
|
||||
context.lineWidth = options.lineWidth;
|
||||
|
||||
// Shadows
|
||||
context.fillStyle = 'rgba(0,0,0,0.05)';
|
||||
context.strokeStyle = 'rgba(0,0,0,0.05)';
|
||||
this.plot(options, shadowSize / 2);
|
||||
context.strokeStyle = 'rgba(0,0,0,0.1)';
|
||||
this.plot(options, shadowSize / 4);
|
||||
|
||||
// Chart
|
||||
context.strokeStyle = options.color;
|
||||
context.fillStyle = options.fillStyle;
|
||||
this.plot(options);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
plot : function (options, offset) {
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
geometry,
|
||||
i, x, y, z;
|
||||
|
||||
offset = offset || 0;
|
||||
|
||||
for (i = 0; i < data.length; ++i){
|
||||
|
||||
geometry = this.getGeometry(data[i], options);
|
||||
|
||||
context.beginPath();
|
||||
context.arc(geometry.x + offset, geometry.y + offset, geometry.z, 0, 2 * Math.PI, true);
|
||||
context.stroke();
|
||||
if (options.fill) context.fill();
|
||||
context.closePath();
|
||||
}
|
||||
},
|
||||
getGeometry : function (point, options) {
|
||||
return {
|
||||
x : options.xScale(point[0]),
|
||||
y : options.yScale(point[1]),
|
||||
z : point[2] * options.baseRadius
|
||||
};
|
||||
},
|
||||
hit : function (options) {
|
||||
var
|
||||
data = options.data,
|
||||
args = options.args,
|
||||
mouse = args[0],
|
||||
n = args[1],
|
||||
x = mouse.x,
|
||||
y = mouse.y,
|
||||
geometry,
|
||||
dx, dy;
|
||||
|
||||
for (i = data.length; i--;) {
|
||||
geometry = this.getGeometry(data[i], options);
|
||||
|
||||
dx = geometry.x - options.xScale(x);
|
||||
dy = geometry.y - options.yScale(y);
|
||||
|
||||
if (Math.sqrt(dx * dx + dy * dy) < geometry.z) {
|
||||
n.x = data[i][0];
|
||||
n.y = data[i][1];
|
||||
n.index = i;
|
||||
n.seriesIndex = options.index;
|
||||
}
|
||||
}
|
||||
},
|
||||
drawHit : function (options) {
|
||||
|
||||
var
|
||||
context = options.context,
|
||||
geometry = this.getGeometry(options.data[options.args.index], options);
|
||||
|
||||
context.save();
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.fillStyle = options.fillStyle;
|
||||
context.strokeStyle = options.color;
|
||||
context.beginPath();
|
||||
context.arc(geometry.x, geometry.y, geometry.z, 0, 2 * Math.PI, true);
|
||||
context.fill();
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
context.restore();
|
||||
},
|
||||
clearHit : function (options) {
|
||||
|
||||
var
|
||||
context = options.context,
|
||||
geometry = this.getGeometry(options.data[options.args.index], options),
|
||||
offset = geometry.z + options.lineWidth;
|
||||
|
||||
context.save();
|
||||
context.clearRect(
|
||||
geometry.x - offset,
|
||||
geometry.y - offset,
|
||||
2 * offset,
|
||||
2 * offset
|
||||
);
|
||||
context.restore();
|
||||
}
|
||||
// TODO Add a hit calculation method (like pie)
|
||||
});
|
|
@ -0,0 +1,127 @@
|
|||
/** Candles **/
|
||||
Flotr.addType('candles', {
|
||||
options: {
|
||||
show: false, // => setting to true will show candle sticks, false will hide
|
||||
lineWidth: 1, // => in pixels
|
||||
wickLineWidth: 1, // => in pixels
|
||||
candleWidth: 0.6, // => in units of the x axis
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
upFillColor: '#00A8F0',// => up sticks fill color
|
||||
downFillColor: '#CB4B4B',// => down sticks fill color
|
||||
fillOpacity: 0.5, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
// TODO Test this barcharts option.
|
||||
barcharts: false // => draw as barcharts (not standard bars but financial barcharts)
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
|
||||
var
|
||||
context = options.context;
|
||||
|
||||
context.save();
|
||||
context.lineJoin = 'miter';
|
||||
context.lineCap = 'butt';
|
||||
// @TODO linewidth not interpreted the right way.
|
||||
context.lineWidth = options.wickLineWidth || options.lineWidth;
|
||||
|
||||
this.plot(options);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
plot : function (options) {
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
xScale = options.xScale,
|
||||
yScale = options.yScale,
|
||||
width = options.candleWidth / 2,
|
||||
shadowSize = options.shadowSize,
|
||||
lineWidth = options.lineWidth,
|
||||
wickLineWidth = options.wickLineWidth,
|
||||
pixelOffset = (wickLineWidth % 2) / 2,
|
||||
color,
|
||||
datum, x, y,
|
||||
open, high, low, close,
|
||||
left, right, bottom, top, bottom2, top2,
|
||||
i;
|
||||
|
||||
if (data.length < 1) return;
|
||||
|
||||
for (i = 0; i < data.length; i++) {
|
||||
datum = data[i];
|
||||
x = datum[0];
|
||||
open = datum[1];
|
||||
high = datum[2];
|
||||
low = datum[3];
|
||||
close = datum[4];
|
||||
left = xScale(x - width);
|
||||
right = xScale(x + width);
|
||||
bottom = yScale(low);
|
||||
top = yScale(high);
|
||||
bottom2 = yScale(Math.min(open, close));
|
||||
top2 = yScale(Math.max(open, close));
|
||||
|
||||
/*
|
||||
// TODO skipping
|
||||
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
|
||||
continue;
|
||||
*/
|
||||
|
||||
color = options[open > close ? 'downFillColor' : 'upFillColor'];
|
||||
|
||||
// Fill the candle.
|
||||
// TODO Test the barcharts option
|
||||
if (options.fill && !options.barcharts) {
|
||||
context.fillStyle = 'rgba(0,0,0,0.05)';
|
||||
context.fillRect(left + shadowSize, top2 + shadowSize, right - left, bottom2 - top2);
|
||||
context.save();
|
||||
context.globalAlpha = options.fillOpacity;
|
||||
context.fillStyle = color;
|
||||
context.fillRect(left, top2 + lineWidth, right - left, bottom2 - top2);
|
||||
context.restore();
|
||||
}
|
||||
|
||||
// Draw candle outline/border, high, low.
|
||||
if (lineWidth || wickLineWidth) {
|
||||
|
||||
x = Math.floor((left + right) / 2) + pixelOffset;
|
||||
|
||||
context.strokeStyle = color;
|
||||
context.beginPath();
|
||||
|
||||
// TODO Again with the bartcharts
|
||||
if (options.barcharts) {
|
||||
|
||||
context.moveTo(x, Math.floor(top + width));
|
||||
context.lineTo(x, Math.floor(bottom + width));
|
||||
|
||||
y = Math.floor(open + width) + 0.5;
|
||||
context.moveTo(Math.floor(left) + pixelOffset, y);
|
||||
context.lineTo(x, y);
|
||||
|
||||
y = Math.floor(close + width) + 0.5;
|
||||
context.moveTo(Math.floor(right) + pixelOffset, y);
|
||||
context.lineTo(x, y);
|
||||
} else {
|
||||
context.strokeRect(left, top2 + lineWidth, right - left, bottom2 - top2);
|
||||
|
||||
context.moveTo(x, Math.floor(top2 + lineWidth));
|
||||
context.lineTo(x, Math.floor(top + lineWidth));
|
||||
context.moveTo(x, Math.floor(bottom2 + lineWidth));
|
||||
context.lineTo(x, Math.floor(bottom + lineWidth));
|
||||
}
|
||||
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
},
|
||||
extendXRange: function (axis, data, options) {
|
||||
if (axis.options.max === null) {
|
||||
axis.max = Math.max(axis.datamax + 0.5, axis.max);
|
||||
axis.min = Math.min(axis.datamin - 0.5, axis.min);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,229 @@
|
|||
/** Gantt
|
||||
* Base on data in form [s,y,d] where:
|
||||
* y - executor or simply y value
|
||||
* s - task start value
|
||||
* d - task duration
|
||||
* **/
|
||||
Flotr.addType('gantt', {
|
||||
options: {
|
||||
show: false, // => setting to true will show gantt, false will hide
|
||||
lineWidth: 2, // => in pixels
|
||||
barWidth: 1, // => in units of the x axis
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillColor: null, // => fill color
|
||||
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
centered: true // => center the bars to their x axis value
|
||||
},
|
||||
/**
|
||||
* Draws gantt series in the canvas element.
|
||||
* @param {Object} series - Series with options.gantt.show = true.
|
||||
*/
|
||||
draw: function(series) {
|
||||
var ctx = this.ctx,
|
||||
bw = series.gantt.barWidth,
|
||||
lw = Math.min(series.gantt.lineWidth, bw);
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(this.plotOffset.left, this.plotOffset.top);
|
||||
ctx.lineJoin = 'miter';
|
||||
|
||||
/**
|
||||
* @todo linewidth not interpreted the right way.
|
||||
*/
|
||||
ctx.lineWidth = lw;
|
||||
ctx.strokeStyle = series.color;
|
||||
|
||||
ctx.save();
|
||||
this.gantt.plotShadows(series, bw, 0, series.gantt.fill);
|
||||
ctx.restore();
|
||||
|
||||
if(series.gantt.fill){
|
||||
var color = series.gantt.fillColor || series.color;
|
||||
ctx.fillStyle = this.processColor(color, {opacity: series.gantt.fillOpacity});
|
||||
}
|
||||
|
||||
this.gantt.plot(series, bw, 0, series.gantt.fill);
|
||||
ctx.restore();
|
||||
},
|
||||
plot: function(series, barWidth, offset, fill){
|
||||
var data = series.data;
|
||||
if(data.length < 1) return;
|
||||
|
||||
var xa = series.xaxis,
|
||||
ya = series.yaxis,
|
||||
ctx = this.ctx, i;
|
||||
|
||||
for(i = 0; i < data.length; i++){
|
||||
var y = data[i][0],
|
||||
s = data[i][1],
|
||||
d = data[i][2],
|
||||
drawLeft = true, drawTop = true, drawRight = true;
|
||||
|
||||
if (s === null || d === null) continue;
|
||||
|
||||
var left = s,
|
||||
right = s + d,
|
||||
bottom = y - (series.gantt.centered ? barWidth/2 : 0),
|
||||
top = y + barWidth - (series.gantt.centered ? barWidth/2 : 0);
|
||||
|
||||
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
|
||||
continue;
|
||||
|
||||
if(left < xa.min){
|
||||
left = xa.min;
|
||||
drawLeft = false;
|
||||
}
|
||||
|
||||
if(right > xa.max){
|
||||
right = xa.max;
|
||||
if (xa.lastSerie != series)
|
||||
drawTop = false;
|
||||
}
|
||||
|
||||
if(bottom < ya.min)
|
||||
bottom = ya.min;
|
||||
|
||||
if(top > ya.max){
|
||||
top = ya.max;
|
||||
if (ya.lastSerie != series)
|
||||
drawTop = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the bar.
|
||||
*/
|
||||
if(fill){
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);
|
||||
ctx.lineTo(xa.d2p(left), ya.d2p(top) + offset);
|
||||
ctx.lineTo(xa.d2p(right), ya.d2p(top) + offset);
|
||||
ctx.lineTo(xa.d2p(right), ya.d2p(bottom) + offset);
|
||||
ctx.fill();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw bar outline/border.
|
||||
*/
|
||||
if(series.gantt.lineWidth && (drawLeft || drawRight || drawTop)){
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xa.d2p(left), ya.d2p(bottom) + offset);
|
||||
|
||||
ctx[drawLeft ?'lineTo':'moveTo'](xa.d2p(left), ya.d2p(top) + offset);
|
||||
ctx[drawTop ?'lineTo':'moveTo'](xa.d2p(right), ya.d2p(top) + offset);
|
||||
ctx[drawRight?'lineTo':'moveTo'](xa.d2p(right), ya.d2p(bottom) + offset);
|
||||
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
},
|
||||
plotShadows: function(series, barWidth, offset){
|
||||
var data = series.data;
|
||||
if(data.length < 1) return;
|
||||
|
||||
var i, y, s, d,
|
||||
xa = series.xaxis,
|
||||
ya = series.yaxis,
|
||||
ctx = this.ctx,
|
||||
sw = this.options.shadowSize;
|
||||
|
||||
for(i = 0; i < data.length; i++){
|
||||
y = data[i][0];
|
||||
s = data[i][1];
|
||||
d = data[i][2];
|
||||
|
||||
if (s === null || d === null) continue;
|
||||
|
||||
var left = s,
|
||||
right = s + d,
|
||||
bottom = y - (series.gantt.centered ? barWidth/2 : 0),
|
||||
top = y + barWidth - (series.gantt.centered ? barWidth/2 : 0);
|
||||
|
||||
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max)
|
||||
continue;
|
||||
|
||||
if(left < xa.min) left = xa.min;
|
||||
if(right > xa.max) right = xa.max;
|
||||
if(bottom < ya.min) bottom = ya.min;
|
||||
if(top > ya.max) top = ya.max;
|
||||
|
||||
var width = xa.d2p(right)-xa.d2p(left)-((xa.d2p(right)+sw <= this.plotWidth) ? 0 : sw);
|
||||
var height = ya.d2p(bottom)-ya.d2p(top)-((ya.d2p(bottom)+sw <= this.plotHeight) ? 0 : sw );
|
||||
|
||||
ctx.fillStyle = 'rgba(0,0,0,0.05)';
|
||||
ctx.fillRect(Math.min(xa.d2p(left)+sw, this.plotWidth), Math.min(ya.d2p(top)+sw, this.plotHeight), width, height);
|
||||
}
|
||||
},
|
||||
extendXRange: function(axis) {
|
||||
if(axis.options.max === null){
|
||||
var newmin = axis.min,
|
||||
newmax = axis.max,
|
||||
i, j, x, s, g,
|
||||
stackedSumsPos = {},
|
||||
stackedSumsNeg = {},
|
||||
lastSerie = null;
|
||||
|
||||
for(i = 0; i < this.series.length; ++i){
|
||||
s = this.series[i];
|
||||
g = s.gantt;
|
||||
|
||||
if(g.show && s.xaxis == axis) {
|
||||
for (j = 0; j < s.data.length; j++) {
|
||||
if (g.show) {
|
||||
y = s.data[j][0]+'';
|
||||
stackedSumsPos[y] = Math.max((stackedSumsPos[y] || 0), s.data[j][1]+s.data[j][2]);
|
||||
lastSerie = s;
|
||||
}
|
||||
}
|
||||
for (j in stackedSumsPos) {
|
||||
newmax = Math.max(stackedSumsPos[j], newmax);
|
||||
}
|
||||
}
|
||||
}
|
||||
axis.lastSerie = lastSerie;
|
||||
axis.max = newmax;
|
||||
axis.min = newmin;
|
||||
}
|
||||
},
|
||||
extendYRange: function(axis){
|
||||
if(axis.options.max === null){
|
||||
var newmax = Number.MIN_VALUE,
|
||||
newmin = Number.MAX_VALUE,
|
||||
i, j, s, g,
|
||||
stackedSumsPos = {},
|
||||
stackedSumsNeg = {},
|
||||
lastSerie = null;
|
||||
|
||||
for(i = 0; i < this.series.length; ++i){
|
||||
s = this.series[i];
|
||||
g = s.gantt;
|
||||
|
||||
if (g.show && !s.hide && s.yaxis == axis) {
|
||||
var datamax = Number.MIN_VALUE, datamin = Number.MAX_VALUE;
|
||||
for(j=0; j < s.data.length; j++){
|
||||
datamax = Math.max(datamax,s.data[j][0]);
|
||||
datamin = Math.min(datamin,s.data[j][0]);
|
||||
}
|
||||
|
||||
if (g.centered) {
|
||||
newmax = Math.max(datamax + 0.5, newmax);
|
||||
newmin = Math.min(datamin - 0.5, newmin);
|
||||
}
|
||||
else {
|
||||
newmax = Math.max(datamax + 1, newmax);
|
||||
newmin = Math.min(datamin, newmin);
|
||||
}
|
||||
// For normal horizontal bars
|
||||
if (g.barWidth + datamax > newmax){
|
||||
newmax = axis.max + g.barWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
axis.lastSerie = lastSerie;
|
||||
axis.max = newmax;
|
||||
axis.min = newmin;
|
||||
axis.tickSize = Flotr.getTickSize(axis.options.noTicks, newmin, newmax, axis.options.tickDecimals);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,275 @@
|
|||
/** Lines **/
|
||||
Flotr.addType('lines', {
|
||||
options: {
|
||||
show: false, // => setting to true will show lines, false will hide
|
||||
lineWidth: 2, // => line width in pixels
|
||||
fill: false, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillBorder: false, // => draw a border around the fill
|
||||
fillColor: null, // => fill color
|
||||
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
steps: false, // => draw steps
|
||||
stacked: false // => setting to true will show stacked lines, false will show normal lines
|
||||
},
|
||||
|
||||
stack : {
|
||||
values : []
|
||||
},
|
||||
|
||||
/**
|
||||
* Draws lines series in the canvas element.
|
||||
* @param {Object} options
|
||||
*/
|
||||
draw : function (options) {
|
||||
|
||||
var
|
||||
context = options.context,
|
||||
lineWidth = options.lineWidth,
|
||||
shadowSize = options.shadowSize,
|
||||
offset;
|
||||
|
||||
context.save();
|
||||
context.lineJoin = 'round';
|
||||
|
||||
if (shadowSize) {
|
||||
|
||||
context.lineWidth = shadowSize / 2;
|
||||
offset = lineWidth / 2 + context.lineWidth / 2;
|
||||
|
||||
// @TODO do this instead with a linear gradient
|
||||
context.strokeStyle = "rgba(0,0,0,0.1)";
|
||||
this.plot(options, offset + shadowSize / 2, false);
|
||||
|
||||
context.strokeStyle = "rgba(0,0,0,0.2)";
|
||||
this.plot(options, offset, false);
|
||||
}
|
||||
|
||||
context.lineWidth = lineWidth;
|
||||
context.strokeStyle = options.color;
|
||||
|
||||
this.plot(options, 0, true);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
plot : function (options, shadowOffset, incStack) {
|
||||
|
||||
var
|
||||
context = options.context,
|
||||
width = options.width,
|
||||
height = options.height,
|
||||
xScale = options.xScale,
|
||||
yScale = options.yScale,
|
||||
data = options.data,
|
||||
stack = options.stacked ? this.stack : false,
|
||||
length = data.length - 1,
|
||||
prevx = null,
|
||||
prevy = null,
|
||||
zero = yScale(0),
|
||||
x1, x2, y1, y2, stack1, stack2, i;
|
||||
|
||||
if (length < 1) return;
|
||||
|
||||
context.beginPath();
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
|
||||
// To allow empty values
|
||||
if (data[i][1] === null || data[i+1][1] === null) continue;
|
||||
|
||||
// Zero is infinity for log scales
|
||||
// TODO handle zero for logarithmic
|
||||
// if (xa.options.scaling === 'logarithmic' && (data[i][0] <= 0 || data[i+1][0] <= 0)) continue;
|
||||
// if (ya.options.scaling === 'logarithmic' && (data[i][1] <= 0 || data[i+1][1] <= 0)) continue;
|
||||
|
||||
x1 = xScale(data[i][0]);
|
||||
x2 = xScale(data[i+1][0]);
|
||||
|
||||
if (stack) {
|
||||
|
||||
stack1 = stack.values[data[i][0]] || 0;
|
||||
stack2 = stack.values[data[i+1][0]] || stack.values[data[i][0]] || 0;
|
||||
|
||||
y1 = yScale(data[i][1] + stack1);
|
||||
y2 = yScale(data[i+1][1] + stack2);
|
||||
|
||||
if(incStack){
|
||||
stack.values[data[i][0]] = data[i][1]+stack1;
|
||||
|
||||
if(i == length-1)
|
||||
stack.values[data[i+1][0]] = data[i+1][1]+stack2;
|
||||
}
|
||||
}
|
||||
else{
|
||||
y1 = yScale(data[i][1]);
|
||||
y2 = yScale(data[i+1][1]);
|
||||
}
|
||||
|
||||
if (
|
||||
(y1 > height && y2 > height) ||
|
||||
(y1 < 0 && y2 < 0) ||
|
||||
(x1 < 0 && x2 < 0) ||
|
||||
(x1 > width && x2 > width)
|
||||
) continue;
|
||||
|
||||
if((prevx != x1) || (prevy != y1 + shadowOffset))
|
||||
context.moveTo(x1, y1 + shadowOffset);
|
||||
|
||||
prevx = x2;
|
||||
prevy = y2 + shadowOffset;
|
||||
if (options.steps) {
|
||||
context.lineTo(prevx + shadowOffset / 2, y1 + shadowOffset);
|
||||
context.lineTo(prevx + shadowOffset / 2, prevy);
|
||||
} else {
|
||||
context.lineTo(prevx, prevy);
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.fill || options.fill && !options.fillBorder) context.stroke();
|
||||
|
||||
// TODO stacked lines
|
||||
if(!shadowOffset && options.fill){
|
||||
x1 = xScale(data[0][0]);
|
||||
context.fillStyle = options.fillStyle;
|
||||
context.lineTo(x2, zero);
|
||||
context.lineTo(x1, zero);
|
||||
context.lineTo(x1, yScale(data[0][1]));
|
||||
context.fill();
|
||||
if (options.fillBorder) {
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
context.closePath();
|
||||
},
|
||||
|
||||
// Perform any pre-render precalculations (this should be run on data first)
|
||||
// - Pie chart total for calculating measures
|
||||
// - Stacks for lines and bars
|
||||
// precalculate : function () {
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Get any bounds after pre calculation (axis can fetch this if does not have explicit min/max)
|
||||
// getBounds : function () {
|
||||
// }
|
||||
// getMin : function () {
|
||||
// }
|
||||
// getMax : function () {
|
||||
// }
|
||||
//
|
||||
//
|
||||
// Padding around rendered elements
|
||||
// getPadding : function () {
|
||||
// }
|
||||
|
||||
extendYRange : function (axis, data, options, lines) {
|
||||
|
||||
var o = axis.options;
|
||||
|
||||
// If stacked and auto-min
|
||||
if (options.stacked && ((!o.max && o.max !== 0) || (!o.min && o.min !== 0))) {
|
||||
|
||||
var
|
||||
newmax = axis.max,
|
||||
newmin = axis.min,
|
||||
positiveSums = lines.positiveSums || {},
|
||||
negativeSums = lines.negativeSums || {},
|
||||
x, j;
|
||||
|
||||
for (j = 0; j < data.length; j++) {
|
||||
|
||||
x = data[j][0] + '';
|
||||
|
||||
// Positive
|
||||
if (data[j][1] > 0) {
|
||||
positiveSums[x] = (positiveSums[x] || 0) + data[j][1];
|
||||
newmax = Math.max(newmax, positiveSums[x]);
|
||||
}
|
||||
|
||||
// Negative
|
||||
else {
|
||||
negativeSums[x] = (negativeSums[x] || 0) + data[j][1];
|
||||
newmin = Math.min(newmin, negativeSums[x]);
|
||||
}
|
||||
}
|
||||
|
||||
lines.negativeSums = negativeSums;
|
||||
lines.positiveSums = positiveSums;
|
||||
|
||||
axis.max = newmax;
|
||||
axis.min = newmin;
|
||||
}
|
||||
|
||||
if (options.steps) {
|
||||
|
||||
this.hit = function (options) {
|
||||
var
|
||||
data = options.data,
|
||||
args = options.args,
|
||||
yScale = options.yScale,
|
||||
mouse = args[0],
|
||||
length = data.length,
|
||||
n = args[1],
|
||||
x = mouse.x,
|
||||
relY = mouse.relY,
|
||||
i;
|
||||
|
||||
for (i = 0; i < length - 1; i++) {
|
||||
if (x >= data[i][0] && x <= data[i+1][0]) {
|
||||
if (Math.abs(yScale(data[i][1]) - relY) < 8) {
|
||||
n.x = data[i][0];
|
||||
n.y = data[i][1];
|
||||
n.index = i;
|
||||
n.seriesIndex = options.index;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.drawHit = function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
args = options.args,
|
||||
data = options.data,
|
||||
xScale = options.xScale,
|
||||
index = args.index,
|
||||
x = xScale(args.x),
|
||||
y = options.yScale(args.y),
|
||||
x2;
|
||||
|
||||
if (data.length - 1 > index) {
|
||||
x2 = options.xScale(data[index + 1][0]);
|
||||
context.save();
|
||||
context.strokeStyle = options.color;
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.beginPath();
|
||||
context.moveTo(x, y);
|
||||
context.lineTo(x2, y);
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
context.restore();
|
||||
}
|
||||
};
|
||||
|
||||
this.clearHit = function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
args = options.args,
|
||||
data = options.data,
|
||||
xScale = options.xScale,
|
||||
width = options.lineWidth,
|
||||
index = args.index,
|
||||
x = xScale(args.x),
|
||||
y = options.yScale(args.y),
|
||||
x2;
|
||||
|
||||
if (data.length - 1 > index) {
|
||||
x2 = options.xScale(data[index + 1][0]);
|
||||
context.clearRect(x - width, y - width, x2 - x + 2 * width, 2 * width);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,140 @@
|
|||
/** Markers **/
|
||||
/**
|
||||
* Formats the marker labels.
|
||||
* @param {Object} obj - Marker value Object {x:..,y:..}
|
||||
* @return {String} Formatted marker string
|
||||
*/
|
||||
(function () {
|
||||
|
||||
Flotr.defaultMarkerFormatter = function(obj){
|
||||
return (Math.round(obj.y*100)/100)+'';
|
||||
};
|
||||
|
||||
Flotr.addType('markers', {
|
||||
options: {
|
||||
show: false, // => setting to true will show markers, false will hide
|
||||
lineWidth: 1, // => line width of the rectangle around the marker
|
||||
color: '#000000', // => text color
|
||||
fill: false, // => fill or not the marekers' rectangles
|
||||
fillColor: "#FFFFFF", // => fill color
|
||||
fillOpacity: 0.4, // => fill opacity
|
||||
stroke: false, // => draw the rectangle around the markers
|
||||
position: 'ct', // => the markers position (vertical align: b, m, t, horizontal align: l, c, r)
|
||||
verticalMargin: 0, // => the margin between the point and the text.
|
||||
labelFormatter: Flotr.defaultMarkerFormatter,
|
||||
fontSize: Flotr.defaultOptions.fontSize,
|
||||
stacked: false, // => true if markers should be stacked
|
||||
stackingType: 'b', // => define staching behavior, (b- bars like, a - area like) (see Issue 125 for details)
|
||||
horizontal: false // => true if markers should be horizontal (For now only in a case on horizontal stacked bars, stacks should be calculated horizontaly)
|
||||
},
|
||||
|
||||
// TODO test stacked markers.
|
||||
stack : {
|
||||
positive : [],
|
||||
negative : [],
|
||||
values : []
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
stack = options.stacked ? options.stack : false,
|
||||
stackType = options.stackingType,
|
||||
stackOffsetNeg,
|
||||
stackOffsetPos,
|
||||
stackOffset,
|
||||
i, x, y, label;
|
||||
|
||||
context.save();
|
||||
context.lineJoin = 'round';
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.strokeStyle = 'rgba(0,0,0,0.5)';
|
||||
context.fillStyle = options.fillStyle;
|
||||
|
||||
function stackPos (a, b) {
|
||||
stackOffsetPos = stack.negative[a] || 0;
|
||||
stackOffsetNeg = stack.positive[a] || 0;
|
||||
if (b > 0) {
|
||||
stack.positive[a] = stackOffsetPos + b;
|
||||
return stackOffsetPos + b;
|
||||
} else {
|
||||
stack.negative[a] = stackOffsetNeg + b;
|
||||
return stackOffsetNeg + b;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < data.length; ++i) {
|
||||
|
||||
x = data[i][0];
|
||||
y = data[i][1];
|
||||
|
||||
if (stack) {
|
||||
if (stackType == 'b') {
|
||||
if (options.horizontal) y = stackPos(y, x);
|
||||
else x = stackPos(x, y);
|
||||
} else if (stackType == 'a') {
|
||||
stackOffset = stack.values[x] || 0;
|
||||
stack.values[x] = stackOffset + y;
|
||||
y = stackOffset + y;
|
||||
}
|
||||
}
|
||||
|
||||
label = options.labelFormatter({x: x, y: y, index: i, data : data});
|
||||
this.plot(options.xScale(x), options.yScale(y), label, options);
|
||||
}
|
||||
context.restore();
|
||||
},
|
||||
plot: function(x, y, label, options) {
|
||||
var context = options.context;
|
||||
if (isImage(label) && !label.complete) {
|
||||
throw 'Marker image not loaded.';
|
||||
} else {
|
||||
this._plot(x, y, label, options);
|
||||
}
|
||||
},
|
||||
|
||||
_plot: function(x, y, label, options) {
|
||||
var context = options.context,
|
||||
margin = 2,
|
||||
left = x,
|
||||
top = y,
|
||||
dim;
|
||||
|
||||
if (isImage(label))
|
||||
dim = {height : label.height, width: label.width};
|
||||
else
|
||||
dim = options.text.canvas(label);
|
||||
|
||||
dim.width = Math.floor(dim.width+margin*2);
|
||||
dim.height = Math.floor(dim.height+margin*2);
|
||||
|
||||
if (options.position.indexOf('c') != -1) left -= dim.width/2 + margin;
|
||||
else if (options.position.indexOf('l') != -1) left -= dim.width;
|
||||
|
||||
if (options.position.indexOf('m') != -1) top -= dim.height/2 + margin;
|
||||
else if (options.position.indexOf('t') != -1) top -= dim.height + options.verticalMargin;
|
||||
else top += options.verticalMargin;
|
||||
|
||||
left = Math.floor(left)+0.5;
|
||||
top = Math.floor(top)+0.5;
|
||||
|
||||
if(options.fill)
|
||||
context.fillRect(left, top, dim.width, dim.height);
|
||||
|
||||
if(options.stroke)
|
||||
context.strokeRect(left, top, dim.width, dim.height);
|
||||
|
||||
if (isImage(label))
|
||||
context.drawImage(label, left+margin, top+margin);
|
||||
else
|
||||
Flotr.drawText(context, label, left+margin, top+margin, {textBaseline: 'top', textAlign: 'left', size: options.fontSize, color: options.color});
|
||||
}
|
||||
});
|
||||
|
||||
function isImage (i) {
|
||||
return typeof i === 'object' && i.constructor && (Image ? true : i.constructor === Image);
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,210 @@
|
|||
/** Pie **/
|
||||
/**
|
||||
* Formats the pies labels.
|
||||
* @param {Object} slice - Slice object
|
||||
* @return {String} Formatted pie label string
|
||||
*/
|
||||
(function () {
|
||||
|
||||
var
|
||||
_ = Flotr._;
|
||||
|
||||
Flotr.defaultPieLabelFormatter = function (total, value) {
|
||||
return (100 * value / total).toFixed(2)+'%';
|
||||
};
|
||||
|
||||
Flotr.addType('pie', {
|
||||
options: {
|
||||
show: false, // => setting to true will show bars, false will hide
|
||||
lineWidth: 1, // => in pixels
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillColor: null, // => fill color
|
||||
fillOpacity: 0.6, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
explode: 6, // => the number of pixels the splices will be far from the center
|
||||
sizeRatio: 0.6, // => the size ratio of the pie relative to the plot
|
||||
startAngle: Math.PI/4, // => the first slice start angle
|
||||
labelFormatter: Flotr.defaultPieLabelFormatter,
|
||||
pie3D: false, // => whether to draw the pie in 3 dimenstions or not (ineffective)
|
||||
pie3DviewAngle: (Math.PI/2 * 0.8),
|
||||
pie3DspliceThickness: 20
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
|
||||
// TODO 3D charts what?
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
canvas = context.canvas,
|
||||
lineWidth = options.lineWidth,
|
||||
shadowSize = options.shadowSize,
|
||||
sizeRatio = options.sizeRatio,
|
||||
height = options.height,
|
||||
width = options.width,
|
||||
explode = options.explode,
|
||||
color = options.color,
|
||||
fill = options.fill,
|
||||
fillStyle = options.fillStyle,
|
||||
radius = Math.min(canvas.width, canvas.height) * sizeRatio / 2,
|
||||
value = data[0][1],
|
||||
html = [],
|
||||
vScale = 1,//Math.cos(series.pie.viewAngle);
|
||||
measure = Math.PI * 2 * value / this.total,
|
||||
startAngle = this.startAngle || (2 * Math.PI * options.startAngle), // TODO: this initial startAngle is already in radians (fixing will be test-unstable)
|
||||
endAngle = startAngle + measure,
|
||||
bisection = startAngle + measure / 2,
|
||||
label = options.labelFormatter(this.total, value),
|
||||
//plotTickness = Math.sin(series.pie.viewAngle)*series.pie.spliceThickness / vScale;
|
||||
explodeCoeff = explode + radius + 4,
|
||||
distX = Math.cos(bisection) * explodeCoeff,
|
||||
distY = Math.sin(bisection) * explodeCoeff,
|
||||
textAlign = distX < 0 ? 'right' : 'left',
|
||||
textBaseline = distY > 0 ? 'top' : 'bottom',
|
||||
style,
|
||||
x, y,
|
||||
distX, distY;
|
||||
|
||||
context.save();
|
||||
context.translate(width / 2, height / 2);
|
||||
context.scale(1, vScale);
|
||||
|
||||
x = Math.cos(bisection) * explode;
|
||||
y = Math.sin(bisection) * explode;
|
||||
|
||||
// Shadows
|
||||
if (shadowSize > 0) {
|
||||
this.plotSlice(x + shadowSize, y + shadowSize, radius, startAngle, endAngle, context);
|
||||
if (fill) {
|
||||
context.fillStyle = 'rgba(0,0,0,0.1)';
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
|
||||
this.plotSlice(x, y, radius, startAngle, endAngle, context);
|
||||
if (fill) {
|
||||
context.fillStyle = fillStyle;
|
||||
context.fill();
|
||||
}
|
||||
context.lineWidth = lineWidth;
|
||||
context.strokeStyle = color;
|
||||
context.stroke();
|
||||
|
||||
style = {
|
||||
size : options.fontSize * 1.2,
|
||||
color : options.fontColor,
|
||||
weight : 1.5
|
||||
};
|
||||
|
||||
if (label) {
|
||||
if (options.htmlText || !options.textEnabled) {
|
||||
divStyle = 'position:absolute;' + textBaseline + ':' + (height / 2 + (textBaseline === 'top' ? distY : -distY)) + 'px;';
|
||||
divStyle += textAlign + ':' + (width / 2 + (textAlign === 'right' ? -distX : distX)) + 'px;';
|
||||
html.push('<div style="', divStyle, '" class="flotr-grid-label">', label, '</div>');
|
||||
}
|
||||
else {
|
||||
style.textAlign = textAlign;
|
||||
style.textBaseline = textBaseline;
|
||||
Flotr.drawText(context, label, distX, distY, style);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.htmlText || !options.textEnabled) {
|
||||
var div = Flotr.DOM.node('<div style="color:' + options.fontColor + '" class="flotr-labels"></div>');
|
||||
Flotr.DOM.insert(div, html.join(''));
|
||||
Flotr.DOM.insert(options.element, div);
|
||||
}
|
||||
|
||||
context.restore();
|
||||
|
||||
// New start angle
|
||||
this.startAngle = endAngle;
|
||||
this.slices = this.slices || [];
|
||||
this.slices.push({
|
||||
radius : Math.min(canvas.width, canvas.height) * sizeRatio / 2,
|
||||
x : x,
|
||||
y : y,
|
||||
explode : explode,
|
||||
start : startAngle,
|
||||
end : endAngle
|
||||
});
|
||||
},
|
||||
plotSlice : function (x, y, radius, startAngle, endAngle, context) {
|
||||
context.beginPath();
|
||||
context.moveTo(x, y);
|
||||
context.arc(x, y, radius, startAngle, endAngle, false);
|
||||
context.lineTo(x, y);
|
||||
context.closePath();
|
||||
},
|
||||
hit : function (options) {
|
||||
|
||||
var
|
||||
data = options.data[0],
|
||||
args = options.args,
|
||||
index = options.index,
|
||||
mouse = args[0],
|
||||
n = args[1],
|
||||
slice = this.slices[index],
|
||||
x = mouse.relX - options.width / 2,
|
||||
y = mouse.relY - options.height / 2,
|
||||
r = Math.sqrt(x * x + y * y),
|
||||
theta = Math.atan(y / x),
|
||||
circle = Math.PI * 2,
|
||||
explode = slice.explode || options.explode,
|
||||
start = slice.start % circle,
|
||||
end = slice.end % circle;
|
||||
|
||||
if (x < 0) {
|
||||
theta += Math.PI;
|
||||
} else if (x > 0 && y < 0) {
|
||||
theta += circle;
|
||||
}
|
||||
|
||||
if (r < slice.radius + explode && r > explode) {
|
||||
if ((start > end && (theta < end || theta > start)) ||
|
||||
(theta > start && theta < end)) {
|
||||
|
||||
// TODO Decouple this from hit plugin (chart shouldn't know what n means)
|
||||
n.x = data[0];
|
||||
n.y = data[1];
|
||||
n.sAngle = start;
|
||||
n.eAngle = end;
|
||||
n.index = 0;
|
||||
n.seriesIndex = index;
|
||||
n.fraction = data[1] / this.total;
|
||||
}
|
||||
}
|
||||
},
|
||||
drawHit: function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
slice = this.slices[options.args.seriesIndex];
|
||||
|
||||
context.save();
|
||||
context.translate(options.width / 2, options.height / 2);
|
||||
this.plotSlice(slice.x, slice.y, slice.radius, slice.start, slice.end, context);
|
||||
context.stroke();
|
||||
context.restore();
|
||||
},
|
||||
clearHit : function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
slice = this.slices[options.args.seriesIndex],
|
||||
padding = 2 * options.lineWidth,
|
||||
radius = slice.radius + padding;
|
||||
|
||||
context.save();
|
||||
context.translate(options.width / 2, options.height / 2);
|
||||
context.clearRect(
|
||||
slice.x - radius,
|
||||
slice.y - radius,
|
||||
2 * radius + padding,
|
||||
2 * radius + padding
|
||||
);
|
||||
context.restore();
|
||||
},
|
||||
extendYRange : function (axis, data) {
|
||||
this.total = (this.total || 0) + data[0][1];
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,66 @@
|
|||
/** Points **/
|
||||
Flotr.addType('points', {
|
||||
options: {
|
||||
show: false, // => setting to true will show points, false will hide
|
||||
radius: 3, // => point radius (pixels)
|
||||
lineWidth: 2, // => line width in pixels
|
||||
fill: true, // => true to fill the points with a color, false for (transparent) no fill
|
||||
fillColor: '#FFFFFF', // => fill color
|
||||
fillOpacity: 0.4 // => opacity of color inside the points
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
lineWidth = options.lineWidth,
|
||||
shadowSize = options.shadowSize;
|
||||
|
||||
context.save();
|
||||
|
||||
if (shadowSize > 0) {
|
||||
context.lineWidth = shadowSize / 2;
|
||||
|
||||
context.strokeStyle = 'rgba(0,0,0,0.1)';
|
||||
this.plot(options, shadowSize / 2 + context.lineWidth / 2);
|
||||
|
||||
context.strokeStyle = 'rgba(0,0,0,0.2)';
|
||||
this.plot(options, context.lineWidth / 2);
|
||||
}
|
||||
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.strokeStyle = options.color;
|
||||
context.fillStyle = options.fillColor || options.color;
|
||||
|
||||
this.plot(options);
|
||||
context.restore();
|
||||
},
|
||||
|
||||
plot : function (options, offset) {
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
xScale = options.xScale,
|
||||
yScale = options.yScale,
|
||||
i, x, y;
|
||||
|
||||
for (i = data.length - 1; i > -1; --i) {
|
||||
y = data[i][1];
|
||||
if (y === null) continue;
|
||||
|
||||
x = xScale(data[i][0]);
|
||||
y = yScale(y);
|
||||
|
||||
if (x < 0 || x > options.width || y < 0 || y > options.height) continue;
|
||||
|
||||
context.beginPath();
|
||||
if (offset) {
|
||||
context.arc(x, y + offset, options.radius, 0, Math.PI, false);
|
||||
} else {
|
||||
context.arc(x, y, options.radius, 0, 2 * Math.PI, true);
|
||||
if (options.fill) context.fill();
|
||||
}
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
/** Radar **/
|
||||
Flotr.addType('radar', {
|
||||
options: {
|
||||
show: false, // => setting to true will show radar chart, false will hide
|
||||
lineWidth: 2, // => line width in pixels
|
||||
fill: true, // => true to fill the area from the line to the x axis, false for (transparent) no fill
|
||||
fillOpacity: 0.4, // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill
|
||||
radiusRatio: 0.90 // => ratio of the radar, against the plot size
|
||||
},
|
||||
draw : function (options) {
|
||||
var
|
||||
context = options.context,
|
||||
shadowSize = options.shadowSize;
|
||||
|
||||
context.save();
|
||||
context.translate(options.width / 2, options.height / 2);
|
||||
context.lineWidth = options.lineWidth;
|
||||
|
||||
// Shadow
|
||||
context.fillStyle = 'rgba(0,0,0,0.05)';
|
||||
context.strokeStyle = 'rgba(0,0,0,0.05)';
|
||||
this.plot(options, shadowSize / 2);
|
||||
context.strokeStyle = 'rgba(0,0,0,0.1)';
|
||||
this.plot(options, shadowSize / 4);
|
||||
|
||||
// Chart
|
||||
context.strokeStyle = options.color;
|
||||
context.fillStyle = options.fillStyle;
|
||||
this.plot(options);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
plot : function (options, offset) {
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
radius = Math.min(options.height, options.width) * options.radiusRatio / 2,
|
||||
step = 2 * Math.PI / data.length,
|
||||
angle = -Math.PI / 2,
|
||||
i, ratio;
|
||||
|
||||
offset = offset || 0;
|
||||
|
||||
context.beginPath();
|
||||
for (i = 0; i < data.length; ++i) {
|
||||
ratio = data[i][1] / this.max;
|
||||
|
||||
context[i === 0 ? 'moveTo' : 'lineTo'](
|
||||
Math.cos(i * step + angle) * radius * ratio + offset,
|
||||
Math.sin(i * step + angle) * radius * ratio + offset
|
||||
);
|
||||
}
|
||||
context.closePath();
|
||||
if (options.fill) context.fill();
|
||||
context.stroke();
|
||||
},
|
||||
extendYRange : function (axis, data) {
|
||||
this.max = Math.max(axis.max, this.max || -Number.MAX_VALUE);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,90 @@
|
|||
Flotr.addType('timeline', {
|
||||
options: {
|
||||
show: false,
|
||||
lineWidth: 1,
|
||||
barWidth: 0.2,
|
||||
fill: true,
|
||||
fillColor: null,
|
||||
fillOpacity: 0.4,
|
||||
centered: true
|
||||
},
|
||||
|
||||
draw : function (options) {
|
||||
|
||||
var
|
||||
context = options.context;
|
||||
|
||||
context.save();
|
||||
context.lineJoin = 'miter';
|
||||
context.lineWidth = options.lineWidth;
|
||||
context.strokeStyle = options.color;
|
||||
context.fillStyle = options.fillStyle;
|
||||
|
||||
this.plot(options);
|
||||
|
||||
context.restore();
|
||||
},
|
||||
|
||||
plot : function (options) {
|
||||
|
||||
var
|
||||
data = options.data,
|
||||
context = options.context,
|
||||
xScale = options.xScale,
|
||||
yScale = options.yScale,
|
||||
barWidth = options.barWidth,
|
||||
lineWidth = options.lineWidth,
|
||||
i;
|
||||
|
||||
Flotr._.each(data, function (timeline) {
|
||||
|
||||
var
|
||||
x = timeline[0],
|
||||
y = timeline[1],
|
||||
w = timeline[2],
|
||||
h = barWidth,
|
||||
|
||||
xt = Math.ceil(xScale(x)),
|
||||
wt = Math.ceil(xScale(x + w)) - xt,
|
||||
yt = Math.round(yScale(y)),
|
||||
ht = Math.round(yScale(y - h)) - yt,
|
||||
|
||||
x0 = xt - lineWidth / 2,
|
||||
y0 = Math.round(yt - ht / 2) - lineWidth / 2;
|
||||
|
||||
context.strokeRect(x0, y0, wt, ht);
|
||||
context.fillRect(x0, y0, wt, ht);
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
extendRange : function (series) {
|
||||
|
||||
var
|
||||
data = series.data,
|
||||
xa = series.xaxis,
|
||||
ya = series.yaxis,
|
||||
w = series.timeline.barWidth;
|
||||
|
||||
if (xa.options.min === null)
|
||||
xa.min = xa.datamin - w / 2;
|
||||
|
||||
if (xa.options.max === null) {
|
||||
|
||||
var
|
||||
max = xa.max;
|
||||
|
||||
Flotr._.each(data, function (timeline) {
|
||||
max = Math.max(max, timeline[0] + timeline[2]);
|
||||
}, this);
|
||||
|
||||
xa.max = max + w / 2;
|
||||
}
|
||||
|
||||
if (ya.options.min === null)
|
||||
ya.min = ya.datamin - w;
|
||||
if (ya.options.min === null)
|
||||
ya.max = ya.datamax + w;
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,113 @@
|
|||
/* Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
|
||||
* Version: 1.0
|
||||
* LastModified: Dec 25 1999
|
||||
* This library is free. You can redistribute it and/or modify it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Interfaces:
|
||||
* b64 = base64encode(data);
|
||||
* data = base64decode(b64);
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var base64DecodeChars = [
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1];
|
||||
|
||||
function base64encode(str) {
|
||||
var out, i, len;
|
||||
var c1, c2, c3;
|
||||
|
||||
len = str.length;
|
||||
i = 0;
|
||||
out = "";
|
||||
while(i < len) {
|
||||
c1 = str.charCodeAt(i++) & 0xff;
|
||||
if(i == len)
|
||||
{
|
||||
out += base64EncodeChars.charAt(c1 >> 2);
|
||||
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
|
||||
out += "==";
|
||||
break;
|
||||
}
|
||||
c2 = str.charCodeAt(i++);
|
||||
if(i == len)
|
||||
{
|
||||
out += base64EncodeChars.charAt(c1 >> 2);
|
||||
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
|
||||
out += base64EncodeChars.charAt((c2 & 0xF) << 2);
|
||||
out += "=";
|
||||
break;
|
||||
}
|
||||
c3 = str.charCodeAt(i++);
|
||||
out += base64EncodeChars.charAt(c1 >> 2);
|
||||
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
|
||||
out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
|
||||
out += base64EncodeChars.charAt(c3 & 0x3F);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function base64decode(str) {
|
||||
var c1, c2, c3, c4;
|
||||
var i, len, out;
|
||||
|
||||
len = str.length;
|
||||
i = 0;
|
||||
out = "";
|
||||
while(i < len) {
|
||||
/* c1 */
|
||||
do {
|
||||
c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
||||
} while(i < len && c1 == -1);
|
||||
if(c1 == -1)
|
||||
break;
|
||||
|
||||
/* c2 */
|
||||
do {
|
||||
c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
|
||||
} while(i < len && c2 == -1);
|
||||
if(c2 == -1)
|
||||
break;
|
||||
|
||||
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
|
||||
|
||||
/* c3 */
|
||||
do {
|
||||
c3 = str.charCodeAt(i++) & 0xff;
|
||||
if(c3 == 61)
|
||||
return out;
|
||||
c3 = base64DecodeChars[c3];
|
||||
} while(i < len && c3 == -1);
|
||||
if(c3 == -1)
|
||||
break;
|
||||
|
||||
out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
|
||||
|
||||
/* c4 */
|
||||
do {
|
||||
c4 = str.charCodeAt(i++) & 0xff;
|
||||
if(c4 == 61)
|
||||
return out;
|
||||
c4 = base64DecodeChars[c4];
|
||||
} while(i < len && c4 == -1);
|
||||
if(c4 == -1)
|
||||
break;
|
||||
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
if (!window.btoa) window.btoa = base64encode;
|
||||
if (!window.atob) window.atob = base64decode;
|
||||
|
||||
})();
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,503 @@
|
|||
/*!
|
||||
* bean.js - copyright Jacob Thornton 2011
|
||||
* https://github.com/fat/bean
|
||||
* MIT License
|
||||
* special thanks to:
|
||||
* dean edwards: http://dean.edwards.name/
|
||||
* dperini: https://github.com/dperini/nwevents
|
||||
* the entire mootools team: github.com/mootools/mootools-core
|
||||
*/
|
||||
/*global module:true, define:true*/
|
||||
!function (name, context, definition) {
|
||||
if (typeof module !== 'undefined') module.exports = definition(name, context);
|
||||
else if (typeof define === 'function' && typeof define.amd === 'object') define(definition);
|
||||
else context[name] = definition(name, context);
|
||||
}('bean', this, function (name, context) {
|
||||
var win = window
|
||||
, old = context[name]
|
||||
, overOut = /over|out/
|
||||
, namespaceRegex = /[^\.]*(?=\..*)\.|.*/
|
||||
, nameRegex = /\..*/
|
||||
, addEvent = 'addEventListener'
|
||||
, attachEvent = 'attachEvent'
|
||||
, removeEvent = 'removeEventListener'
|
||||
, detachEvent = 'detachEvent'
|
||||
, doc = document || {}
|
||||
, root = doc.documentElement || {}
|
||||
, W3C_MODEL = root[addEvent]
|
||||
, eventSupport = W3C_MODEL ? addEvent : attachEvent
|
||||
, slice = Array.prototype.slice
|
||||
, mouseTypeRegex = /click|mouse|menu|drag|drop/i
|
||||
, touchTypeRegex = /^touch|^gesture/i
|
||||
, ONE = { one: 1 } // singleton for quick matching making add() do one()
|
||||
|
||||
, nativeEvents = (function (hash, events, i) {
|
||||
for (i = 0; i < events.length; i++)
|
||||
hash[events[i]] = 1
|
||||
return hash
|
||||
})({}, (
|
||||
'click dblclick mouseup mousedown contextmenu ' + // mouse buttons
|
||||
'mousewheel DOMMouseScroll ' + // mouse wheel
|
||||
'mouseover mouseout mousemove selectstart selectend ' + // mouse movement
|
||||
'keydown keypress keyup ' + // keyboard
|
||||
'orientationchange ' + // mobile
|
||||
'focus blur change reset select submit ' + // form elements
|
||||
'load unload beforeunload resize move DOMContentLoaded readystatechange ' + // window
|
||||
'error abort scroll ' + // misc
|
||||
(W3C_MODEL ? // element.fireEvent('onXYZ'... is not forgiving if we try to fire an event
|
||||
// that doesn't actually exist, so make sure we only do these on newer browsers
|
||||
'show ' + // mouse buttons
|
||||
'input invalid ' + // form elements
|
||||
'touchstart touchmove touchend touchcancel ' + // touch
|
||||
'gesturestart gesturechange gestureend ' + // gesture
|
||||
'message readystatechange pageshow pagehide popstate ' + // window
|
||||
'hashchange offline online ' + // window
|
||||
'afterprint beforeprint ' + // printing
|
||||
'dragstart dragenter dragover dragleave drag drop dragend ' + // dnd
|
||||
'loadstart progress suspend emptied stalled loadmetadata ' + // media
|
||||
'loadeddata canplay canplaythrough playing waiting seeking ' + // media
|
||||
'seeked ended durationchange timeupdate play pause ratechange ' + // media
|
||||
'volumechange cuechange ' + // media
|
||||
'checking noupdate downloading cached updateready obsolete ' + // appcache
|
||||
'' : '')
|
||||
).split(' ')
|
||||
)
|
||||
|
||||
, customEvents = (function () {
|
||||
function isDescendant(parent, node) {
|
||||
while ((node = node.parentNode) !== null) {
|
||||
if (node === parent) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function check(event) {
|
||||
var related = event.relatedTarget
|
||||
if (!related) return related === null
|
||||
return (related !== this && related.prefix !== 'xul' && !/document/.test(this.toString()) && !isDescendant(this, related))
|
||||
}
|
||||
|
||||
return {
|
||||
mouseenter: { base: 'mouseover', condition: check }
|
||||
, mouseleave: { base: 'mouseout', condition: check }
|
||||
, mousewheel: { base: /Firefox/.test(navigator.userAgent) ? 'DOMMouseScroll' : 'mousewheel' }
|
||||
}
|
||||
})()
|
||||
|
||||
, fixEvent = (function () {
|
||||
var commonProps = 'altKey attrChange attrName bubbles cancelable ctrlKey currentTarget detail eventPhase getModifierState isTrusted metaKey relatedNode relatedTarget shiftKey srcElement target timeStamp type view which'.split(' ')
|
||||
, mouseProps = commonProps.concat('button buttons clientX clientY dataTransfer fromElement offsetX offsetY pageX pageY screenX screenY toElement'.split(' '))
|
||||
, keyProps = commonProps.concat('char charCode key keyCode'.split(' '))
|
||||
, touchProps = commonProps.concat('touches targetTouches changedTouches scale rotation'.split(' '))
|
||||
, preventDefault = 'preventDefault'
|
||||
, createPreventDefault = function (event) {
|
||||
return function () {
|
||||
if (event[preventDefault])
|
||||
event[preventDefault]()
|
||||
else
|
||||
event.returnValue = false
|
||||
}
|
||||
}
|
||||
, stopPropagation = 'stopPropagation'
|
||||
, createStopPropagation = function (event) {
|
||||
return function () {
|
||||
if (event[stopPropagation])
|
||||
event[stopPropagation]()
|
||||
else
|
||||
event.cancelBubble = true
|
||||
}
|
||||
}
|
||||
, createStop = function (synEvent) {
|
||||
return function () {
|
||||
synEvent[preventDefault]()
|
||||
synEvent[stopPropagation]()
|
||||
synEvent.stopped = true
|
||||
}
|
||||
}
|
||||
, copyProps = function (event, result, props) {
|
||||
var i, p
|
||||
for (i = props.length; i--;) {
|
||||
p = props[i]
|
||||
if (!(p in result) && p in event) result[p] = event[p]
|
||||
}
|
||||
}
|
||||
|
||||
return function (event, isNative) {
|
||||
var result = { originalEvent: event, isNative: isNative }
|
||||
if (!event)
|
||||
return result
|
||||
|
||||
var props
|
||||
, type = event.type
|
||||
, target = event.target || event.srcElement
|
||||
|
||||
result[preventDefault] = createPreventDefault(event)
|
||||
result[stopPropagation] = createStopPropagation(event)
|
||||
result.stop = createStop(result)
|
||||
result.target = target && target.nodeType === 3 ? target.parentNode : target
|
||||
|
||||
if (isNative) { // we only need basic augmentation on custom events, the rest is too expensive
|
||||
if (type.indexOf('key') !== -1) {
|
||||
props = keyProps
|
||||
result.keyCode = event.which || event.keyCode
|
||||
} else if (mouseTypeRegex.test(type)) {
|
||||
props = mouseProps
|
||||
result.rightClick = event.which === 3 || event.button === 2
|
||||
result.pos = { x: 0, y: 0 }
|
||||
if (event.pageX || event.pageY) {
|
||||
result.clientX = event.pageX
|
||||
result.clientY = event.pageY
|
||||
} else if (event.clientX || event.clientY) {
|
||||
result.clientX = event.clientX + doc.body.scrollLeft + root.scrollLeft
|
||||
result.clientY = event.clientY + doc.body.scrollTop + root.scrollTop
|
||||
}
|
||||
if (overOut.test(type))
|
||||
result.relatedTarget = event.relatedTarget || event[(type === 'mouseover' ? 'from' : 'to') + 'Element']
|
||||
} else if (touchTypeRegex.test(type)) {
|
||||
props = touchProps
|
||||
}
|
||||
copyProps(event, result, props || commonProps)
|
||||
}
|
||||
return result
|
||||
}
|
||||
})()
|
||||
|
||||
// if we're in old IE we can't do onpropertychange on doc or win so we use doc.documentElement for both
|
||||
, targetElement = function (element, isNative) {
|
||||
return !W3C_MODEL && !isNative && (element === doc || element === win) ? root : element
|
||||
}
|
||||
|
||||
// we use one of these per listener, of any type
|
||||
, RegEntry = (function () {
|
||||
function entry(element, type, handler, original, namespaces) {
|
||||
this.element = element
|
||||
this.type = type
|
||||
this.handler = handler
|
||||
this.original = original
|
||||
this.namespaces = namespaces
|
||||
this.custom = customEvents[type]
|
||||
this.isNative = nativeEvents[type] && element[eventSupport]
|
||||
this.eventType = W3C_MODEL || this.isNative ? type : 'propertychange'
|
||||
this.customType = !W3C_MODEL && !this.isNative && type
|
||||
this.target = targetElement(element, this.isNative)
|
||||
this.eventSupport = this.target[eventSupport]
|
||||
}
|
||||
|
||||
entry.prototype = {
|
||||
// given a list of namespaces, is our entry in any of them?
|
||||
inNamespaces: function (checkNamespaces) {
|
||||
var i, j
|
||||
if (!checkNamespaces)
|
||||
return true
|
||||
if (!this.namespaces)
|
||||
return false
|
||||
for (i = checkNamespaces.length; i--;) {
|
||||
for (j = this.namespaces.length; j--;) {
|
||||
if (checkNamespaces[i] === this.namespaces[j])
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// match by element, original fn (opt), handler fn (opt)
|
||||
, matches: function (checkElement, checkOriginal, checkHandler) {
|
||||
return this.element === checkElement &&
|
||||
(!checkOriginal || this.original === checkOriginal) &&
|
||||
(!checkHandler || this.handler === checkHandler)
|
||||
}
|
||||
}
|
||||
|
||||
return entry
|
||||
})()
|
||||
|
||||
, registry = (function () {
|
||||
// our map stores arrays by event type, just because it's better than storing
|
||||
// everything in a single array. uses '$' as a prefix for the keys for safety
|
||||
var map = {}
|
||||
|
||||
// generic functional search of our registry for matching listeners,
|
||||
// `fn` returns false to break out of the loop
|
||||
, forAll = function (element, type, original, handler, fn) {
|
||||
if (!type || type === '*') {
|
||||
// search the whole registry
|
||||
for (var t in map) {
|
||||
if (t.charAt(0) === '$')
|
||||
forAll(element, t.substr(1), original, handler, fn)
|
||||
}
|
||||
} else {
|
||||
var i = 0, l, list = map['$' + type], all = element === '*'
|
||||
if (!list)
|
||||
return
|
||||
for (l = list.length; i < l; i++) {
|
||||
if (all || list[i].matches(element, original, handler))
|
||||
if (!fn(list[i], list, i, type))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, has = function (element, type, original) {
|
||||
// we're not using forAll here simply because it's a bit slower and this
|
||||
// needs to be fast
|
||||
var i, list = map['$' + type]
|
||||
if (list) {
|
||||
for (i = list.length; i--;) {
|
||||
if (list[i].matches(element, original, null))
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
, get = function (element, type, original) {
|
||||
var entries = []
|
||||
forAll(element, type, original, null, function (entry) { return entries.push(entry) })
|
||||
return entries
|
||||
}
|
||||
|
||||
, put = function (entry) {
|
||||
(map['$' + entry.type] || (map['$' + entry.type] = [])).push(entry)
|
||||
return entry
|
||||
}
|
||||
|
||||
, del = function (entry) {
|
||||
forAll(entry.element, entry.type, null, entry.handler, function (entry, list, i) {
|
||||
list.splice(i, 1)
|
||||
if (list.length === 0)
|
||||
delete map['$' + entry.type]
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
// dump all entries, used for onunload
|
||||
, entries = function () {
|
||||
var t, entries = []
|
||||
for (t in map) {
|
||||
if (t.charAt(0) === '$')
|
||||
entries = entries.concat(map[t])
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
return { has: has, get: get, put: put, del: del, entries: entries }
|
||||
})()
|
||||
|
||||
// add and remove listeners to DOM elements
|
||||
, listener = W3C_MODEL ? function (element, type, fn, add) {
|
||||
element[add ? addEvent : removeEvent](type, fn, false)
|
||||
} : function (element, type, fn, add, custom) {
|
||||
if (custom && add && element['_on' + custom] === null)
|
||||
element['_on' + custom] = 0
|
||||
element[add ? attachEvent : detachEvent]('on' + type, fn)
|
||||
}
|
||||
|
||||
, nativeHandler = function (element, fn, args) {
|
||||
return function (event) {
|
||||
event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, true)
|
||||
return fn.apply(element, [event].concat(args))
|
||||
}
|
||||
}
|
||||
|
||||
, customHandler = function (element, fn, type, condition, args, isNative) {
|
||||
return function (event) {
|
||||
if (condition ? condition.apply(this, arguments) : W3C_MODEL ? true : event && event.propertyName === '_on' + type || !event) {
|
||||
if (event)
|
||||
event = fixEvent(event || ((this.ownerDocument || this.document || this).parentWindow || win).event, isNative)
|
||||
fn.apply(element, event && (!args || args.length === 0) ? arguments : slice.call(arguments, event ? 0 : 1).concat(args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, once = function (rm, element, type, fn, originalFn) {
|
||||
// wrap the handler in a handler that does a remove as well
|
||||
return function () {
|
||||
rm(element, type, originalFn)
|
||||
fn.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
, removeListener = function (element, orgType, handler, namespaces) {
|
||||
var i, l, entry
|
||||
, type = (orgType && orgType.replace(nameRegex, ''))
|
||||
, handlers = registry.get(element, type, handler)
|
||||
|
||||
for (i = 0, l = handlers.length; i < l; i++) {
|
||||
if (handlers[i].inNamespaces(namespaces)) {
|
||||
if ((entry = handlers[i]).eventSupport)
|
||||
listener(entry.target, entry.eventType, entry.handler, false, entry.type)
|
||||
// TODO: this is problematic, we have a registry.get() and registry.del() that
|
||||
// both do registry searches so we waste cycles doing this. Needs to be rolled into
|
||||
// a single registry.forAll(fn) that removes while finding, but the catch is that
|
||||
// we'll be splicing the arrays that we're iterating over. Needs extra tests to
|
||||
// make sure we don't screw it up. @rvagg
|
||||
registry.del(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, addListener = function (element, orgType, fn, originalFn, args) {
|
||||
var entry
|
||||
, type = orgType.replace(nameRegex, '')
|
||||
, namespaces = orgType.replace(namespaceRegex, '').split('.')
|
||||
|
||||
if (registry.has(element, type, fn))
|
||||
return element // no dupe
|
||||
if (type === 'unload')
|
||||
fn = once(removeListener, element, type, fn, originalFn) // self clean-up
|
||||
if (customEvents[type]) {
|
||||
if (customEvents[type].condition)
|
||||
fn = customHandler(element, fn, type, customEvents[type].condition, true)
|
||||
type = customEvents[type].base || type
|
||||
}
|
||||
entry = registry.put(new RegEntry(element, type, fn, originalFn, namespaces[0] && namespaces))
|
||||
entry.handler = entry.isNative ?
|
||||
nativeHandler(element, entry.handler, args) :
|
||||
customHandler(element, entry.handler, type, false, args, false)
|
||||
if (entry.eventSupport)
|
||||
listener(entry.target, entry.eventType, entry.handler, true, entry.customType)
|
||||
}
|
||||
|
||||
, del = function (selector, fn, $) {
|
||||
return function (e) {
|
||||
var target, i, array = typeof selector === 'string' ? $(selector, this) : selector
|
||||
for (target = e.target; target && target !== this; target = target.parentNode) {
|
||||
for (i = array.length; i--;) {
|
||||
if (array[i] === target) {
|
||||
return fn.apply(target, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
, remove = function (element, typeSpec, fn) {
|
||||
var k, m, type, namespaces, i
|
||||
, rm = removeListener
|
||||
, isString = typeSpec && typeof typeSpec === 'string'
|
||||
|
||||
if (isString && typeSpec.indexOf(' ') > 0) {
|
||||
// remove(el, 't1 t2 t3', fn) or remove(el, 't1 t2 t3')
|
||||
typeSpec = typeSpec.split(' ')
|
||||
for (i = typeSpec.length; i--;)
|
||||
remove(element, typeSpec[i], fn)
|
||||
return element
|
||||
}
|
||||
type = isString && typeSpec.replace(nameRegex, '')
|
||||
if (type && customEvents[type])
|
||||
type = customEvents[type].type
|
||||
if (!typeSpec || isString) {
|
||||
// remove(el) or remove(el, t1.ns) or remove(el, .ns) or remove(el, .ns1.ns2.ns3)
|
||||
if (namespaces = isString && typeSpec.replace(namespaceRegex, ''))
|
||||
namespaces = namespaces.split('.')
|
||||
rm(element, type, fn, namespaces)
|
||||
} else if (typeof typeSpec === 'function') {
|
||||
// remove(el, fn)
|
||||
rm(element, null, typeSpec)
|
||||
} else {
|
||||
// remove(el, { t1: fn1, t2, fn2 })
|
||||
for (k in typeSpec) {
|
||||
if (typeSpec.hasOwnProperty(k))
|
||||
remove(element, k, typeSpec[k])
|
||||
}
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
, add = function (element, events, fn, delfn, $) {
|
||||
var type, types, i, args
|
||||
, originalFn = fn
|
||||
, isDel = fn && typeof fn === 'string'
|
||||
|
||||
if (events && !fn && typeof events === 'object') {
|
||||
for (type in events) {
|
||||
if (events.hasOwnProperty(type))
|
||||
add.apply(this, [ element, type, events[type] ])
|
||||
}
|
||||
} else {
|
||||
args = arguments.length > 3 ? slice.call(arguments, 3) : []
|
||||
types = (isDel ? fn : events).split(' ')
|
||||
isDel && (fn = del(events, (originalFn = delfn), $)) && (args = slice.call(args, 1))
|
||||
// special case for one()
|
||||
this === ONE && (fn = once(remove, element, events, fn, originalFn))
|
||||
for (i = types.length; i--;) addListener(element, types[i], fn, originalFn, args)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
, one = function () {
|
||||
return add.apply(ONE, arguments)
|
||||
}
|
||||
|
||||
, fireListener = W3C_MODEL ? function (isNative, type, element) {
|
||||
var evt = doc.createEvent(isNative ? 'HTMLEvents' : 'UIEvents')
|
||||
evt[isNative ? 'initEvent' : 'initUIEvent'](type, true, true, win, 1)
|
||||
element.dispatchEvent(evt)
|
||||
} : function (isNative, type, element) {
|
||||
element = targetElement(element, isNative)
|
||||
// if not-native then we're using onpropertychange so we just increment a custom property
|
||||
isNative ? element.fireEvent('on' + type, doc.createEventObject()) : element['_on' + type]++
|
||||
}
|
||||
|
||||
, fire = function (element, type, args) {
|
||||
var i, j, l, names, handlers
|
||||
, types = type.split(' ')
|
||||
|
||||
for (i = types.length; i--;) {
|
||||
type = types[i].replace(nameRegex, '')
|
||||
if (names = types[i].replace(namespaceRegex, ''))
|
||||
names = names.split('.')
|
||||
if (!names && !args && element[eventSupport]) {
|
||||
fireListener(nativeEvents[type], type, element)
|
||||
} else {
|
||||
// non-native event, either because of a namespace, arguments or a non DOM element
|
||||
// iterate over all listeners and manually 'fire'
|
||||
handlers = registry.get(element, type)
|
||||
args = [false].concat(args)
|
||||
for (j = 0, l = handlers.length; j < l; j++) {
|
||||
if (handlers[j].inNamespaces(names))
|
||||
handlers[j].handler.apply(element, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
, clone = function (element, from, type) {
|
||||
var i = 0
|
||||
, handlers = registry.get(from, type)
|
||||
, l = handlers.length
|
||||
|
||||
for (;i < l; i++)
|
||||
handlers[i].original && add(element, handlers[i].type, handlers[i].original)
|
||||
return element
|
||||
}
|
||||
|
||||
, bean = {
|
||||
add: add
|
||||
, one: one
|
||||
, remove: remove
|
||||
, clone: clone
|
||||
, fire: fire
|
||||
, noConflict: function () {
|
||||
context[name] = old
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
if (win[attachEvent]) {
|
||||
// for IE, clean up on unload to avoid leaks
|
||||
var cleanup = function () {
|
||||
var i, entries = registry.entries()
|
||||
for (i in entries) {
|
||||
if (entries[i].type && entries[i].type !== 'unload')
|
||||
remove(entries[i].element, entries[i].type)
|
||||
}
|
||||
win[detachEvent]('onunload', cleanup)
|
||||
win.CollectGarbage && win.CollectGarbage()
|
||||
}
|
||||
win[attachEvent]('onunload', cleanup)
|
||||
}
|
||||
|
||||
return bean
|
||||
});
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Canvas2Image v0.1
|
||||
* Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com
|
||||
* MIT License [http://www.opensource.org/licenses/mit-license.php]
|
||||
*/
|
||||
|
||||
var Canvas2Image = (function() {
|
||||
// check if we have canvas support
|
||||
var oCanvas = document.createElement("canvas"),
|
||||
sc = String.fromCharCode,
|
||||
strDownloadMime = "image/octet-stream",
|
||||
bReplaceDownloadMime = false;
|
||||
|
||||
// no canvas, bail out.
|
||||
if (!oCanvas.getContext) {
|
||||
return {
|
||||
saveAsBMP : function(){},
|
||||
saveAsPNG : function(){},
|
||||
saveAsJPEG : function(){}
|
||||
}
|
||||
}
|
||||
|
||||
var bHasImageData = !!(oCanvas.getContext("2d").getImageData),
|
||||
bHasDataURL = !!(oCanvas.toDataURL),
|
||||
bHasBase64 = !!(window.btoa);
|
||||
|
||||
// ok, we're good
|
||||
var readCanvasData = function(oCanvas) {
|
||||
var iWidth = parseInt(oCanvas.width),
|
||||
iHeight = parseInt(oCanvas.height);
|
||||
return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight);
|
||||
}
|
||||
|
||||
// base64 encodes either a string or an array of charcodes
|
||||
var encodeData = function(data) {
|
||||
var i, aData, strData = "";
|
||||
|
||||
if (typeof data == "string") {
|
||||
strData = data;
|
||||
} else {
|
||||
aData = data;
|
||||
for (i = 0; i < aData.length; i++) {
|
||||
strData += sc(aData[i]);
|
||||
}
|
||||
}
|
||||
return btoa(strData);
|
||||
}
|
||||
|
||||
// creates a base64 encoded string containing BMP data takes an imagedata object as argument
|
||||
var createBMP = function(oData) {
|
||||
var strHeader = '',
|
||||
iWidth = oData.width,
|
||||
iHeight = oData.height;
|
||||
|
||||
strHeader += 'BM';
|
||||
|
||||
var iFileSize = iWidth*iHeight*4 + 54; // total header size = 54 bytes
|
||||
strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
strHeader += sc(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
|
||||
strHeader += sc(iFileSize % 256);
|
||||
|
||||
strHeader += sc(0, 0, 0, 0, 54, 0, 0, 0); // data offset
|
||||
strHeader += sc(40, 0, 0, 0); // info header size
|
||||
|
||||
var iImageWidth = iWidth;
|
||||
strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
strHeader += sc(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
|
||||
strHeader += sc(iImageWidth % 256);
|
||||
|
||||
var iImageHeight = iHeight;
|
||||
strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
strHeader += sc(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
|
||||
strHeader += sc(iImageHeight % 256);
|
||||
|
||||
strHeader += sc(1, 0, 32, 0); // num of planes & num of bits per pixel
|
||||
strHeader += sc(0, 0, 0, 0); // compression = none
|
||||
|
||||
var iDataSize = iWidth*iHeight*4;
|
||||
strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
strHeader += sc(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
|
||||
strHeader += sc(iDataSize % 256);
|
||||
|
||||
strHeader += sc(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // these bytes are not used
|
||||
|
||||
var aImgData = oData.data,
|
||||
strPixelData = "",
|
||||
c, x, y = iHeight,
|
||||
iOffsetX, iOffsetY, strPixelRow;
|
||||
|
||||
do {
|
||||
iOffsetY = iWidth*(y-1)*4;
|
||||
strPixelRow = "";
|
||||
for (x = 0; x < iWidth; x++) {
|
||||
iOffsetX = 4*x;
|
||||
strPixelRow += sc(
|
||||
aImgData[iOffsetY + iOffsetX + 2], // B
|
||||
aImgData[iOffsetY + iOffsetX + 1], // G
|
||||
aImgData[iOffsetY + iOffsetX], // R
|
||||
aImgData[iOffsetY + iOffsetX + 3] // A
|
||||
);
|
||||
}
|
||||
strPixelData += strPixelRow;
|
||||
} while (--y);
|
||||
|
||||
return encodeData(strHeader + strPixelData);
|
||||
}
|
||||
|
||||
// sends the generated file to the client
|
||||
var saveFile = function(strData) {
|
||||
if (!window.open(strData)) {
|
||||
document.location.href = strData;
|
||||
}
|
||||
}
|
||||
|
||||
var makeDataURI = function(strData, strMime) {
|
||||
return "data:" + strMime + ";base64," + strData;
|
||||
}
|
||||
|
||||
// generates a <img> object containing the imagedata
|
||||
var makeImageObject = function(strSource) {
|
||||
var oImgElement = document.createElement("img");
|
||||
oImgElement.src = strSource;
|
||||
return oImgElement;
|
||||
}
|
||||
|
||||
var scaleCanvas = function(oCanvas, iWidth, iHeight) {
|
||||
if (iWidth && iHeight) {
|
||||
var oSaveCanvas = document.createElement("canvas");
|
||||
|
||||
oSaveCanvas.width = iWidth;
|
||||
oSaveCanvas.height = iHeight;
|
||||
oSaveCanvas.style.width = iWidth+"px";
|
||||
oSaveCanvas.style.height = iHeight+"px";
|
||||
|
||||
var oSaveCtx = oSaveCanvas.getContext("2d");
|
||||
|
||||
oSaveCtx.drawImage(oCanvas, 0, 0, oCanvas.width, oCanvas.height, 0, 0, iWidth, iWidth);
|
||||
|
||||
return oSaveCanvas;
|
||||
}
|
||||
return oCanvas;
|
||||
}
|
||||
|
||||
return {
|
||||
saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!bHasDataURL) return false;
|
||||
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight),
|
||||
strMime = "image/png",
|
||||
strData = oScaledCanvas.toDataURL(strMime);
|
||||
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(strData);
|
||||
} else {
|
||||
saveFile(bReplaceDownloadMime ? strData.replace(strMime, strDownloadMime) : strData);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!bHasDataURL) return false;
|
||||
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight),
|
||||
strMime = "image/jpeg",
|
||||
strData = oScaledCanvas.toDataURL(strMime);
|
||||
|
||||
// check if browser actually supports jpeg by looking for the mime type in the data uri. if not, return false
|
||||
if (strData.indexOf(strMime) != 5) return false;
|
||||
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(strData);
|
||||
} else {
|
||||
saveFile(bReplaceDownloadMime ? strData.replace(strMime, strDownloadMime) : strData);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) {
|
||||
if (!(bHasDataURL && bHasImageData && bHasBase64)) return false;
|
||||
|
||||
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight),
|
||||
strMime = "image/bmp",
|
||||
oData = readCanvasData(oScaledCanvas),
|
||||
strImgData = createBMP(oData);
|
||||
|
||||
if (bReturnImg) {
|
||||
return makeImageObject(makeDataURI(strImgData, strMime));
|
||||
} else {
|
||||
saveFile(makeDataURI(strImgData, strMime));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
})();
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue